@sma1lboy/rove 0.9.274 → 0.9.277-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/cli/{chunk-kgs56yns.js → chunk-0a65j1zj.js} +1 -1
  2. package/dist/cli/{chunk-55ajzrj6.js → chunk-0r1jf8m9.js} +1 -1
  3. package/dist/cli/{chunk-q6septaj.js → chunk-0rvydb9q.js} +1 -1
  4. package/dist/cli/chunk-1g6vhgfe.js +14 -0
  5. package/dist/cli/{chunk-mmq6922s.js → chunk-1vxtd8rh.js} +2 -2
  6. package/dist/cli/chunk-1wwqf6kx.js +3 -0
  7. package/dist/cli/{chunk-8h437k1q.js → chunk-2kbzb24j.js} +1 -1
  8. package/dist/cli/chunk-2kt0mfvf.js +2 -0
  9. package/dist/cli/{chunk-m4dvx7bd.js → chunk-2vph030z.js} +3 -3
  10. package/dist/cli/chunk-2xsxcdp6.js +3 -0
  11. package/dist/cli/{chunk-fexzfgd1.js → chunk-2y70y8na.js} +1 -1
  12. package/dist/cli/{chunk-tw9w8pzg.js → chunk-36eyrv4r.js} +3 -3
  13. package/dist/cli/chunk-37fas9aj.js +2 -0
  14. package/dist/cli/chunk-3dhv5n19.js +2 -0
  15. package/dist/cli/{chunk-kx53jjee.js → chunk-3x5g06mw.js} +8 -6
  16. package/dist/cli/{chunk-8sggyn8y.js → chunk-3y0z478f.js} +1 -1
  17. package/dist/cli/chunk-4dt1gfb3.js +3 -0
  18. package/dist/cli/chunk-4w4tpsqx.js +2 -0
  19. package/dist/cli/chunk-5apxck3d.js +97 -0
  20. package/dist/cli/chunk-5vrg6h71.js +3 -0
  21. package/dist/cli/chunk-5xpvd182.js +2 -0
  22. package/dist/cli/chunk-5yfffvqe.js +2 -0
  23. package/dist/cli/{chunk-gryq2vgw.js → chunk-5yh5trgy.js} +1 -1
  24. package/dist/cli/chunk-61cn88e7.js +15 -0
  25. package/dist/cli/{chunk-g9yba5vh.js → chunk-671ak750.js} +2 -2
  26. package/dist/cli/chunk-676f3tva.js +5 -0
  27. package/dist/cli/chunk-6hcyvg0m.js +3 -0
  28. package/dist/cli/chunk-6mg5zp8y.js +2 -0
  29. package/dist/cli/chunk-6q4br0pt.js +2 -0
  30. package/dist/cli/chunk-6wfhq8xd.js +4 -0
  31. package/dist/cli/{chunk-ypd337d0.js → chunk-748656h7.js} +3 -3
  32. package/dist/cli/chunk-7bakcyes.js +5 -0
  33. package/dist/cli/{chunk-hfdk7j28.js → chunk-7kgcy89w.js} +1 -1
  34. package/dist/cli/chunk-8qkyd589.js +5 -0
  35. package/dist/cli/chunk-9ckvdy0j.js +3 -0
  36. package/dist/cli/{chunk-egpcaxwb.js → chunk-9fdy8vne.js} +1 -1
  37. package/dist/cli/{chunk-h8t4xg1p.js → chunk-9rj2fx6v.js} +1 -1
  38. package/dist/cli/chunk-9x2xd1rz.js +2 -0
  39. package/dist/cli/{chunk-r77pfw4m.js → chunk-9zswhjkv.js} +1 -1
  40. package/dist/cli/{chunk-p6jw0zcd.js → chunk-agvtzads.js} +1 -1
  41. package/dist/cli/chunk-aq4j8ar4.js +2 -0
  42. package/dist/cli/{chunk-ecrw211n.js → chunk-aqwckba5.js} +1 -1
  43. package/dist/cli/{chunk-rzef6szc.js → chunk-atg13xab.js} +1 -1
  44. package/dist/cli/{chunk-ez438kn3.js → chunk-b95q6zhk.js} +1 -1
  45. package/dist/cli/chunk-bagwgxsg.js +2 -0
  46. package/dist/cli/{chunk-4hnz77xf.js → chunk-bcyex84w.js} +1 -1
  47. package/dist/cli/chunk-bv050ssb.js +8 -0
  48. package/dist/cli/chunk-bykqhbvy.js +2 -0
  49. package/dist/cli/{chunk-29s67j3k.js → chunk-ca4x8s96.js} +4 -4
  50. package/dist/cli/chunk-cakyme4s.js +2 -0
  51. package/dist/cli/{chunk-16ttm9x6.js → chunk-ccmke5sx.js} +1 -1
  52. package/dist/cli/chunk-cf3vpsm5.js +3 -0
  53. package/dist/cli/chunk-d552cvb2.js +2 -0
  54. package/dist/cli/{chunk-gcg5h667.js → chunk-dsdtq087.js} +1 -1
  55. package/dist/cli/{chunk-387ecqb8.js → chunk-e58kj4gm.js} +1 -1
  56. package/dist/cli/chunk-ee314raa.js +2 -0
  57. package/dist/cli/{chunk-0rrmz6k6.js → chunk-eege1x0a.js} +1 -1
  58. package/dist/cli/{chunk-m6yy3qc1.js → chunk-fgtc1xq9.js} +1 -1
  59. package/dist/cli/{chunk-5dd7trgf.js → chunk-fq2pefc5.js} +1 -1
  60. package/dist/cli/chunk-g4p5znv1.js +2 -0
  61. package/dist/cli/chunk-g9ba25q5.js +3 -0
  62. package/dist/cli/{chunk-qe7xb4rm.js → chunk-g9xknhjq.js} +2 -2
  63. package/dist/cli/chunk-gbd5j3dv.js +2 -0
  64. package/dist/cli/{chunk-ycd28edp.js → chunk-ggjmcqpt.js} +3 -4
  65. package/dist/cli/chunk-hv79ys32.js +1 -1
  66. package/dist/cli/chunk-hz1x3hak.js +3 -0
  67. package/dist/cli/{chunk-03rbg95q.js → chunk-hzz020xx.js} +5 -5
  68. package/dist/cli/chunk-j1qy79fd.js +3 -0
  69. package/dist/cli/chunk-j9tsdn1d.js +2 -0
  70. package/dist/cli/chunk-jejmt4k0.js +140 -0
  71. package/dist/cli/chunk-jj5f4n51.js +1 -1
  72. package/dist/cli/{chunk-nzbq51h9.js → chunk-k225696b.js} +1 -1
  73. package/dist/cli/{chunk-by6fjf7m.js → chunk-kag7cfbr.js} +1 -1
  74. package/dist/cli/{chunk-jwedmkm8.js → chunk-kdnk8m5j.js} +1 -1
  75. package/dist/cli/{chunk-yzkhwn31.js → chunk-kxp04jnp.js} +1 -1
  76. package/dist/cli/chunk-m4z6kepz.js +3 -0
  77. package/dist/cli/chunk-mdc7b01e.js +3 -0
  78. package/dist/cli/{chunk-bj6sa371.js → chunk-mjyhy152.js} +1 -1
  79. package/dist/cli/chunk-mqfvzdpr.js +3 -0
  80. package/dist/cli/chunk-mqvkd2g1.js +2 -0
  81. package/dist/cli/chunk-mvtqstte.js +4 -0
  82. package/dist/cli/{chunk-qs1r0tqr.js → chunk-mxx0y791.js} +1 -1
  83. package/dist/cli/{chunk-1rjq808h.js → chunk-n0rdn2fn.js} +1 -1
  84. package/dist/cli/{chunk-3v9s3bcz.js → chunk-n2zgej1a.js} +1 -1
  85. package/dist/cli/chunk-n94vzkbe.js +3 -0
  86. package/dist/cli/chunk-njncqdwf.js +2 -0
  87. package/dist/cli/{chunk-6jk2khme.js → chunk-nw092jak.js} +1 -1
  88. package/dist/cli/{chunk-22d4fjjb.js → chunk-pfagrkt6.js} +1 -1
  89. package/dist/cli/{chunk-sa48rtjz.js → chunk-pnrftncm.js} +1 -1
  90. package/dist/cli/{chunk-mxgtz5vz.js → chunk-pqr6td4z.js} +1 -1
  91. package/dist/cli/chunk-q8ffd1pc.js +3 -0
  92. package/dist/cli/{chunk-snqa07h6.js → chunk-qcjkactr.js} +1 -1
  93. package/dist/cli/{chunk-tjj9bt7n.js → chunk-qqr4tfhk.js} +1 -1
  94. package/dist/cli/chunk-qs359899.js +3 -0
  95. package/dist/cli/{chunk-sza3tyhs.js → chunk-qzzmd63x.js} +2 -3
  96. package/dist/cli/{chunk-xkmdxcjg.js → chunk-r2bb8b11.js} +1 -1
  97. package/dist/cli/{chunk-rtpa8hg3.js → chunk-rjfdzjhj.js} +1 -1
  98. package/dist/cli/chunk-rjy900p4.js +4 -0
  99. package/dist/cli/chunk-rmsdybs3.js +20 -0
  100. package/dist/cli/{chunk-y5axx6ev.js → chunk-rztqccs3.js} +1 -1
  101. package/dist/cli/chunk-s1308b3d.js +63 -0
  102. package/dist/cli/{chunk-b492aee1.js → chunk-se00fc8v.js} +1 -1
  103. package/dist/cli/chunk-shtm72c0.js +3 -0
  104. package/dist/cli/{chunk-dpq60qph.js → chunk-t9vjj8sy.js} +1 -1
  105. package/dist/cli/chunk-ta1p2nwa.js +6 -0
  106. package/dist/cli/chunk-teh40faw.js +1 -1
  107. package/dist/cli/{chunk-gqh6atvp.js → chunk-tjhw09bz.js} +1 -1
  108. package/dist/cli/chunk-tnatqcy7.js +2 -0
  109. package/dist/cli/chunk-tq0h3vy6.js +3 -0
  110. package/dist/cli/{chunk-9er68n24.js → chunk-tt58819p.js} +1 -1
  111. package/dist/cli/chunk-tw9tfs1d.js +3 -0
  112. package/dist/cli/chunk-v6dz59gy.js +2 -0
  113. package/dist/cli/chunk-v6v272b3.js +3 -0
  114. package/dist/cli/chunk-vkv1v3xn.js +56 -0
  115. package/dist/cli/chunk-vkz6kkmm.js +24 -0
  116. package/dist/cli/chunk-vp29jgbt.js +4 -0
  117. package/dist/cli/chunk-vpsarakj.js +2 -0
  118. package/dist/cli/{chunk-xbwq4bc0.js → chunk-vr2r2ncm.js} +1 -1
  119. package/dist/cli/{chunk-qvnr8c18.js → chunk-vvqwgcge.js} +1 -1
  120. package/dist/cli/{chunk-5gzpq34w.js → chunk-vwpf55cr.js} +3 -3
  121. package/dist/cli/{chunk-zaw71cf4.js → chunk-vytrhm50.js} +1 -1
  122. package/dist/cli/chunk-w77s2df8.js +3 -0
  123. package/dist/cli/{chunk-bexnp6g4.js → chunk-w7v71t7e.js} +1 -1
  124. package/dist/cli/chunk-wfgrn8p2.js +4 -0
  125. package/dist/cli/chunk-wzss4pzh.js +3 -0
  126. package/dist/cli/chunk-x1ghdmp1.js +2 -0
  127. package/dist/cli/chunk-x1jxxmtc.js +3 -0
  128. package/dist/cli/{chunk-edrxs4cy.js → chunk-x83xbzjd.js} +5 -5
  129. package/dist/cli/{chunk-9trbc3kj.js → chunk-x85rcep5.js} +2 -2
  130. package/dist/cli/{chunk-jd7p0y5x.js → chunk-x89zw7br.js} +1 -1
  131. package/dist/cli/chunk-xg42e2ff.js +19 -0
  132. package/dist/cli/{chunk-sbds7zx4.js → chunk-xte4phd1.js} +1 -1
  133. package/dist/cli/chunk-y8b2p44q.js +1 -1
  134. package/dist/cli/chunk-ys6v36ph.js +2 -0
  135. package/dist/cli/chunk-yzdrhytj.js +10 -0
  136. package/dist/cli/{chunk-39sfhppm.js → chunk-z3qz6enf.js} +1 -1
  137. package/dist/cli/chunk-z3wrbtcq.js +22 -0
  138. package/dist/cli/chunk-z43e08kw.js +4 -0
  139. package/dist/cli/{chunk-t34kjg5y.js → chunk-zpwvjt14.js} +7 -4
  140. package/dist/cli/chunk-zrf93zrx.js +2 -0
  141. package/dist/cli/index.js +9 -12
  142. package/dist/cli/pty-host-node.mjs +259 -32
  143. package/dist/cli/rove-run.js +1 -1
  144. package/dist/cli/rove.js +1 -1
  145. package/dist/completions/rove.bash +2 -2
  146. package/dist/completions/rove.fish +2 -2
  147. package/dist/completions/rove.zsh +2 -2
  148. package/dist/skills/rove/SKILL.md +25 -10
  149. package/dist/skills/rove/references/api-flags.md +30 -14
  150. package/dist/web/assets/index-BVQbpUea.css +1 -0
  151. package/dist/web/assets/index-Cn_7vMHX.js +152 -0
  152. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  153. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  154. package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-8K4wrrwR.woff +0 -0
  155. package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-EVf6-Yzo.woff2 +0 -0
  156. package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  157. package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  158. package/dist/web/assets/jetbrains-mono-greek-600-normal-H7WoG9Et.woff2 +0 -0
  159. package/dist/web/assets/jetbrains-mono-greek-600-normal-mc2nkWzM.woff +0 -0
  160. package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  161. package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  162. package/dist/web/assets/jetbrains-mono-latin-600-normal-BfsvjouI.woff +0 -0
  163. package/dist/web/assets/jetbrains-mono-latin-600-normal-C8RAYTDA.woff2 +0 -0
  164. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  165. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  166. package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-BfB_LPfz.woff2 +0 -0
  167. package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-DObL3zCW.woff +0 -0
  168. package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  169. package/dist/web/assets/jetbrains-mono-vietnamese-600-normal-OWROknRo.woff +0 -0
  170. package/dist/web/index.html +2 -2
  171. package/dist/web/relay/index.html +34 -0
  172. package/dist/web/sw.js +52 -0
  173. package/dist/web-ui/assets/{harness-pEyVtWV4.js → harness-C7ciHx2w.js} +2 -2
  174. package/dist/web-ui/assets/{index-Dyg-qcgB.js → index-B1FLht5L.js} +2 -2
  175. package/dist/web-ui/assets/{index-Bsn8NuKa.css → index-BDIkPexK.css} +1 -1
  176. package/dist/web-ui/index.html +2 -2
  177. package/package.json +3 -3
  178. package/dist/cli/chunk-033k4ap7.js +0 -2
  179. package/dist/cli/chunk-0g3sjn8z.js +0 -3
  180. package/dist/cli/chunk-0ppkvphp.js +0 -2
  181. package/dist/cli/chunk-11wyec8z.js +0 -2
  182. package/dist/cli/chunk-15fp4p9v.js +0 -15
  183. package/dist/cli/chunk-1exwrmjd.js +0 -3
  184. package/dist/cli/chunk-1ffqacz9.js +0 -3
  185. package/dist/cli/chunk-2v68zaaa.js +0 -2
  186. package/dist/cli/chunk-2y29zrm1.js +0 -2
  187. package/dist/cli/chunk-4d1v6066.js +0 -3
  188. package/dist/cli/chunk-4dxc29mm.js +0 -2
  189. package/dist/cli/chunk-5zsdjrgz.js +0 -4
  190. package/dist/cli/chunk-68wpp5y8.js +0 -20
  191. package/dist/cli/chunk-6h4qdckd.js +0 -3
  192. package/dist/cli/chunk-6jj82bej.js +0 -2
  193. package/dist/cli/chunk-783d1ba0.js +0 -3
  194. package/dist/cli/chunk-78v1q5mz.js +0 -2
  195. package/dist/cli/chunk-7z463zkx.js +0 -3
  196. package/dist/cli/chunk-97m9nyw7.js +0 -2
  197. package/dist/cli/chunk-9bae7f3m.js +0 -4
  198. package/dist/cli/chunk-b5c7p22a.js +0 -2
  199. package/dist/cli/chunk-b5pytfgm.js +0 -2
  200. package/dist/cli/chunk-b68dd36h.js +0 -3
  201. package/dist/cli/chunk-bbksmb5a.js +0 -3
  202. package/dist/cli/chunk-cpf78z3x.js +0 -4
  203. package/dist/cli/chunk-cyzp2btw.js +0 -63
  204. package/dist/cli/chunk-d9e4ssy7.js +0 -12
  205. package/dist/cli/chunk-e01321zn.js +0 -5
  206. package/dist/cli/chunk-ec7f3wcm.js +0 -3
  207. package/dist/cli/chunk-erx7x9xg.js +0 -97
  208. package/dist/cli/chunk-ewqqwtrj.js +0 -2
  209. package/dist/cli/chunk-fmbv4m3y.js +0 -25
  210. package/dist/cli/chunk-fs51wx31.js +0 -3
  211. package/dist/cli/chunk-fvvb0072.js +0 -3
  212. package/dist/cli/chunk-g263457v.js +0 -2
  213. package/dist/cli/chunk-g3tv8kht.js +0 -2
  214. package/dist/cli/chunk-g53yqr82.js +0 -78
  215. package/dist/cli/chunk-k67vbszb.js +0 -3
  216. package/dist/cli/chunk-kyt9ytz2.js +0 -20
  217. package/dist/cli/chunk-n2ejvjrd.js +0 -3
  218. package/dist/cli/chunk-p4s826r2.js +0 -96
  219. package/dist/cli/chunk-p6334e7v.js +0 -2
  220. package/dist/cli/chunk-q9zgj6kw.js +0 -3
  221. package/dist/cli/chunk-qfe1wf5c.js +0 -2
  222. package/dist/cli/chunk-r40aj7z6.js +0 -2
  223. package/dist/cli/chunk-sqxdfzdq.js +0 -5
  224. package/dist/cli/chunk-sz2xs0a2.js +0 -4
  225. package/dist/cli/chunk-t5y8k772.js +0 -2
  226. package/dist/cli/chunk-vht2e44j.js +0 -10
  227. package/dist/cli/chunk-vjp7drne.js +0 -2
  228. package/dist/cli/chunk-vm551pxs.js +0 -2
  229. package/dist/cli/chunk-w09356fj.js +0 -20
  230. package/dist/cli/chunk-w9cdc97q.js +0 -3
  231. package/dist/cli/chunk-x20bs6bs.js +0 -3
  232. package/dist/cli/chunk-xmqv1zx6.js +0 -2
  233. package/dist/cli/chunk-xn7bk4f9.js +0 -3
  234. package/dist/cli/chunk-zybjm7d5.js +0 -2
  235. package/dist/web/assets/index-C9vaSoIJ.js +0 -113
  236. package/dist/web/assets/index-sA2EEQJw.css +0 -1
@@ -0,0 +1,8 @@
1
+ // @bun
2
+ import{canonPath,normalizeMainRepo,randomDirTaskSuffix,repoWorkingDir,samePrStatus,titleFromRepo}from"./chunk-1wwqf6kx.js";import{readLastActiveTaskId,writeLastActiveTaskId}from"./chunk-c1dz3qg3.js";import{CLONEFILEAT_PERL,GitWorktreeManager,defaultExecDeps,git,isDirtyOutput,parseDirtyPaths}from"./chunk-3x5g06mw.js";import{LAUNCH_PREFIX_FILE}from"./chunk-4dt1gfb3.js";import{PLACEHOLDER_TASK_TITLE,isPlaceholderDerivedBranch,sanitizeTaskTitle}from"./chunk-2xsxcdp6.js";import{CannotDeleteMainTaskError,DirtyWorktreeError,EmptyBranchDirtyWorktreeError,EmptyBranchError,ExtraRepoNotGitError,ForkUnsupportedError,GitCommandFailedError,IllegalTransitionError,InvalidWorktreeNameError,LandConflictError,MainCheckoutDirtyError,MissingRefError,MoveRefusedError,NotAGitRepoError,NotALinkedRepoError,REMOTE_PARENT_UNSUPPORTED_CODE,RemoteExtraUnsupportedError,RemoteParentUnsupportedError,ReparentRefusedError,RepoDuplicateError,TaskDeletingError,TaskNotFoundError,TooManyReposError,WorktreeNameTakenError,WorktreeRemoveFailedError,canonicalize,listWorktreeDirNames,worktreePathFor}from"./chunk-1vxtd8rh.js";import{detectLanguage}from"./chunk-hpay0ax8.js";import{resolvePreferredVendor}from"./chunk-9rj2fx6v.js";import{addSavedRepo,getPersistedString,getSavedRepos,isGitRepo,isRemoteRepoKey,removeSavedRepo,resolveMainRepoRoot,resolveRepoRoot}from"./chunk-rfp0ayra.js";import{READ_ONLY_GIT_ENV}from"./chunk-6xppe9bj.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{projectRejection,rejectionReason}from"./chunk-7mwvvj46.js";import{createStateCell}from"./chunk-cmp64q4j.js";import{MULTI_REPO_CAP,SESSION_PR_CAP,WORKSPACE_CAPABILITIES,applyTodoOps,checkNotes,childrenOf,gitRepoOf,lineageRootId,pickRepos,repoNameOf,subtreeIds,taskCwd,taskRepos,taskWorktreePaths,uniqueRepoName,workspaceBackendId,workspaceCapabilities}from"./chunk-x1jxxmtc.js";import{DEFAULT_TASK_VENDOR,toTaskId}from"./chunk-xy8be792.js";import{pathIdentity,pathWithin,samePath}from"./chunk-pn5ygqn6.js";class WorkspaceCheckpoints{store;worktrees;workspaces;requireTask;constructor(store,worktrees,workspaces,requireTask){this.store=store;this.worktrees=worktrees;this.workspaces=workspaces;this.requireTask=requireTask}workspaceOwner(id){let task=this.requireTask(id);return(task.sharesWorkspaceOf?this.store.get(task.sharesWorkspaceOf):void 0)??task}checkpointScope(id){let owner=this.workspaceOwner(id),sharers=this.store.list().filter((t)=>t.sharesWorkspaceOf===owner.id&&!t.deletion);return{ownerId:String(owner.id),sharerIds:[String(owner.id),...sharers.map((t)=>String(t.id))]}}canCheckpoint(id){if(!this.store.get(id))return!1;let owner=this.workspaceOwner(id);return workspaceCapabilities(owner).checkpoint&&!owner.deletion&&owner.worktreePath!==""}async target(id){let owner=this.workspaceOwner(id),own=this.canCheckpoint(id)?this.workspaces.get(workspaceBackendId(owner)).checkpoints:void 0,dir=owner.worktreePath;if(!this.canCheckpoint(id)||!await this.worktrees.pathExists(own?dir:`${dir}/.git`))throw Error(`NOT_CHECKPOINTABLE: ${owner.kind==="task"?"the task has no workspace on disk":`a ${owner.kind} task works in your own checkout`}`);let ownerId=String(owner.id);return{ownerId,dir,via:own??{take:(d,opts)=>this.worktrees.takeCheckpoint(d,ownerId,opts),list:(d)=>this.worktrees.listCheckpoints(d,ownerId),rewind:(d,n)=>this.worktrees.rewindToCheckpoint(d,ownerId,n)}}}async takeCheckpoint(id,opts){let{dir,via}=await this.target(id);return via.take(dir,opts)}async listCheckpoints(id){let{dir,via}=await this.target(id);return via.list(dir)}async rewindToCheckpoint(id,n){let{dir,via}=await this.target(id);return via.rewind(dir,n)}async exportBranch(id){let owner=this.workspaceOwner(id),backend=this.workspaces.get(workspaceBackendId(owner));if(!backend.exportBranch||!owner.worktreePath)throw Error("EXPORT_UNSUPPORTED: the task's branch is already in its repo (or it has no workspace yet)");return backend.exportBranch({path:owner.worktreePath,repo:gitRepoOf(owner),branch:owner.branch})}}var TYPE_PREFIXES=new Set(["feat","feature","fix","bugfix","hotfix","chore","docs","doc","refactor","test","tests","ci","build","perf","style","revert","release"]),BRAND_TOKENS=new Set(["rove"]);function inferBranchStyle(names){let typed=new Map,bare=0;for(let raw of new Set(names)){let name=raw.trim();if(!name||name==="HEAD")continue;let slash=name.indexOf("/");if(slash===-1){bare+=1;continue}let prefix=name.slice(0,slash);if(TYPE_PREFIXES.has(prefix))typed.set(prefix,(typed.get(prefix)??0)+1)}let typedTotal=0;for(let n of typed.values())typedTotal+=n;if(typedTotal===0||typedTotal<bare)return{kind:"bare"};let best="",bestN=0;for(let[prefix,n]of typed)if(n>bestN||n===bestN&&(best===""||prefix<best))best=prefix,bestN=n;return{kind:"typed",defaultPrefix:best}}function slugTokens(title){return title.toLowerCase().replace(/[^a-z0-9]+/g,"-").split("-").filter((t)=>t.length>0&&!BRAND_TOKENS.has(t))}function capSlug(tokens){let slug="";for(let token of tokens){let candidate=slug?`${slug}-${token}`:token;if(candidate.length>32)return slug||token.slice(0,32);slug=candidate}return slug}function identitySlug(taskId){return`task-${taskId.slice(-6).toLowerCase()}`}function deriveConventionBranch(title,style,taskId){let tokens=slugTokens(title);if(style.kind==="typed"){let first=tokens[0];return`${first!==void 0&&TYPE_PREFIXES.has(first)?tokens.shift():style.defaultPrefix}/${capSlug(tokens)||identitySlug(taskId)}`}return capSlug(tokens)||identitySlug(taskId)}function uniqueBranchName(base,taken,taskId){let dirs=new Set;for(let name of taken)for(let i=name.indexOf("/");i>0;i=name.indexOf("/",i+1))dirs.add(name.slice(0,i));let isFree=(name)=>!taken.has(name)&&!dirs.has(name),segments=base.split("/"),root=segments.slice(0,-1).some((_,i)=>taken.has(segments.slice(0,i+1).join("/")))?segments.join("-"):base;if(isFree(root))return root;for(let n=2;n<=99;n+=1){let candidate=`${root}-${n}`;if(isFree(candidate))return candidate}return`${root}-${taskId.slice(-6).toLowerCase()}`}function planExtraRepos(primary,inputs){if(inputs.length===0)return[];if(1+inputs.length>MULTI_REPO_CAP)throw new TooManyReposError(1+inputs.length,MULTI_REPO_CAP);if(isRemoteRepoKey(primary))throw new RemoteExtraUnsupportedError(primary);let seen=new Set([canonPath(primary)]),names=new Set([repoNameOf(primary)]);return inputs.map((input)=>{if(isRemoteRepoKey(input.repo))throw new RemoteExtraUnsupportedError(input.repo);let{repo}=normalizeMainRepo(input.repo);if(!isGitRepo(repo))throw new ExtraRepoNotGitError(input.repo);if(seen.has(canonPath(repo)))throw new RepoDuplicateError(repo);seen.add(canonPath(repo));let name=uniqueRepoName(repoNameOf(repo),names);names.add(name);let baseRef=input.baseRef?.trim();return{name,repo,branch:"",worktreePath:"",...baseRef?{baseRef}:{}}})}async function rollbackWorktree(backend,created){try{await backend.dispose({path:created.path,...created.fresh},created.fresh?{force:!0,deleteBranch:!0}:{force:!0})}catch(err){console.error(`[rove] ensureWorktree rollback failed for ${created.path}:`,err)}}async function extrasToMaterialise(worktrees,extras){let missing=[];for(let extra of extras??[])if(!extra.worktreePath||!await worktrees.pathExists(extra.worktreePath))missing.push(extra);return missing}function scaleProgress(onProgress,step,total){return onProgress&&((percent)=>onProgress(Math.floor((step+percent/100)/total*100)))}async function materialiseExtras(opts){let{worktrees,backend,slugs,task}=opts,callerNamed=task.branch!=="",created=[],picked=[],done=new Map,missing=await extrasToMaterialise(worktrees,task.extraRepos);try{for(let[index,extra]of missing.entries()){if(extra.worktreePath)await worktrees.pruneWorktrees(extra.repo);let names=await worktrees.listBranchNames(extra.repo),branch=extra.branch||(callerNamed||!names.includes(opts.branch)?opts.branch:uniqueBranchName(opts.branch,new Set(names),task.id)),slug=await slugs.allocate(extra.repo);picked.push({repo:extra.repo,slug});let info=await backend.materialize({repo:extra.repo,slug,branch,baseRef:extra.baseRef,onProgress:opts.progressFor(index)});created.push({path:info.path,...names.includes(branch)?{}:{fresh:{repo:extra.repo,branch}}}),done.set(extra.name,{...extra,branch,worktreePath:info.path})}}catch(err){for(let wt of created.reverse())await rollbackWorktree(backend,wt);for(let{repo,slug}of picked)slugs.cancel(repo,slug);throw err}return{extraRepos:(task.extraRepos??[]).map((e)=>done.get(e.name)??e),created,slugs:picked}}function nestingParent(store,id){let parent=store.get(id);if(!parent)throw new TaskNotFoundError(id);if(parent.deletion)throw new TaskDeletingError(String(parent.id));if(isRemoteRepoKey(parent.repo))throw new RemoteParentUnsupportedError(String(parent.id));return toTaskId(lineageRootId(store.list(),parent.id))}async function createTaskRow(deps,input){if(!input.repo)throw Error("createTask: repo is required");let extraRepos=planExtraRepos(normalizeMainRepo(input.repo).repo,input.extraRepos??[]),parentTaskId=input.parentTaskId?nestingParent(deps.store,input.parentTaskId):void 0,repo=(await deps.mainTasks.ensureIfEligible(input.repo,input.projectIntent??"explicit"))?.repo??normalizeMainRepo(input.repo).repo,title=sanitizeTaskTitle(input.title??PLACEHOLDER_TASK_TITLE)||PLACEHOLDER_TASK_TITLE,worktreeName=input.worktreeName?.trim();if(worktreeName)await deps.claimWorktreeName(repo,worktreeName);return await deps.store.create({repo,title,branch:input.branch??"",worktreePath:"",status:"backlog",kind:"task",vendor:input.vendor??DEFAULT_TASK_VENDOR,...input.command?.trim()?{command:input.command.trim()}:{},...input.modelEffort?{modelEffort:input.modelEffort}:{},...input.model?.trim()?{model:input.model.trim()}:{},...input.tier?.trim()?{tier:input.tier.trim()}:{},...input.groupId?{groupId:input.groupId}:{},...input.dispatcher?{dispatcher:input.dispatcher}:{},...parentTaskId?{parentTaskId}:{},...input.cwdSubpath?{cwdSubpath:input.cwdSubpath}:{},...input.routine?{routine:input.routine}:{},...input.baseRef?.trim()?{baseRef:input.baseRef.trim()}:{},...extraRepos.length>0?{extraRepos}:{},...input.workspace?{workspace:input.workspace}:{},...worktreeName?{worktreeName}:{}})}async function openDirectoryTaskRow(deps,input){if(!input.dir)throw Error("openDirectoryTask: dir is required");let dir=canonPath(input.dir),title=(input.title?sanitizeTaskTitle(input.title):"")||(input.scratch?"":`${titleFromRepo(dir)}-${randomDirTaskSuffix()}`);return deps.store.create({repo:dir,title,branch:"",worktreePath:dir,status:"backlog",kind:"dir",...input.scratch?{scratch:!0}:{},...input.noProject?{noProject:!0}:{},...input.routine?{routine:input.routine}:{},...input.modelEffort?{modelEffort:input.modelEffort}:{},...input.model?.trim()?{model:input.model.trim()}:{},vendor:input.vendor??resolvePreferredVendor()})}async function adoptScratchRepoRow(deps,id,repo){let task=deps.store.get(id);if(!task||task.kind!=="dir"||task.scratch!==!0)return;let dir=canonPath(repo);await deps.store.update(id,{repo:dir,worktreePath:dir,scratch:!1,title:titleFromRepo(dir)})}class BorrowedBackend{id="borrowed";capabilities=WORKSPACE_CAPABILITIES.borrowed;async materialize(spec){return{backend:this.id,path:spec.path??repoWorkingDir(spec.repo)}}async dispose(){}}import{execFile}from"child_process";import{promises as fs,statSync}from"fs";import path from"path";import{promisify}from"util";var run=promisify(execFile),CP_CHUNK=200;function cloneFlags(){if(process.platform==="darwin")return["-c","-R","-P"];if(process.platform==="linux")return["--reflink=always","-a"];return null}var cpClone=async(entries,to)=>{let flags=cloneFlags();if(!flags)throw new ForkUnsupportedError(`copy-on-write clones are not supported on ${process.platform}`);for(let i=0;i<entries.length;i+=CP_CHUNK)try{await run("cp",[...flags,...entries.slice(i,i+CP_CHUNK),`${to}${path.sep}`])}catch(err){let detail=err.stderr?.trim()||String(err);throw Error(`cloning the worktree failed: ${detail}`)}},probeCache=new Map;async function cloneOneFile(source,target){if(process.platform==="darwin")await run("/usr/bin/perl",["-e",CLONEFILEAT_PERL,source,target]);else if(process.platform==="linux")await run("cp",["--reflink=always",source,target]);else throw Error(`no clone primitive on ${process.platform}`)}var volumeProbe=(from,toDir)=>{let key=`${statSync(from).dev}:${statSync(toDir).dev}`,known=probeCache.get(key);if(!known)known=(async()=>{let probe=path.join(toDir,`.rove-clone-probe-${process.pid}`);try{return await cloneOneFile(path.join(from,".git"),probe),!0}catch{return!1}finally{await fs.rm(probe,{force:!0})}})(),probeCache.set(key,known);return known};async function git2(cwd,args){let{stdout}=await run("git",[...args],{cwd,maxBuffer:67108864});return stdout.trim()}class CowCloneBackend{plain;id="cow-clone";capabilities=WORKSPACE_CAPABILITIES["cow-clone"];probe;cloneEntries;constructor(plain,deps={}){this.plain=plain;this.probe=deps.probe??volumeProbe,this.cloneEntries=deps.cloneEntries??cpClone}async materialize(spec){let parent=spec.forkFrom;if(!parent)return{...await this.plain.materialize(spec),backend:this.id};if(isRemoteRepoKey(spec.repo))throw new ForkUnsupportedError("a remote project's worktree cannot be cloned");let target=worktreePathFor(spec.repo,spec.slug),root=path.dirname(target);if(await fs.mkdir(root,{recursive:!0}),!await this.probe(parent,root))throw new ForkUnsupportedError(`${parent} and ${root} are not on one volume that can clone files (APFS, btrfs, XFS with reflink); the fork would have to copy the whole tree`);let head=await git2(parent,["rev-parse","HEAD"]);await git2(spec.repo,["worktree","add","--no-checkout","-b",spec.branch,target,head]);try{let entries=(await fs.readdir(parent)).filter((name)=>name!==".git").map((name)=>path.join(parent,name));await this.cloneEntries(entries,target),await this.carryIndex(parent,target)}catch(err){throw await this.discard(spec.repo,target,spec.branch),err}return{backend:this.id,path:target}}async carryIndex(parent,target){let indexOf=(dir)=>git2(dir,["rev-parse","--path-format=absolute","--git-path","index"]);try{await fs.copyFile(await indexOf(parent),await indexOf(target)),await git2(target,["update-index","-q","--refresh"])}catch{await git2(target,["reset","-q"])}}async discard(repo,target,branch){await git2(repo,["worktree","remove","--force",target]).catch(()=>{}),await fs.rm(target,{recursive:!0,force:!0}),await git2(repo,["worktree","prune"]).catch(()=>{}),await git2(repo,["branch","-D",branch]).catch(()=>{})}dispose(target,opts){return this.plain.dispose(target,opts)}}class GitWorktreeBackend{worktrees;id="git-worktree";capabilities=WORKSPACE_CAPABILITIES["git-worktree"];constructor(worktrees){this.worktrees=worktrees}async materialize(spec){let info=await this.worktrees.createForTask({repo:spec.repo,slug:spec.slug,branch:spec.branch,baseRef:spec.baseRef,onProgress:spec.onProgress});return{backend:this.id,path:info.path}}async dispose(target,opts){await this.worktrees.remove(target.path,{...opts,repo:target.repo,branch:target.branch})}}import{execFile as execFile2}from"child_process";import{createHash}from"crypto";import{promises as fs2,readFileSync}from"fs";import os from"os";import path2 from"path";import{promisify as promisify2}from"util";var LAYERFS_CONFIG_KEY="experimental.layerfs",STATE_FILE=".rove-layerfs.json",SANDBOX_USER="501:20",SANDBOX_HOME="/tmp/rove-home",KILL_TAGGED=`for p in /proc/[0-9]*; do tr '\\0' '\\n' < $p/environ 2>/dev/null | grep -qx "$1" && kill -KILL \${p#/proc/}; done; true`,KILL_UNDER='for p in /proc/[0-9]*; do case $(readlink $p/cwd) in "$1"|"$1"/*) kill -KILL ${p#/proc/};; esac; done; sleep 0.2',execRun=async(file,args)=>{try{let{stdout}=await promisify2(execFile2)(file,[...args],{maxBuffer:67108864});return stdout}catch(err){let e=err;throw Error((e.stderr?.trim()||e.stdout?.trim()||e.message).slice(0,2000))}};function parseLayerfsEnv(text){let env={};for(let line of text.split(`
3
+ `)){let m=/^\s*(?:export\s+)?([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);if(m)env[m[1]]=m[2].trim().replace(/^(["'])(.*)\1$/,"$2")}return env}class LayerfsBackend{id="layerfs";capabilities=WORKSPACE_CAPABILITIES.layerfs;run;configPath;platform;rootChecked=!1;constructor(deps={}){this.run=deps.run??execRun,this.configPath=deps.configPath??(()=>getPersistedString(LAYERFS_CONFIG_KEY)),this.platform=deps.platform??process.platform}async preflight(){this.env(),await this.requireRoot()}env(){if(this.platform!=="linux")throw unavailable(`it runs on Linux Runners only, not ${this.platform}`);let file=this.configPath();if(!file)throw unavailable(`set ${LAYERFS_CONFIG_KEY} in state.json to a LayerFS env file`);let env;try{env=parseLayerfsEnv(readFileSync(file,"utf8"))}catch(err){throw unavailable(`cannot read ${file}: ${err.message}`)}for(let key of["LAYERFS_CLI","LAYERFS_NODE","LAYERFS_HOME","LAYERFS_STORE_PATH","LAYERFS_IMAGE"])if(!env[key])throw unavailable(`${file} does not set ${key}`);return env}async requireRoot(){if(this.rootChecked)return;try{await this.run("sudo",["-n","true"])}catch{throw Error("LAYERFS_NEEDS_ROOT: LayerFS Store calls run as root, and `sudo -n` cannot (configure passwordless sudo for this user)")}this.rootChecked=!0}async lfs(project,...args){let env=this.env();await this.requireRoot();let pass=Object.entries(env).filter(([k])=>k!=="LAYERFS_CLI"&&k!=="LAYERFS_NODE").map(([k,v])=>`${k}=${v}`),out=await this.run("sudo",["-n","env",...pass,`LAYERFS_PROJECT=${project}`,env.LAYERFS_NODE,env.LAYERFS_CLI,...args]);return JSON.parse(out)}docker(...args){return this.run("docker",args)}inSandbox(s,dir,...argv){return this.docker("exec","-u",SANDBOX_USER,"-w",dir,"-e",`HOME=${SANDBOX_HOME}`,s.container,...argv)}async ensureProject(repo){let project=`rove-${createHash("sha1").update(repo).digest("hex").slice(0,12)}`;try{await this.lfs(project,"project","list_branches","--page-size","1")}catch{await this.lfs(project,"init",project,repo,"--timeout","1800000")}return project}async materialize(spec){if(spec.forkFrom||spec.path)throw unavailable("a layerfs task cannot be forked or borrow a directory");this.env();let dir=worktreePathFor(spec.repo,spec.slug),project=await this.ensureProject(spec.repo),branch=`${spec.slug}-${Date.now().toString(36)}`;await this.lfs(project,"branch","fork",branch);let{sandbox}=await this.lfs(project,"sandbox","create"),s={project,sandbox,ws:"",container:"",branch,gitBase:"",checkpoints:[]};try{s.container=await this.containerOf(project,sandbox),await this.mount(s,dir),await this.inSandbox(s,dir,"git","checkout","-q","-b",spec.branch,...spec.baseRef?[spec.baseRef]:[]),s.gitBase=(await this.inSandbox(s,dir,"git","rev-parse","HEAD")).trim();let reap=["docker","exec","-u",SANDBOX_USER,s.container,"sh","-c",KILL_TAGGED,"sh","ROVE_LAUNCH={launch}"];await fs2.writeFile(path2.join(dir,LAUNCH_PREFIX_FILE),JSON.stringify({prefix:this.launchPrefix(s,dir),reap})),await this.save(dir,s)}catch(err){throw await this.teardown(s,dir,!0).catch(()=>{}),err}return{backend:this.id,path:dir,ref:`${project}:${branch}`}}launchPrefix(s,dir){let env=["-e",`HOME=${SANDBOX_HOME}`,"-e","ROVE_LAUNCH={launch}","-e","TERM","-e","COLUMNS","-e","LINES"];return["docker","exec","-it","-u",SANDBOX_USER,"-w",dir,...env,s.container]}async containerOf(project,sandbox){let id=(await this.docker("ps","-q","--filter",`label=layerfs.project=${project}`,"--filter",`label=layerfs.sandbox=${sandbox}`)).trim();if(!id||id.includes(`
4
+ `))throw Error(`no single container for LayerFS sandbox ${project}/${sandbox}`);return id}async mount(s,dir){s.ws=(await this.lfs(s.project,"workspace","mount",s.sandbox,s.branch)).ws,await fs2.mkdir(dir,{recursive:!0}),await this.bind(s,dir)}async bind(s,dir){let mountPath=s.ws.slice(s.ws.indexOf(":")+1),script=`mkdir -p "$1" ${SANDBOX_HOME} && chown ${SANDBOX_USER} ${SANDBOX_HOME} && mount --bind "$2" "$1"`;await this.docker("exec","-u","0",s.container,"sh","-c",script,"sh",dir,mountPath)}async unbind(s,dir){try{await this.docker("exec","-u","0",s.container,"umount",dir)}catch(err){if(!/busy/i.test(err.message))throw err;throw Error(`WORKSPACE_BUSY: a process still runs in ${dir} inside the sandbox; close the task's terminals first`)}}async unmount(s,dir,discard){await this.unbind(s,dir);try{await this.lfs(s.project,"workspace","unmount",s.ws,...discard?["--discard"]:[])}catch(err){if(await this.bind(s,dir),!/"code":"Dirty"/.test(err.message))throw err;throw Error(`WORKSPACE_DIRTY: LayerFS has uncommitted changes in ${dir}; checkpoint first, or force`)}}async teardown(s,dir,discard){if(s.ws){if(s.container)await this.docker("exec","-u",SANDBOX_USER,s.container,"sh","-c",KILL_UNDER,"sh",dir);await this.unmount(s,dir,discard)}await this.lfs(s.project,"sandbox","close",s.sandbox),await fs2.rm(path2.join(dir,STATE_FILE),{force:!0}),await fs2.rm(path2.join(dir,LAUNCH_PREFIX_FILE),{force:!0}),await fs2.rmdir(dir).catch(()=>{})}async dispose(target,opts){let s=await this.load(target.path).catch(()=>{return});if(!s){await fs2.rm(path2.join(target.path,LAUNCH_PREFIX_FILE),{force:!0}),await fs2.rmdir(target.path).catch(()=>{});return}await this.teardown(s,target.path,opts.force===!0)}async dirtyPaths(dir){let s=await this.load(dir).catch(()=>{return});if(!s?.ws)return[];let{items}=await this.lfs(s.project,"sandbox","list_workspaces",s.sandbox);return items.find((w)=>`${s.sandbox}:${w.path}`===s.ws)?.changed?["(changes since the last checkpoint, inside the LayerFS sandbox)"]:[]}checkpoints={take:(dir,opts)=>this.take(dir,opts?.tabId),list:async(dir)=>(await this.load(dir)).checkpoints,rewind:async(dir,n)=>{let target=(await this.load(dir)).checkpoints.find((c)=>c.n===n);if(!target)throw Error(`CHECKPOINT_NOT_FOUND: checkpoint ${n} does not exist`);let undo=(await this.take(dir)).checkpoint,after=await this.load(dir),fork=`${after.branch.replace(/-r[0-9a-z]+$/,"")}-r${Date.now().toString(36)}`;return await this.lfs(after.project,"branch","fork",fork,target.commit),await this.unmount(after,dir,!0),after.branch=fork,await this.mount(after,dir),await this.save(dir,after),{target,undo,uncaptured:[]}}};async take(dir,tabId){let s=await this.load(dir),latest=s.checkpoints.at(-1),reply=await this.lfs(s.project,"workspace","commit",s.ws,"-m","rove checkpoint",...latest?[]:["--force"]);if(latest&&reply.kind==="UpToDate")return{created:!1,checkpoint:latest};let checkpoint={n:(latest?.n??0)+1,ref:`layerfs:${s.project}:${s.branch}`,commit:reply.head,tree:reply.commit?.root??reply.root??reply.head,at:new Date().toISOString(),...tabId?{tabId}:{}};return s.checkpoints.push(checkpoint),await this.save(dir,s),{created:!0,checkpoint,uncaptured:[]}}async exportBranch(target){let s=await this.load(target.path),inner=`/tmp/rove-export-${process.pid}.bundle`;await this.inSandbox(s,target.path,"git","bundle","create","-q",inner,target.branch,`^${s.gitBase}`);let local=path2.join(os.tmpdir(),`rove-layerfs-${process.pid}-${Date.now()}.bundle`);try{await this.docker("cp",`${s.container}:${inner}`,local),await this.run("git",["-C",target.repo,"fetch","-q",local,`${target.branch}:refs/heads/${target.branch}`]);let commit=(await this.run("git",["-C",target.repo,"rev-parse",target.branch])).trim();return{branch:target.branch,commit}}finally{await fs2.rm(local,{force:!0})}}async load(dir){return JSON.parse(await fs2.readFile(path2.join(dir,STATE_FILE),"utf8"))}save(dir,s){return fs2.writeFile(path2.join(dir,STATE_FILE),`${JSON.stringify(s,null,2)}
5
+ `)}}function unavailable(why){return Error(`LAYERFS_UNAVAILABLE: the layerfs workspace backend is unavailable: ${why}`)}class WorkspaceRegistry{backends=new Map;constructor(backends){for(let backend of backends)this.backends.set(backend.id,backend)}get(id){let backend=this.backends.get(id);if(!backend)throw Error(`workspace backend '${id}' is not available in this build`);return backend}}function createWorkspaceRegistry(worktrees,cowClone){let plain=new GitWorktreeBackend(worktrees);return new WorkspaceRegistry([plain,new BorrowedBackend,new CowCloneBackend(plain,cowClone),new LayerfsBackend])}async function createOrForkTask(deps,input){if(input.workspace)await deps.workspaces.get(input.workspace.backend).preflight?.();let createRow=(row)=>createTaskRow(deps,row),{forkFrom}=input;return forkFrom?forkTaskRow({...deps,createRow},{...input,forkFrom}):createRow(input)}async function forkTaskRow(deps,input){let parent=deps.store.get(input.forkFrom);if(!parent)throw new TaskNotFoundError(input.forkFrom);if(parent.deletion)throw new TaskDeletingError(String(parent.id));if(!workspaceCapabilities(parent).ownsDirectory)throw new ForkUnsupportedError("a main or directory task has no Rove-created worktree to clone");if(isRemoteRepoKey(parent.repo))throw new ForkUnsupportedError("a remote project's worktree cannot be cloned");if(parent.extraRepos?.length)throw new ForkUnsupportedError("a multi-repo task cannot be forked: only its primary repo would be cloned");let parentPath=await deps.ensureWorktree(parent.id),baseRef=git(["rev-parse","HEAD"],{cwd:parentPath,readOnly:!0}).stdout.trim(),child=await deps.createRow({...input,repo:gitRepoOf(parent),title:input.title??`${parent.title} (fork)`,baseRef,workspace:{backend:"cow-clone",ref:String(parent.id)},...parent.cwdSubpath?{cwdSubpath:parent.cwdSubpath}:{}});if(parent.gitRepo)await deps.store.update(child.id,{repo:parent.repo,gitRepo:parent.gitRepo});try{await deps.cloneInto(child,parentPath)}catch(err){throw await discard(deps,child).catch((cleanup)=>console.error(`[rove] fork rollback failed for ${child.id}:`,cleanup)),await deps.store.remove(child.id),err}return deps.store.get(child.id)??child}async function discard(deps,child){let row=deps.store.get(child.id);if(!row?.worktreePath)return;await deps.workspaces.get(workspaceBackendId(row)).dispose({path:row.worktreePath,repo:gitRepoOf(row),branch:row.branch},{force:!0,deleteBranch:!0})}function baseRepoCtx(repo,deps){let basePath=deps.remoteBasePath(repo);return{exec:deps.execForRepo(repo),dir:basePath??repo}}async function landGit(exec,dir,args,opts){let r=await exec.run(["git",...args],{cwd:dir,env:opts?.readOnly?READ_ONLY_GIT_ENV:void 0});return{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode}}async function isDirty(exec,dir){return isDirtyOutput((await landGit(exec,dir,["status","--porcelain"],{readOnly:!0})).stdout)}async function landPreflight(task,deps=defaultExecDeps){let refuse=(pf,refusal)=>{let refused={...pf,refusal};return{...refused,message:landRefusalError(task,refused).message}},branch=task.branch.trim();if(!branch)throw Error(`landPreflight: task ${task.id} has no branch to land (never materialised)`);let{exec,dir}=baseRepoCtx(gitRepoOf(task),deps),headOut=await landGit(exec,dir,["symbolic-ref","--short","HEAD"],{readOnly:!0}),landedOn=headOut.stdout.trim();if(headOut.exitCode!==0||!landedOn){let headCommit=await landGit(exec,dir,["rev-parse","--verify","--quiet","HEAD"],{readOnly:!0}),detached=headCommit.exitCode===0&&headCommit.stdout.trim().length>0;return refuse({branch,landedOn:"",baseDir:dir},detached?"DETACHED_HEAD":"UNREADABLE_BASE")}if(landedOn===branch)return refuse({branch,landedOn,baseDir:dir},"SAME_BRANCH");let baseDirty=await isDirty(exec,dir),aheadOut=await landGit(exec,dir,["rev-list","--count",`${landedOn}..${branch}`],{readOnly:!0});if(aheadOut.exitCode!==0){let baseHead=await landGit(exec,dir,["rev-parse","--verify","--quiet","HEAD"],{readOnly:!0});if(baseHead.exitCode!==0||!baseHead.stdout.trim())return refuse({branch,landedOn,baseDirty,baseDir:dir},"UNBORN_BASE");return refuse({branch,landedOn,baseDirty,baseDir:dir},baseDirty?"MAIN_CHECKOUT_DIRTY":"MISSING_REF")}let parsed=Number.parseInt(aheadOut.stdout.trim(),10),base={branch,landedOn,baseDirty,baseDir:dir,...Number.isFinite(parsed)?{ahead:parsed}:{}};if(baseDirty)return refuse(base,"MAIN_CHECKOUT_DIRTY");if(base.ahead!==0)return base;let worktreePath=task.worktreePath.trim();if(worktreePath){let manager=new GitWorktreeManager(deps),dirty=!1;try{dirty=await manager.isDirty(worktreePath)}catch{dirty=!1}if(dirty){let wtExec=deps.execForPath(worktreePath),files=parseDirtyPaths((await landGit(wtExec,worktreePath,["status","--porcelain"],{readOnly:!0})).stdout);return refuse({...base,dirtyFiles:files},"EMPTY_BRANCH_DIRTY_WORKTREE")}}return refuse(base,"EMPTY_BRANCH")}function landRefusalError(task,pf){switch(pf.refusal){case"DETACHED_HEAD":return Error(`landTask: base checkout at ${pf.baseDir} is in detached-HEAD state; check out a branch first`);case"UNREADABLE_BASE":return Error(`landTask: could not read HEAD of the base checkout at ${pf.baseDir} \u2014 is it still a git repo?`);case"UNBORN_BASE":return Error(`landTask: base checkout at ${pf.baseDir} is on '${pf.landedOn}' but has no commits yet, so there is nothing to merge '${pf.branch}' into; make the first commit there (or \`git merge --ff-only ${pf.branch}\`) and land again`);case"SAME_BRANCH":return Error(`landTask: base checkout is already on '${pf.branch}' \u2014 nothing to land onto`);case"MAIN_CHECKOUT_DIRTY":return new MainCheckoutDirtyError(gitRepoOf(task),pf.baseDir);case"MISSING_REF":return new MissingRefError(pf.branch,pf.landedOn,pf.baseDir);case"EMPTY_BRANCH_DIRTY_WORKTREE":return new EmptyBranchDirtyWorktreeError(pf.branch,pf.landedOn,task.worktreePath.trim(),[...pf.dirtyFiles??[]]);case"EMPTY_BRANCH":return new EmptyBranchError(pf.branch,pf.landedOn)}}function assertOwnsBranch(task){if(workspaceCapabilities(task).ownsBranch)return;throw Error(task.kind==="main"?"landTask: a main task has no branch to land":"landTask: a directory task has no Rove-managed branch to land")}async function landTaskWithCleanup(task,opts,deps){assertOwnsBranch(task);let result=await landTask(task,{strategy:opts.strategy});return finishLanded(task,result,opts,deps)}async function finishLanded(task,result,opts,deps){let worktree=opts.removeWorktree===!1?void 0:await removeLandedWorktree(task,opts.callerCwd,deps),branchAnchor=null,branchKept,worktreeGone=!task.worktreePath.trim()||worktree?.removed===!0;if(opts.deleteBranch&&!worktreeGone)branchKept={reason:worktree?.reason??`worktree ${task.worktreePath} was kept, and still has the branch checked out`};else if(opts.deleteBranch){let outcome=await deps.worktrees.deleteBranch(gitRepoOf(task),result.branch,{force:!0,onAnchor:(record)=>{branchAnchor=record}});if(!outcome.deleted)branchKept={reason:outcome.reason}}return{...result,...worktree?{worktree}:{},...branchAnchor?{branchAnchor}:{},...branchKept?{branchKept}:{}}}async function removeLandedWorktree(task,callerCwd,deps){let worktreePath=task.worktreePath.trim();if(!worktreePath)return{removed:!1,reason:"task has no worktree on disk (never materialised)"};let wt=canonicalize(worktreePath);if(samePath(wt,canonicalize(gitRepoOf(task))))return{removed:!1,reason:"refusing to remove the base checkout"};if(deps.sharesWorktree?.(task))return{removed:!1,reason:`kept ${worktreePath}: other tasks still run in this directory`};if(callerCwd){if(pathWithin(wt,canonicalize(callerCwd))!==null)return{removed:!1,reason:`refusing to remove the caller's own worktree (${worktreePath}) \u2014 re-run from outside it`}}if(deps.tearDownSession)try{await deps.tearDownSession(task.id)}catch{}let residue;try{await deps.worktrees.remove(worktreePath,{onResidue:(r)=>{residue=r}})}catch(err){return{removed:!1,reason:errText(err)}}try{await deps.clearWorktreePath(task.id)}catch(err){return{removed:!0,reason:`worktree removed, but clearing the task's worktree path failed: ${errText(err)}`,...residue?{residue}:{}}}return{removed:!0,...residue?{residue}:{}}}function errText(err){return err instanceof Error?err.message:String(err)}async function conflictedFiles(exec,dir){return(await landGit(exec,dir,["diff","--name-only","--diff-filter=U"],{readOnly:!0})).stdout.split(`
6
+ `).map((l)=>l.trim()).filter((l)=>l.length>0)}async function landTask(task,input={},deps=defaultExecDeps){let strategy=input.strategy??"merge",preflight=await landPreflight(task,deps);if(preflight.refusal)throw landRefusalError(task,{...preflight,refusal:preflight.refusal});let{branch,landedOn}=preflight,{exec,dir}=baseRepoCtx(gitRepoOf(task),deps);if(strategy==="squash"){if((await landGit(exec,dir,["merge","--squash",branch])).exitCode!==0){let files=await conflictedFiles(exec,dir);throw await landGit(exec,dir,["merge","--abort"]).catch(()=>{}),await landGit(exec,dir,["reset","--hard","HEAD"]).catch(()=>{}),new LandConflictError(task.id,branch,files)}let commit=await landGit(exec,dir,["commit","--no-edit","-m",`Land ${branch} (squash)`]);if(commit.exitCode!==0){if((await landGit(exec,dir,["diff","--cached","--quiet"],{readOnly:!0})).exitCode===0)throw await landGit(exec,dir,["reset","--hard","HEAD"]).catch(()=>{}),Error(`landTask: '${branch}' has nothing to land onto '${landedOn}' (already merged or empty)`);throw new GitCommandFailedError(`commit (squash-landing '${branch}' onto '${landedOn}')`,commit.stderr,"the squashed merge is left STAGED in the base checkout \u2014 fix the cause and `git commit` it, or `git reset --hard HEAD` to discard")}}else{let before=(await landGit(exec,dir,["rev-parse","HEAD"],{readOnly:!0})).stdout.trim(),merge=await landGit(exec,dir,["merge","--no-ff","-m",`Land ${branch}`,branch]);if(merge.exitCode!==0){let files=await conflictedFiles(exec,dir);if(await landGit(exec,dir,["merge","--abort"]).catch(()=>{}),files.length===0)throw new GitCommandFailedError(`merge --no-ff (landing '${branch}' onto '${landedOn}')`,merge.stderr,"the merge was aborted, so the base checkout is unchanged");throw new LandConflictError(task.id,branch,files)}let after=(await landGit(exec,dir,["rev-parse","HEAD"],{readOnly:!0})).stdout.trim();if(before===after)throw Error(`landTask: '${branch}' has nothing to land onto '${landedOn}' (already merged or empty)`)}let shaOut=await landGit(exec,dir,["rev-parse","--short","HEAD"],{readOnly:!0});return{branch,strategy,landedOn,commit:shaOut.stdout.trim()}}function viewOf(task,repo){let{baseRef:_primaryBase,...rest}=task;return{...rest,repo:repo.repo,branch:repo.branch,worktreePath:repo.worktreePath,...repo.baseRef?{baseRef:repo.baseRef}:{}}}function codeOf(message){return/^([A-Z][A-Z0-9_]{2,}): /.exec(message)?.[1]}async function contentLanded(view,landedOn,branch,deps){let{exec,dir}=baseRepoCtx(view.repo,deps),merged=await landGit(exec,dir,["merge-tree","--write-tree",landedOn,branch],{readOnly:!0});if(merged.exitCode!==0)return!1;let base=await landGit(exec,dir,["rev-parse",`${landedOn}^{tree}`],{readOnly:!0}),tree=merged.stdout.split(`
7
+ `)[0]?.trim();return base.exitCode===0&&!!tree&&tree===base.stdout.trim()}async function probeOne(task,repo,deps){let view=viewOf(task,repo);if(!repo.branch.trim()){let row={name:repo.name,status:"skipped",branch:"",landedOn:"",baseDir:repo.repo,message:"never materialised \u2014 nothing to land"};return{repo,view,row}}let pf=await landPreflight(view,deps);if(pf.refusal==="MISSING_REF"&&!repo.worktreePath.trim()){let row={...pf,name:repo.name,status:"skipped",message:`branch '${pf.branch}' is gone and the worktree was already removed \u2014 landed earlier`};return{repo,view,row}}if(!pf.refusal&&pf.ahead!==0&&await contentLanded(view,pf.landedOn,pf.branch,deps)){let row={...pf,name:repo.name,status:"skipped",refusal:"EMPTY_BRANCH",message:`its changes are already on ${pf.landedOn} \u2014 landed earlier`};return{repo,view,row}}let status=!pf.refusal?"ready":pf.refusal==="EMPTY_BRANCH"?"skipped":"refused";return{repo,view,row:{...pf,name:repo.name,status}}}async function probeAll(task,only,deps){let probed=[];for(let repo of pickRepos(task,only))probed.push(await probeOne(task,repo,deps));return probed}async function landPreflightAll(task,only,deps=defaultExecDeps){let rows=(await probeAll(task,only,deps)).map((p)=>p.row),head=rows.find((r)=>r.status==="refused")??rows.find((r)=>r.status==="ready")??rows[0];if(!head)throw Error("landPreflight: task has no repos");let{name:_name,status:_status,...top}=head,nothingToLand=rows.every((r)=>r.status==="skipped");return{...top,...nothingToLand&&!top.refusal?{refusal:"EMPTY_BRANCH"}:{},repos:rows}}function pendingRow(p){let{row}=p;if(row.status==="skipped"){let reason=row.refusal==="EMPTY_BRANCH"&&row.ahead===0?`nothing ahead of ${row.landedOn} \u2014 already landed or untouched`:row.message;return{name:row.name,status:"skipped",branch:row.branch,landedOn:row.landedOn,...reason?{error:reason}:{}}}return{name:row.name,status:"pending",branch:row.branch,landedOn:row.landedOn,...row.ahead===void 0?{}:{ahead:row.ahead}}}function summarise(strategy,results,stopped){let lead=results.find((r)=>r.status==="landed")??results.find((r)=>r.status==="refused")??results[0];return{branch:lead?.branch??"",strategy,landedOn:lead?.landedOn??"",commit:lead?.commit??"",...lead?.worktree?{worktree:lead.worktree}:{},...lead?.branchAnchor?{branchAnchor:lead.branchAnchor}:{},...lead?.branchKept?{branchKept:lead.branchKept}:{},repos:results,...stopped?{stopped}:{}}}async function landTaskAll(task,opts,deps,only){let strategy=opts.strategy??"merge",probed=await probeAll(task,only,defaultExecDeps),results=probed.map(pendingRow),refused=probed.findIndex((p)=>p.row.status==="refused");if(refused>=0){let{row}=probed[refused],code=row.refusal??"REFUSED",message=row.message??code;return results[refused]={...results[refused],name:row.name,status:"refused",code,error:message},summarise(strategy,results,{repo:row.name,code,message})}if(!probed.some((p)=>p.row.status==="ready")){let first=probed.find((p)=>p.row.refusal);if(first?.row.refusal)throw landRefusalError(first.view,{...first.row,refusal:first.row.refusal});throw Error("landTask: task has no branch to land (never materialised)")}let stopped;for(let[i,p]of probed.entries()){if(p.row.status!=="ready"||stopped)continue;try{let merged=await landTask(p.view,{strategy});results[i]={name:p.repo.name,status:"landed",branch:merged.branch,landedOn:merged.landedOn,commit:merged.commit,...p.row.ahead===void 0?{}:{ahead:p.row.ahead}}}catch(err){let message=errorMessage(err),conflict=err instanceof LandConflictError,code=conflict?"LAND_CONFLICT":codeOf(message)??"LAND_FAILED";results[i]={name:p.repo.name,status:conflict?"conflict":"failed",branch:p.row.branch,landedOn:p.row.landedOn,code,error:message},stopped={repo:p.repo.name,code,message}}}if(stopped)return summarise(strategy,results,stopped);let selected=new Set(probed.map((p)=>p.repo.name)),othersOpen=only!==void 0&&pickRepos(task).some((r)=>!selected.has(r.name)&&r.worktreePath.trim());for(let[i,p]of probed.entries()){let row=results[i];if(!p.repo.branch.trim()||row.status!=="landed"&&row.status!=="skipped")continue;if(row.status==="skipped"&&p.row.refusal==="MISSING_REF")continue;if(p.repo.primary&&othersOpen){results[i]={...row,worktree:{removed:!1,reason:"kept until every repo has landed: other repos of this task still have worktrees"},...opts.deleteBranch?{branchKept:{reason:"kept until every repo has landed: other repos of this task still have worktrees"}}:{}};continue}let repoDeps={worktrees:deps.worktrees,tearDownSession:p.repo.primary?deps.tearDownSession:void 0,clearWorktreePath:(id)=>deps.clearRepoWorktreePath(id,p.repo.name),sharesWorktree:()=>deps.sharesRepoWorktree(task,p.repo)},done=await finishLanded(p.view,{branch:p.row.branch,strategy,landedOn:p.row.landedOn,commit:row.commit??""},opts,repoDeps);results[i]={...row,...done.worktree&&p.view.worktreePath?{worktree:done.worktree}:{},...done.branchAnchor?{branchAnchor:done.branchAnchor}:{},...done.branchKept?{branchKept:done.branchKept}:{}}}return summarise(strategy,results,void 0)}async function landRouted(task,opts,deps){pickRepos(task,opts.repo);let sharing=(landed,path3)=>deps.list().some((t)=>t.id!==landed.id&&(t.worktreePath&&canonPath(t.worktreePath)===canonPath(path3)||t.sharesWorkspaceOf===landed.id));if(task.extraRepos?.length)return landTaskAll(task,opts,{worktrees:deps.worktrees,tearDownSession:deps.tearDownSession,clearRepoWorktreePath:(tid,name)=>deps.clearRepoWorktreePath(tid,name),sharesRepoWorktree:(landed,repo)=>sharing(landed,repo.worktreePath)},opts.repo);return landTaskWithCleanup(task,opts,{worktrees:deps.worktrees,clearWorktreePath:(tid)=>deps.clearWorktreePath(tid),tearDownSession:deps.tearDownSession,sharesWorktree:(landed)=>sharing(landed,landed.worktreePath)})}class MainTaskCoordinator{store;forgetWorktree;locks=new Map;constructor(store,forgetWorktree){this.store=store;this.forgetWorktree=forgetWorktree}async ensure(repo){let task=await this.ensureIfEligible(repo,"explicit");if(task)return task;let{repo:normalizedRepo}=normalizeMainRepo(repo),rejected=projectRejection(normalizedRepo,isGitRepo,"explicit")??"notGitRepo";throw Error(`cannot add ${normalizedRepo} as a project: ${rejectionReason(rejected)}`)}async ensureIfEligible(repo,intent="derived"){let{repo:normalizedRepo,key}=normalizeMainRepo(repo),onThisRoot=(task)=>normalizeMainRepo(task.repo).key===key,existing=this.store.list().find((task)=>task.kind==="main"&&onThisRoot(task));if(existing)return existing;if(projectRejection(normalizedRepo,isGitRepo,intent))return null;let inflight=this.locks.get(key);if(inflight)return inflight;let promise=(async()=>{addSavedRepo(normalizedRepo,{intent:"explicit"});let adoptable=this.store.list().find((task)=>task.kind==="dir"&&task.scratch!==!0&&task.noProject!==!0&&!task.sharesWorkspaceOf&&onThisRoot(task));if(adoptable)return await this.store.update(adoptable.id,this.mainShape(normalizedRepo));return await this.store.create({...this.mainShape(normalizedRepo),status:"backlog",vendor:resolvePreferredVendor(normalizedRepo)})})();this.locks.set(key,promise);try{return await promise}finally{this.locks.delete(key)}}mainShape(normalizedRepo){return{kind:"main",title:titleFromRepo(normalizedRepo),repo:normalizedRepo,branch:"",worktreePath:repoWorkingDir(normalizedRepo)}}async forget(repo){if(!repo)throw Error("forgetProject: repo is required");let key=normalizeMainRepo(repo).key;for(let saved of getSavedRepos())if(normalizeMainRepo(saved).key===key)removeSavedRepo(saved);for(let task of this.store.list()){if(task.kind!=="main")continue;if(normalizeMainRepo(task.repo).key!==key)continue;await this.store.remove(task.id),this.forgetWorktree(task.id);for(let dependent of this.store.list()){if(dependent.parentTaskId!==task.id&&dependent.sharesWorkspaceOf!==task.id)continue;await this.store.update(dependent.id,{...dependent.parentTaskId===task.id?{parentTaskId:void 0}:{},...dependent.sharesWorkspaceOf===task.id?{sharesWorkspaceOf:void 0}:{}})}}}}import{isAbsolute,relative,resolve,sep}from"path";import{lstatSync,readdirSync,realpathSync}from"fs";import{join}from"path";function scanLinkedRepos(workspaceDir){let workspace,names;try{workspace=realpathSync(workspaceDir),names=readdirSync(workspaceDir)}catch{return[]}let out=[];for(let name of names.sort()){let entry=join(workspaceDir,name);try{if(!lstatSync(entry).isSymbolicLink())continue;let target=realpathSync(entry);if(pathWithin(workspace,target)!==null)continue;if(isGitRepo(target)&&samePath(resolveRepoRoot(target),target))out.push({name,path:target})}catch{}}return out}function sharedChildRow(parent,workspace,fields){let command=fields.command?.trim();return{repo:parent.repo,branch:parent.kind==="task"?parent.branch:"",worktreePath:workspace,status:"backlog",kind:parent.kind==="task"?"task":"dir",title:fields.title,vendor:fields.vendor,...command?{command}:{},...fields.modelEffort?{modelEffort:fields.modelEffort}:{},...fields.model?{model:fields.model}:{},...fields.tier?{tier:fields.tier}:{},...fields.groupId?{groupId:fields.groupId}:{},...fields.dispatcher?{dispatcher:fields.dispatcher}:{},...parent.kind==="task"&&parent.baseRef?{baseRef:parent.baseRef}:{},...parent.cwdSubpath?{cwdSubpath:parent.cwdSubpath}:{},...fields.prompt?{prompt:fields.prompt}:{},parentTaskId:toTaskId(fields.parentTaskId),sharesWorkspaceOf:toTaskId(parent.sharesWorkspaceOf??parent.id)}}async function spawnChildTaskRow(deps,input){let found=deps.store.get(input.parentTaskId);if(!found)throw new TaskNotFoundError(input.parentTaskId);if(found.deletion)throw new TaskDeletingError(String(found.id));if(isRemoteRepoKey(found.repo))throw new RemoteParentUnsupportedError(String(found.id));if(input.isolateIn&&!input.worktree)throw Error("INVALID_SPAWN: isolateIn needs a worktree child");let workspace=found.kind==="task"?await deps.ensureWorktree(found.id):found.worktreePath,parent=deps.store.get(found.id)??found,cwd=taskCwd({worktreePath:workspace,cwdSubpath:parent.cwdSubpath}),links=scanLinkedRepos(cwd),inherit=!input.vendor&&!input.command,common={parentTaskId:lineageRootId(deps.store.list(),parent.id),title:sanitizeTaskTitle(input.title??PLACEHOLDER_TASK_TITLE)||PLACEHOLDER_TASK_TITLE,vendor:input.vendor??parent.vendor,command:input.command??(inherit?parent.command:void 0),dispatcher:input.dispatcher,prompt:input.prompt,modelEffort:input.modelEffort,model:input.model,tier:input.tier,groupId:input.groupId};if(!input.worktree)return{task:await deps.store.create(sharedChildRow(parent,workspace,common)),parentDirty:await isDirty2(deps,cwd),sharedLinks:links.map((l)=>l.name)};let source=input.isolateIn?requireLink(links,input.isolateIn,cwd):void 0,sourceDir=source?.path??cwd;if(!source&&!isGitRepo(sourceDir))throw new NotAGitRepoError(sourceDir);let top=source?sourceDir:gitOut(sourceDir,["rev-parse","--show-toplevel"]),sub=relative(canonPath(top),canonPath(sourceDir)),cwdSubpath=sub&&!sub.split(sep).includes("..")&&!isAbsolute(sub)?sub:void 0,baseRef=input.baseRef?.trim()||gitOut(sourceDir,["rev-parse","HEAD"]),repo=source||parent.kind==="dir"?resolveMainRepoRoot(sourceDir):gitRepoOf(parent),parentDirty=await isDirty2(deps,sourceDir),child=await deps.createTask({repo,title:common.title,baseRef,vendor:common.vendor,command:common.command,dispatcher:common.dispatcher,parentTaskId:toTaskId(common.parentTaskId),cwdSubpath,projectIntent:"derived",modelEffort:common.modelEffort,model:common.model,tier:common.tier,groupId:common.groupId,branch:input.branch,worktreeName:input.worktreeName});if(!source&&parent.gitRepo)await deps.store.update(child.id,{repo:parent.repo,gitRepo:parent.gitRepo});if(input.lazy){if(input.prompt)await deps.store.update(child.id,{prompt:input.prompt});return{task:deps.store.get(child.id)??child,parentDirty,sharedLinks:source?[]:links.map((l)=>l.name)}}try{await deps.ensureWorktree(child.id)}catch(err){let partial=deps.store.get(child.id);if(partial?.worktreePath)await deps.worktrees.remove(partial.worktreePath,{force:!0,deleteBranch:!0,repo:gitRepoOf(partial),branch:partial.branch}).catch((cleanup)=>console.error(`[rove] spawn rollback failed for ${partial.worktreePath}:`,cleanup));throw await deps.store.remove(child.id),err}if(input.prompt)await deps.store.update(child.id,{prompt:input.prompt});return{task:deps.store.get(child.id)??child,parentDirty,sharedLinks:source?[]:links.map((l)=>l.name)}}function requireLink(links,isolateIn,cwd){let name=isAbsolute(isolateIn)||isolateIn.includes("/")?relative(cwd,resolve(cwd,isolateIn)):isolateIn,link=links.find((l)=>l.name===name);if(!link)throw new NotALinkedRepoError(isolateIn,links.map((l)=>l.name));return link}function gitOut(cwd,args){return git(args,{cwd,readOnly:!0}).stdout.trim()}async function isDirty2(deps,dir){try{return(await deps.worktrees.dirtyPaths(dir)).length>0}catch{return!1}}async function createMigratedChildRow(deps,input){let parent=deps.store.get(input.parentTaskId);if(!parent)throw new TaskNotFoundError(input.parentTaskId);if(parent.deletion)throw new TaskDeletingError(String(parent.id));let inherit=!input.vendor&&!input.command,row=sharedChildRow(parent,parent.worktreePath,{parentTaskId:lineageRootId(deps.store.list(),parent.id),title:sanitizeTaskTitle(input.title)||PLACEHOLDER_TASK_TITLE,vendor:input.vendor??parent.vendor,command:input.command??(inherit?parent.command:void 0)});return await deps.store.create({...row,...input.ptyAlias?{ptyAlias:input.ptyAlias}:{}})}async function setPtyAliasRow(deps,id,alias){let task=deps.store.get(id);if(!task)throw new TaskNotFoundError(id);if(alias===null&&!task.ptyAlias)return;await deps.store.update(id,{ptyAlias:alias??void 0})}function refuse(task,targetRepo,tasks){if(task.kind==="main")throw new MoveRefusedError("TASK_NOT_MOVABLE",`${task.id} is a project's main task \u2014 it is the project`);if(task.routine)throw new MoveRefusedError("TASK_NOT_MOVABLE",`${task.id} is a routine session; the sidebar keeps those in the routine fold`);if(task.extraRepos?.length)throw new MoveRefusedError("TASK_NOT_MOVABLE",`${task.id} spans several repos; it has no single project to move to`);if(isRemoteRepoKey(task.repo)||isRemoteRepoKey(targetRepo))throw new MoveRefusedError("REMOTE_MOVE_UNSUPPORTED",`task ${task.id} or the target lives on a remote project \u2014 move it from that machine's rove`);if(task.parentTaskId)throw new MoveRefusedError("CHILD_NOT_MOVABLE",`${task.id} is a child of ${task.parentTaskId} \u2014 move the parent (or \`update --parent none\` first); its children follow it`);if(task.scratch!==!0&&!task.noProject&&normalizeMainRepo(task.repo).key===normalizeMainRepo(targetRepo).key)throw new MoveRefusedError("SAME_PROJECT",`task ${task.id} already belongs to ${task.repo}`);for(let id of subtreeIds(tasks,String(task.id)))if(tasks.find((t)=>String(t.id)===id)?.deletion)throw new TaskDeletingError(id)}async function moveTaskToProjectRow(deps,id,repo){let task=deps.store.get(id);if(!task)throw new TaskNotFoundError(String(id));let tasks=deps.store.list();refuse(task,repo,tasks);let target=(await deps.ensureMainTask(repo)).repo,moved=subtreeIds(tasks,String(task.id));for(let rowId of moved){let row=tasks.find((t)=>String(t.id)===rowId);if(!row)continue;if(row.kind==="task"&&row.worktreePath){let origin=gitRepoOf(row),home=normalizeMainRepo(origin).key===normalizeMainRepo(target).key;await deps.store.update(row.id,{repo:target,gitRepo:home?void 0:origin});continue}await deps.store.update(row.id,{repo:target,...row.kind==="task"?{gitRepo:void 0}:{},...row.scratch?{scratch:!1,title:row.title||titleFromRepo(target)}:{},...row.noProject?{noProject:void 0}:{}})}return{repo:target,moved}}function promotableDirTasks(deps){let claimed=new Set(deps.tasks.filter((task)=>task.kind==="main").map((task)=>pathIdentity(task.repo))),seen=new Set,out=[];for(let task of deps.tasks){if(task.kind!=="dir"||task.scratch===!0||task.noProject===!0||task.sharesWorkspaceOf)continue;let path3=task.repo,key=pathIdentity(path3);if(!path3||claimed.has(key)||seen.has(key))continue;if(!deps.isRepoRoot(path3))continue;seen.add(key),out.push(task)}return out}function isLeaving(task){return task.deletion?.phase==="queued"||task.deletion?.phase==="running"}class TaskDeletionCoordinator{store;worktrees;workspaces;forgetTask;onSalvage;onResidue;onBranchKept;pathLocks=new Map;constructor(store,worktrees,workspaces,forgetTask,onSalvage,onResidue,onBranchKept){this.store=store;this.worktrees=worktrees;this.workspaces=workspaces;this.forgetTask=forgetTask;this.onSalvage=onSalvage;this.onResidue=onResidue;this.onBranchKept=onBranchKept}targets(id,opts){return opts?.cascade?subtreeIds(this.store.list(),String(id)):[String(id)]}async prepare(id,opts){let task=this.store.get(id);if(!task)return!1;if(task.kind==="main")throw new CannotDeleteMainTaskError;if(isLeaving(task))return!0;let keys=this.targets(task.id,opts).flatMap((targetId)=>{let target=this.store.get(targetId);return target&&workspaceCapabilities(target).ownsDirectory?taskWorktreePaths(target).map(canonPath):[]});return this.withPathLocks([...new Set(keys)].sort(),()=>this.prepareLocked(task.id,opts))}async prepareLocked(id,opts){let task=this.store.get(id);if(!task||isLeaving(task))return task!==void 0;let force=opts?.force===!0,doomed=this.targets(task.id,opts).map((targetId)=>this.store.get(targetId)).filter((t)=>t!==void 0&&t.kind!=="main"&&!isLeaving(t)),leaving=new Set(doomed.map((t)=>String(t.id))),survivors=this.store.list().filter((t)=>!leaving.has(String(t.id))&&!isLeaving(t)),gated=new Set;for(let target of doomed){if(force||!workspaceCapabilities(target).ownsDirectory||!target.worktreePath)continue;let primaryKey=canonPath(target.worktreePath);if(survivors.some((s)=>s.worktreePath&&canonPath(s.worktreePath)===primaryKey))continue;let repos=taskRepos(target).filter((r)=>r.worktreePath),found=[],backend=this.workspaces.get(workspaceBackendId(target));for(let r of repos){let key=canonPath(r.worktreePath);if(gated.has(key))continue;gated.add(key);let own=r.worktreePath===target.worktreePath?backend.dirtyPaths?.bind(backend):void 0,finding=own?await own(r.worktreePath).then((dirty)=>dirty.length?{ignored:[],dirty}:null):await this.probeDirty(r.worktreePath);if(finding)found.push({repo:r.name,...finding})}let[head,...rest]=found;if(head)throw new DirtyWorktreeError(target.id,head.ignored,head.dirty,repos.length>1?head.repo:void 0,rest)}for(let target of doomed)await this.store.update(target.id,{deletion:{phase:"queued",force,deleteBranch:opts?.deleteBranch===!0||target.deleteBranchPending===!0,requestedAt:new Date().toISOString()}});if(await this.transferOwnership(leaving),!opts?.cascade)await this.promoteChildren(task);return!0}async begin(id){let task=this.store.get(id);if(!task||!task.deletion||task.deletion.phase==="error")return!1;if(task.deletion.phase!=="running")await this.store.update(task.id,{deletion:{...task.deletion,phase:"running"}});return!0}async finish(id){let task=this.store.get(id);if(!task?.deletion||task.deletion.phase!=="running")return;let deletion=task.deletion,backend=this.workspaces.get(workspaceBackendId(task)),ownsDirectory=Boolean(task.worktreePath)&&backend.capabilities.ownsDirectory,extras=backend.capabilities.ownsDirectory?(task.extraRepos??[]).filter((x)=>x.worktreePath):[],key=ownsDirectory?canonPath(task.worktreePath):null,keys=[...key?[key]:[],...extras.map((x)=>canonPath(x.worktreePath))];await this.withPathLocks([...new Set(keys)].sort(),async()=>{let shared=key!==null&&this.store.list().some((t)=>t.id!==task.id&&t.worktreePath&&canonPath(t.worktreePath)===key),removing;try{if(ownsDirectory&&!shared){if(extras.length>0)removing=taskRepos(task)[0]?.name;await this.removeWorktree(backend,task,deletion,{path:task.worktreePath,repo:gitRepoOf(task),branch:task.branch})}else if(ownsDirectory&&deletion.deleteBranch)await this.handBranchDeleteOn(task,key);for(let extra of extras){removing=extra.name,await this.removeWorktree(this.workspaces.get("git-worktree"),task,deletion,{path:extra.worktreePath,repo:extra.repo,branch:extra.branch});let current=this.store.get(task.id)?.extraRepos??[];await this.store.update(task.id,{extraRepos:current.map((x)=>x.name===extra.name?{...x,worktreePath:""}:x)})}}catch(cause){let failure=new WorktreeRemoveFailedError(task.id,removing?Error(`${removing}: ${errorMessage(cause)}`,{cause}):cause);throw await this.store.update(task.id,{deletion:{...deletion,phase:"error",error:errorMessage(failure)}}),failure}if(task.kind==="task")await this.dropCheckpoints(task);await this.store.remove(task.id),this.forgetTask(task.id)})}async deleteNow(id,opts){let targets=this.targets(id,opts);if(!await this.prepare(id,opts))return;let failure;for(let target of targets)try{if(await this.begin(target))await this.finish(target)}catch(err){failure??=err}if(failure)throw failure}async dropCheckpoints(task){try{await this.worktrees.deleteTaskCheckpoints(gitRepoOf(task),String(task.id))}catch{}}async probeDirty(path3){let dirty=[],ignored=[],probed=!0;try{dirty=await this.worktrees.dirtyPaths(path3)}catch{probed=!1}if(probed&&dirty.length===0)ignored=await this.worktrees.ignoredWork(path3);return dirty.length>0||ignored==="unknown"||ignored.length>0?{ignored,dirty}:null}async removeWorktree(backend,task,deletion,target){await backend.dispose(target,{force:deletion.force,deleteBranch:deletion.deleteBranch===!0,onSalvage:(record)=>{if(record)this.onSalvage?.(task.id,record,target.repo)},onResidue:(residue)=>this.onResidue?.(task.id,residue),onBranchKept:(kept)=>this.onBranchKept?.(task.id,kept)})}async handBranchDeleteOn(leaving,key){let holder=this.store.list().find((t)=>t.id!==leaving.id&&t.worktreePath&&canonPath(t.worktreePath)===key);if(holder&&isLeaving(holder))await this.store.update(holder.id,{deletion:{...holder.deletion,deleteBranch:!0}});else if(holder)await this.store.update(holder.id,{deleteBranchPending:!0});this.onBranchKept?.(leaving.id,{branch:leaving.branch,reason:"another task still uses this branch"})}async transferOwnership(leaving){for(let ownerId of leaving){let remaining=this.store.list().filter((t)=>t.sharesWorkspaceOf===ownerId&&!leaving.has(String(t.id))&&!isLeaving(t)),[heir,...rest]=remaining;if(!heir)continue;let extraRepos=this.store.get(ownerId)?.extraRepos;if(await this.store.update(heir.id,{sharesWorkspaceOf:void 0,...extraRepos?{extraRepos}:{}}),extraRepos)await this.store.update(ownerId,{extraRepos:void 0});for(let sharer of rest)await this.store.update(sharer.id,{sharesWorkspaceOf:heir.id});await this.moveCheckpoints(ownerId,String(heir.id))}}async moveCheckpoints(fromId,toId){let owner=this.store.get(fromId);if(owner?.kind!=="task")return;try{await this.worktrees.moveTaskCheckpoints(gitRepoOf(owner),fromId,toId)}catch{}}async promoteChildren(deleted){let ungrouped=deleted.kind==="dir"&&(deleted.scratch===!0||deleted.noProject===!0);for(let child of this.store.list().filter((t)=>t.parentTaskId===deleted.id)){let keep=ungrouped&&child.kind==="dir"?{noProject:!0}:{};await this.store.update(child.id,{parentTaskId:deleted.parentTaskId,...keep})}}async withPathLocks(keys,fn){let[first,...rest]=keys;if(first===void 0)return fn();return this.withPathLock(first,()=>this.withPathLocks(rest,fn))}async withPathLock(key,fn){if(key===null)return fn();let previous=this.pathLocks.get(key)??Promise.resolve(),release,turn=new Promise((resolve2)=>{release=resolve2}),tail=previous.then(()=>turn);this.pathLocks.set(key,tail),await previous;try{return await fn()}finally{if(release(),this.pathLocks.get(key)===tail)this.pathLocks.delete(key)}}}class TaskEditor{store;worktrees;constructor(store,worktrees){this.store=store,this.worktrees=worktrees}requireTask(id){let task=this.store.get(id);if(!task)throw new TaskNotFoundError(String(id));return task}async setTitle(id,title,opts){let trimmed=sanitizeTaskTitle(title);if(!trimmed)throw Error("setTitle: title is required (empty or whitespace-only rejected)");let task=this.requireTask(id),unscratch=task.scratch===!0&&opts?.keepScratch!==!0;if(task.title===trimmed&&!unscratch)return;await this.store.update(task.id,{title:trimmed,...unscratch?{scratch:!1}:{}}),await this.followBranchToTitle(task,trimmed)}async followBranchToTitle(taskBefore,newTitle){if(!workspaceCapabilities(taskBefore).ownsBranch||!taskBefore.worktreePath)return;if(!isPlaceholderDerivedBranch(taskBefore.branch,taskBefore.id))return;try{let names=await this.worktrees.listBranchNames(taskBefore.repo),base=deriveConventionBranch(newTitle,inferBranchStyle(names),taskBefore.id);if(base===taskBefore.branch)return;if(await this.worktrees.branchHasUpstream(taskBefore.worktreePath,taskBefore.branch))return;let taken=new Set(names.filter((n)=>n!==taskBefore.branch)),nextBranch=uniqueBranchName(base,taken,taskBefore.id);await this.setBranch(taskBefore.id,nextBranch)}catch(err){console.error(`[rove] follow-branch-to-title failed for ${taskBefore.id}:`,err)}}async setBranch(id,branch){let trimmed=branch.trim();if(!trimmed)throw Error("setBranch: branch is required (empty or whitespace-only rejected)");let task=this.requireTask(id);if(!workspaceCapabilities(task).ownsBranch)throw Error(task.kind==="main"?"setBranch: a main task tracks the repo's own branch; rename it with git directly":"setBranch: a directory task tracks its own checkout; rename branches with git directly");if(task.branch===trimmed)return;let extras=(task.extraRepos??[]).filter((x)=>x.worktreePath&&x.branch===task.branch),renamed=[];try{if(task.worktreePath)await this.worktrees.renameBranch(task.worktreePath,task.branch,trimmed),renamed.push({path:task.worktreePath,from:task.branch});for(let extra of extras)await this.worktrees.renameBranch(extra.worktreePath,extra.branch,trimmed),renamed.push({path:extra.worktreePath,from:extra.branch})}catch(err){for(let done of renamed.reverse())await this.worktrees.renameBranch(done.path,trimmed,done.from).catch(()=>{});throw err}if(await this.store.update(task.id,{branch:trimmed,...task.extraRepos?{extraRepos:task.extraRepos.map((x)=>extras.includes(x)?{...x,branch:trimmed}:x)}:{}}),!task.worktreePath)return;for(let holder of this.store.list()){if(holder.id===task.id||holder.kind!=="task"||holder.branch!==task.branch)continue;if(holder.worktreePath&&samePath(holder.worktreePath,task.worktreePath))await this.store.update(holder.id,{branch:trimmed})}}async observeLanguage(id,text){let observed=detectLanguage(text);if(!observed)return;let task=this.requireTask(id);if(task.observedLanguage===observed)return;await this.store.update(task.id,{observedLanguage:observed})}async setVendor(id,vendor,effort,model){let task=this.requireTask(id),switching=task.vendor!==vendor,effortArg=effort??(switching?"":void 0),modelArg=model??(switching?"":void 0),nextEffort=effortArg?.trim()?effortArg.trim():void 0,effortChanges=effortArg!==void 0&&task.modelEffort!==nextEffort,nextModel=modelArg?.trim()?modelArg.trim():void 0,modelChanges=modelArg!==void 0&&task.model!==nextModel;if(!switching&&!effortChanges&&!modelChanges)return;await this.store.update(task.id,{vendor,...effortArg!==void 0?{modelEffort:nextEffort}:{},...modelArg!==void 0?{model:nextModel}:{}})}async setCommand(id,command,vendor){let trimmed=command.trim();if(!trimmed)throw Error("setCommand: command is required (empty or whitespace-only rejected)");let task=this.requireTask(id),switching=vendor!==void 0&&task.vendor!==vendor;if(task.command===trimmed&&!switching)return;await this.store.update(task.id,{command:trimmed,...switching?{vendor,model:void 0,modelEffort:void 0}:{}})}async setPinned(id,pinned){let task=this.requireTask(id);if(task.kind==="main")return;let next=pinned??!task.pinned;if((task.pinned??!1)===next)return;await this.store.update(task.id,{pinned:next})}async setKept(id,kept){let task=this.requireTask(id);if((task.kept??!1)===kept)return;await this.store.update(task.id,{kept:kept?!0:void 0})}async updateNotes(id,edit){let task=this.requireTask(id),todos=edit.ops&&edit.ops.length>0?applyTodoOps(task.todos??[],edit.ops):task.todos??[],notes=edit.notes===void 0?task.notes:checkNotes(edit.notes);if(todos!==(task.todos??[])||notes!==task.notes)await this.store.update(task.id,{notes:notes&&notes.length>0?notes:void 0,todos:todos.length>0?todos:void 0});return{...notes?{notes}:{},todos}}async setParent(id,parentId,replyTab){let task=this.requireTask(id);if(task.deletion)throw new TaskDeletingError(String(task.id));if(task.kind==="main")throw new ReparentRefusedError("TASK_NOT_MOVABLE",`${task.id} is a project's main task \u2014 it is the project`);if(task.routine)throw new ReparentRefusedError("TASK_NOT_MOVABLE",`${task.id} is a routine session; the sidebar keeps those in the routine fold`);if(isRemoteRepoKey(task.repo))throw new ReparentRefusedError(REMOTE_PARENT_UNSUPPORTED_CODE,`task ${task.id} lives on a remote project \u2014 re-parent it from that machine's rove`);let tasks=this.store.list();if(parentId===null){let oldParent=task.parentTaskId,dispatchedFromOldTree=oldParent!==void 0&&task.dispatcher!==void 0&&subtreeIds(tasks,oldParent).includes(task.dispatcher.taskId);return await this.store.update(task.id,{parentTaskId:void 0,...dispatchedFromOldTree?{dispatcher:void 0}:{}}),{parentTaskId:null,dispatcher:(dispatchedFromOldTree?void 0:task.dispatcher)??null,moved:[]}}if(parentId===task.id)throw new ReparentRefusedError("PARENT_IS_SELF",`${task.id} cannot be its own parent`);let parent=this.store.get(parentId);if(!parent)throw new ReparentRefusedError("PARENT_NOT_FOUND",`no task ${parentId} on this machine \u2014 a parent must live beside its child`);if(parent.deletion)throw new TaskDeletingError(String(parent.id));if(isRemoteRepoKey(parent.repo))throw new ReparentRefusedError(REMOTE_PARENT_UNSUPPORTED_CODE,`task ${parent.id} lives on a remote project \u2014 re-parent under it from that machine's rove`);if(subtreeIds(tasks,task.id).includes(parent.id))throw new ReparentRefusedError("PARENT_CYCLE",`${parent.id} is already a descendant of ${task.id}`);let root=lineageRootId(tasks,parent.id),dispatcher=task.dispatcher?.taskId===parent.id&&replyTab===void 0?task.dispatcher:{taskId:parent.id,tabId:replyTab??"tab-1"};await this.store.update(task.id,{parentTaskId:toTaskId(root),dispatcher});let moved=childrenOf(tasks,task.id).map((child)=>child.id);for(let child of moved)await this.store.update(child,{parentTaskId:toTaskId(root)});return{parentTaskId:root,dispatcher,moved}}async setArchived(id,archived){let task=this.requireTask(id);if(task.kind==="main")throw Error("a project's main checkout cannot be archived");if((task.archived??!1)===archived)return;await this.store.update(task.id,{archived:archived||void 0})}async setRequireReport(id,required){let task=this.requireTask(id);if((task.requireReport??!1)===required)return;await this.store.update(task.id,{requireReport:required||void 0})}async moveTask(id,delta){let task=this.requireTask(id);await this.store.move(task.id,delta,this.movePartition(task))}async moveTaskToTop(id){let task=this.requireTask(id);await this.store.moveToFront(task.id,this.movePartition(task))}movePartition(task){let isMain=task.kind==="main";return this.store.list().filter((t)=>isMain?(t.kind??"task")==="main":(t.kind??"task")!=="main"&&samePath(t.repo,task.repo)&&(t.pinned??!1)===(task.pinned??!1)).map((t)=>String(t.id))}async setStatus(id,status){let task=this.requireTask(id);if(task.status===status)return;if(task.status==="done"&&status==="error"||task.status==="error"&&status==="done")throw new IllegalTransitionError(task.status,status,task.id);await this.store.update(task.id,{status})}async setWorkerReport(id,report){let task=this.requireTask(id),next={...task.report,...report,at:new Date().toISOString()};await this.store.update(task.id,{report:next})}async setPRStatus(id,prStatus,repoName){let task=this.requireTask(id),extra=repoName?task.extraRepos?.find((x)=>x.name===repoName):void 0;if(repoName&&!extra)return;let current=extra?extra.prStatus:task.prStatus;if((current?.lastError??null)===(prStatus?.lastError??null)&&samePrStatus(current,prStatus??void 0))return;if(!extra){await this.store.update(task.id,{prStatus:prStatus??void 0});return}let latest=this.store.get(task.id)?.extraRepos??[];await this.store.update(task.id,{extraRepos:latest.map((x)=>x.name===repoName?{...x,prStatus:prStatus??void 0}:x)})}async addSessionPr(id,url){let task=this.requireTask(id),current=task.sessionPrs??[];if(current.some((p)=>p.url.toLowerCase()===url.toLowerCase()))return;let next=[...current,{url,addedAt:new Date().toISOString()}].slice(-SESSION_PR_CAP);await this.store.update(task.id,{sessionPrs:next})}async setSessionPrStatus(id,url,status){let task=this.store.get(id),entry=task?.sessionPrs?.find((p)=>p.url===url);if(!task||!entry)return;if((entry.status?.lastError??null)===(status.lastError??null)&&samePrStatus(entry.status,status))return;await this.store.update(task.id,{sessionPrs:(task.sessionPrs??[]).map((p)=>p.url===url?{...p,status}:p)})}async setQuotaResume(id,state){let task=this.requireTask(id);if((task.quotaResume?.resumeAt??null)===(state?.resumeAt??null))return;await this.store.update(task.id,{quotaResume:state??void 0})}async setScheduledStart(id,state){let task=this.requireTask(id);if(!task.scheduledStart&&!state)return;await this.store.update(task.id,{scheduledStart:state??void 0})}async setAutoFixCi(id,state){let task=this.requireTask(id),was=task.autoFixCi;if(was?.attempts===state?.attempts&&was?.lastSha===state?.lastSha&&was?.stopped===state?.stopped){if(Boolean(was)===Boolean(state))return}await this.store.update(task.id,{autoFixCi:state??void 0})}async setLinkedWorkItem(id,item){let task=this.requireTask(id);if((task.linkedWorkItem?.url??null)===(item?.url??null))return;await this.store.update(task.id,{linkedWorkItem:item??void 0})}async setPrompt(id,prompt){if(prompt.trim().length===0)throw Error("setPrompt: prompt is required (empty or whitespace-only rejected)");let task=this.requireTask(id);if(task.prompt===prompt)return;await this.store.update(task.id,{prompt})}}import{basename}from"path";var ANIMAL_NAMES=["aardvark","addax","adder","agouti","albatross","alpaca","anaconda","anchovy","anglerfish","anole","anteater","antelope","ape","argali","armadillo","avocet","axolotl","baboon","badger","banteng","barracuda","basilisk","bass","bat","beagle","bear","beaver","bee","beetle","beluga","betta","bison","blackbird","blenny","bluebird","bluegill","boar","bobcat","bonito","bovid","buffalo","bullfrog","bunting","bushbuck","butterfly","buzzard","caiman","camel","canary","capybara","caracal","cardinal","caribou","carp","cassowary","caterpillar","catfish","chameleon","chamois","char","cheetah","chickadee","chimp","chinchilla","chipmunk","chough","cicada","civet","clam","clownfish","cobra","cockatoo","condor","coot","copperhead","coral","cormorant","cougar","coyote","crab","crane","crawfish","crayfish","cricket","crocodile","crow","cuckoo","curlew","cuttlefish","deer","dikdik","dingo","dolphin","donkey","dormouse","dotterel","dove","dragonfly","dromedary","duck","dugong","dunlin","eagle","echidna","eel","egret","eider","eland","elephant","elk","emu","ermine","falcon","fawn","ferret","finch","firefly","flamingo","flounder","fox","frog","gadwall","gannet","gaur","gazelle","gecko","gemsbok","gerbil","gharial","gibbon","gnu","goat","goby","godwit","goldfish","goose","gopher","goral","gorilla","goshawk","grackle","grasshopper","grayling","grebe","grouse","guanaco","guillemot","guineafowl","guppy","halibut","hammerhead","hamster","hare","harrier","hartebeest","hawk","hedgehog","heron","herring","hippo","hornbill","hornet","horse","hummingbird","hyena","ibex","ibis","iguana","impala","indri","jacana","jackal","jaguar","jay","jellyfish","jerboa","junco","kakapo","kangaroo","kestrel","killdeer","kingfisher","kinglet","kite","kiwi","koala","kookaburra","krait","krill","kudu","ladybug","lamprey","langur","lapwing","lark","lemming","lemur","leopard","limpet","lion","lionfish","lizard","llama","lobster","locust","loon","lorikeet","loris","lynx","macaque","macaw","mackerel","magpie","mallard","mamba","mammoth","manatee","mandrill","manta","mantis","marlin","marmoset","marmot","marten","meerkat","merganser","mink","minnow","mole","mongoose","monitor","monkey","moose","moth","mouflon","mouse","mudpuppy","mule","muntjac","musk","muskox","muskrat","mussel","mustang","narwhal","newt","nightingale","numbat","nuthatch","nyala","ocelot","octopus","okapi","opossum","orangutan","orca","oriole","oryx","osprey","ostrich","otter","owl","oyster","oystercatcher","paca","panda","pangolin","panther","parakeet","parrot","parrotfish","partridge","peafowl","pelican","penguin","perch","petrel","pheasant","pigeon","pika","pike","pintail","pipefish","platypus","plover","pollock","pony","porcupine","porpoise","possum","ptarmigan","puffin","puma","pupfish","python","quail","quetzal","quokka","quoll","rabbit","raccoon","rail","rat","rattler","raven","ray","redpoll","reindeer","remora","rhino","roadrunner","robin","rook","sable","sailfish","salamander","salmon","sandpiper","sardine","scallop","scaup","scorpion","seahorse","seal","serval","shark","shearwater","shrew","shrike","shrimp","siskin","sitatunga","skate","skink","skua","skunk","sloth","smelt","snail","snipe","solenodon","sparrow","spider","springbok","squid","squirrel","starfish","starling","stilt","stingray","stoat","stork","sunbird","sunfish","swallow","swan","swift","swordfish","tahr","takin","tamarin","tanager","tapir","tarantula","tarpon","tarsier","teal","tenrec","terrapin","tern","tetra","thrasher","thrush","tiger","toad","tortoise","toucan","treecreeper","treefrog","trout","tuatara","tuna","turkey","turtle","urchin","urial","vicuna","viper","vole","vulture","wagtail","wallaby","walleye","walrus","warbler","warthog","waterbuck","weasel","wildebeest","wolf","wolverine","wombat","woodcock","woodpecker","wrasse","wren","yak","zebra","zebu","zorilla"];class SlugAllocator{activeSlugs;random;pool;pendingByRepo=new Map;chain=Promise.resolve();constructor(activeSlugs,options={}){this.activeSlugs=activeSlugs;if(this.random=options.random??Math.random,this.pool=options.pool??ANIMAL_NAMES,this.pool.length===0)throw Error("SlugAllocator: animal pool cannot be empty")}async allocate(repo){let previous=this.chain,release;this.chain=new Promise((resolve2)=>{release=resolve2}),await previous;try{return await this.pickLocked(repo)}finally{release()}}async claim(repo,slug){let previous=this.chain,release;this.chain=new Promise((resolve2)=>{release=resolve2}),await previous;try{if((await this.occupiedSlugs(repo)).has(slug))throw new WorktreeNameTakenError(slug);return this.addPending(repo,slug),slug}finally{release()}}commit(repo,slug){this.deletePending(repo,slug)}cancel(repo,slug){this.deletePending(repo,slug)}async pickLocked(repo){let occupied=await this.occupiedSlugs(repo),candidates=this.pool.filter((n)=>!occupied.has(n));if(candidates.length>0){let pick=candidates[Math.floor(this.random()*candidates.length)];return this.addPending(repo,pick),pick}let base=this.pool[Math.floor(this.random()*this.pool.length)];for(let v=2;;v++){let candidate=`${base}-v${v}`;if(!occupied.has(candidate))return this.addPending(repo,candidate),candidate}}async occupiedSlugs(repo){let set=new Set(this.pendingByRepo.get(repo)??[]);for(let slug of this.activeSlugs(repo))if(slug)set.add(slug);for(let dir of await listWorktreeDirNames(repo))set.add(dir);return set}addPending(repo,slug){let pending=this.pendingByRepo.get(repo);if(!pending)pending=new Set,this.pendingByRepo.set(repo,pending);pending.add(slug)}deletePending(repo,slug){let pending=this.pendingByRepo.get(repo);if(!pending)return;if(pending.delete(slug),pending.size===0)this.pendingByRepo.delete(repo)}}var WORKTREE_NAME_RE=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;class WorktreeCoordinator{store;worktrees;workspaces;canonPath;ensureProject;slugs;worktreeLocks=new Map;adoptLocks=new Map;reservedBranches=new Set;constructor(store,worktrees,workspaces,canonPath2,ensureProject){this.store=store,this.worktrees=worktrees,this.workspaces=workspaces,this.canonPath=canonPath2,this.ensureProject=ensureProject,this.slugs=new SlugAllocator((repo)=>this.store.list().filter((t)=>t.kind!=="main").flatMap((t)=>[...samePath(gitRepoOf(t),repo)?[t.worktreePath]:[],...(t.extraRepos??[]).filter((x)=>samePath(x.repo,repo)).map((x)=>x.worktreePath)]).map((path3)=>path3.match(/([^/\\]+)[/\\]*$/)?.[1]??"").filter((s)=>s.length>0))}async claimWorktreeName(repo,name){if(!WORKTREE_NAME_RE.test(name))throw new InvalidWorktreeNameError(name);return await this.slugs.claim(repo,name)}forget(id){this.worktreeLocks.delete(id)}async ensure(task,onProgress,forkFrom){let inflight=this.worktreeLocks.get(task.id);if(inflight){if(forkFrom&&!inflight.forked)throw await inflight.work.catch(()=>{}),new ForkUnsupportedError("the task was entered before its clone was made");return inflight.work}let work=this.createWorktree(task,onProgress,forkFrom);this.worktreeLocks.set(task.id,{work,forked:forkFrom!==void 0});try{return await work}finally{this.worktreeLocks.delete(task.id)}}async createWorktree(task,onProgress,forkFrom){let extrasMissing=await extrasToMaterialise(this.worktrees,task.extraRepos);if(task.worktreePath&&extrasMissing.length===0)return task.worktreePath;let total=(task.worktreePath?0:1)+extrasMissing.length,primary=task.worktreePath?void 0:await this.createPrimary(task,scaleProgress(onProgress,0,total),forkFrom),branch=primary?.branch??task.branch,undoPrimary=async()=>{if(!primary)return;await rollbackWorktree(this.workspaces.get(workspaceBackendId(task)),{path:primary.path,...primary.fresh?{fresh:{repo:gitRepoOf(task),branch}}:{}}),this.slugs.cancel(gitRepoOf(task),primary.slug)},extras;try{if(extrasMissing.length>0)extras=await materialiseExtras({worktrees:this.worktrees,backend:this.workspaces.get("git-worktree"),slugs:this.slugs,task,branch,progressFor:(i)=>scaleProgress(onProgress,(primary?1:0)+i,total)})}catch(err){throw await undoPrimary(),err}try{await this.store.update(task.id,{...primary?{worktreePath:primary.path,branch}:{},...extras?{extraRepos:extras.extraRepos}:{}})}catch(err){for(let wt of[...extras?.created??[]].reverse())await rollbackWorktree(this.workspaces.get("git-worktree"),wt);for(let{repo,slug}of extras?.slugs??[])this.slugs.cancel(repo,slug);throw await undoPrimary(),err}if(primary)this.slugs.commit(gitRepoOf(task),primary.slug);for(let{repo,slug}of extras?.slugs??[])this.slugs.commit(repo,slug);return primary?.path??task.worktreePath}async createPrimary(task,onProgress,forkFrom){let slug=task.worktreeName||await this.slugs.allocate(gitRepoOf(task)),branch=task.branch||await this.deriveAutoBranch(task),baseRef=task.baseRef;try{let fresh=!(await this.worktrees.listBranchNames(gitRepoOf(task))).includes(branch);return{path:(await this.workspaces.get(workspaceBackendId(task)).materialize({repo:gitRepoOf(task),slug,branch,baseRef,onProgress,forkFrom})).path,branch,slug,fresh}}catch(err){throw this.slugs.cancel(gitRepoOf(task),slug),err}finally{this.reservedBranches.delete(branch)}}async deriveAutoBranch(task){let names=await this.worktrees.listBranchNames(gitRepoOf(task)),base=deriveConventionBranch(task.title,inferBranchStyle(names),task.id),taken=new Set([...names,...this.reservedBranches]),branch=uniqueBranchName(base,taken,task.id);return this.reservedBranches.add(branch),branch}async discoverAdoptable(repo){let all=await this.worktrees.listAll(repo),linked=new Set(this.store.list().flatMap((t)=>taskWorktreePaths(t)).map((p)=>this.canonPath(p)));return all.filter((wt)=>!linked.has(this.canonPath(wt.path)))}async adopt(input){let target=this.canonPath(input.worktreePath),inflight=this.adoptLocks.get(target);if(inflight)return inflight;let work=this.adoptLocked(input,target);this.adoptLocks.set(target,work);try{return await work}finally{this.adoptLocks.delete(target)}}async adoptLocked(input,target){let holders=this.store.list().filter((t)=>taskWorktreePaths(t).some((p)=>this.canonPath(p)===target)),existing=holders.find((t)=>!t.sharesWorkspaceOf)??holders[0];if(existing){if(input.ifExists==="return")return existing;throw Error(`adoptWorktree: ${input.worktreePath} is already adopted as a task`)}let match=(await this.worktrees.listAdoptablePaths(input.repo)).find((wt)=>this.canonPath(wt.path)===target);if(!match)throw Error(`adoptWorktree: ${input.worktreePath} is not an adoptable git worktree of ${input.repo} (unknown, detached, or the main checkout)`);let branch=input.branch?.trim()||match.branch,title=(input.title??basename(match.path)).trim()||PLACEHOLDER_TASK_TITLE;return await this.ensureProject(input.repo),this.store.create({repo:input.repo,title,branch,worktreePath:match.path,status:"backlog",kind:"task",vendor:input.vendor??DEFAULT_TASK_VENDOR})}}class Orchestrator{store;worktrees;workspaces;checkpoints;worktreeCoordinator;editor;deletions;tasksAcc;activeTaskAcc;unsubscribeStore;mainTasks;tearDownSession;constructor(deps){this.store=deps.store,this.worktrees=deps.worktrees,this.workspaces=deps.workspaces??createWorkspaceRegistry(this.worktrees),this.checkpoints=new WorkspaceCheckpoints(this.store,this.worktrees,this.workspaces,(id)=>this.requireTask(id)),this.tearDownSession=deps.tearDownSession,this.worktreeCoordinator=new WorktreeCoordinator(this.store,this.worktrees,this.workspaces,canonPath,(repo)=>this.mainTasks.ensureIfEligible(repo)),this.mainTasks=new MainTaskCoordinator(this.store,(id)=>this.worktreeCoordinator.forget(id)),this.editor=new TaskEditor(this.store,this.worktrees),this.deletions=new TaskDeletionCoordinator(this.store,this.worktrees,this.workspaces,(id)=>this.worktreeCoordinator.forget(id),deps.onSalvage,deps.onWorktreeResidue,deps.onBranchKept),this.tasksAcc=createStateCell(this.store.list());let persistedFocus=readLastActiveTaskId();this.activeTaskAcc=createStateCell(persistedFocus&&this.store.get(persistedFocus)?persistedFocus:null),this.unsubscribeStore=this.store.subscribe((snapshot)=>{this.tasksAcc.set(snapshot.slice())})}async init(){try{let promotable=promotableDirTasks({tasks:this.store.list(),isRepoRoot:(path3)=>isGitRepo(path3)&&samePath(resolveRepoRoot(path3),path3)});for(let task of promotable)await this.mainTasks.ensureIfEligible(task.repo,"explicit")}catch{}}activeTaskSignal(){return this.activeTaskAcc}async setActiveTask(id){let next=id===null?null:String(id);if(next&&this.store.get(next)?.deletion)throw new TaskDeletingError(next);if(this.activeTaskAcc.set(next),next&&this.store.get(next))writeLastActiveTaskId(next)}tasksSignal(){return this.tasksAcc}subscribeTasks(listener){return this.store.subscribe(listener)}dispose(){this.unsubscribeStore()}listTasks(){return this.store.list()}getTask(id){return this.store.get(id)}createTask=(input)=>createOrForkTask({store:this.store,mainTasks:this.mainTasks,claimWorktreeName:(repo,name)=>this.worktreeCoordinator.claimWorktreeName(repo,name),ensureWorktree:(id)=>this.ensureWorktree(id),cloneInto:(child,parentPath)=>this.worktreeCoordinator.ensure(child,void 0,parentPath),workspaces:this.workspaces},input);openDirectoryTask=(input)=>openDirectoryTaskRow({store:this.store},input);adoptScratchRepo=(id,repo)=>adoptScratchRepoRow({store:this.store},id,repo);moveTaskToProject=(id,repo)=>moveTaskToProjectRow({store:this.store,ensureMainTask:(target)=>this.ensureMainTask(target)},id,repo);spawnChildTask=(input)=>spawnChildTaskRow({store:this.store,worktrees:this.worktrees,createTask:this.createTask,ensureWorktree:(id)=>this.ensureWorktree(id)},input);createMigratedChild=(input)=>createMigratedChildRow({store:this.store},input);setPtyAlias=(id,alias)=>setPtyAliasRow({store:this.store},String(id),alias);async ensureMainTask(repo){return await this.mainTasks.ensure(repo)}async ensureWorktree(id,opts){let task=this.requireTask(id);if(task.deletion)throw new TaskDeletingError(String(task.id));if(!workspaceCapabilities(task).ownsDirectory)return(await this.workspaces.get(workspaceBackendId(task)).materialize({repo:task.repo,slug:"",branch:task.branch,...task.kind==="dir"?{path:task.worktreePath}:{}})).path;let owner=task.sharesWorkspaceOf?this.store.get(task.sharesWorkspaceOf):void 0;if(owner){let path3=await this.ensureWorktree(owner.id,opts);if(path3!==task.worktreePath||owner.branch!==task.branch)await this.store.update(task.id,{worktreePath:path3,branch:owner.branch});return path3}if(task.worktreePath){if(await this.worktrees.pathExists(task.worktreePath))return task.extraRepos?.length?this.worktreeCoordinator.ensure(task,opts?.onProgress):task.worktreePath;return await this.worktrees.pruneWorktrees(gitRepoOf(task)),await this.store.update(task.id,{worktreePath:""}),this.worktreeCoordinator.ensure({...task,worktreePath:""},opts?.onProgress)}return this.worktreeCoordinator.ensure(task,opts?.onProgress)}canCheckpoint=(id)=>this.checkpoints.canCheckpoint(id);checkpointScope=(id)=>this.checkpoints.checkpointScope(id);takeCheckpoint=(id,opts)=>this.checkpoints.takeCheckpoint(id,opts);listCheckpoints=(id)=>this.checkpoints.listCheckpoints(id);rewindToCheckpoint=(id,n)=>this.checkpoints.rewindToCheckpoint(id,n);exportBranch=(id)=>this.checkpoints.exportBranch(id);async clearWorktreePath(id){let task=this.store.get(id);if(!task||!task.worktreePath)return;if(!workspaceCapabilities(task).ownsDirectory)return;let gone=canonPath(task.worktreePath);for(let holder of this.store.list()){if(holder.kind!=="task"||!holder.worktreePath||canonPath(holder.worktreePath)!==gone)continue;await this.store.update(holder.id,{worktreePath:""}),this.worktreeCoordinator.forget(holder.id)}}setTitle=(id,title,opts)=>this.editor.setTitle(id,title,opts);setBranch=(id,branch)=>this.editor.setBranch(id,branch);observeLanguage=(id,text)=>this.editor.observeLanguage(id,text);setVendor=(id,vendor,effort,model)=>this.editor.setVendor(id,vendor,effort,model);setCommand=(id,command,vendor)=>this.editor.setCommand(id,command,vendor);setPinned=(id,pinned)=>this.editor.setPinned(id,pinned);setKept=(id,kept)=>this.editor.setKept(id,kept);updateNotes=(id,edit)=>this.editor.updateNotes(id,edit);setParent=(id,parentId,replyTab)=>this.editor.setParent(id,parentId,replyTab);setArchived=(id,archived)=>this.editor.setArchived(id,archived);setRequireReport=(id,required)=>this.editor.setRequireReport(id,required);moveTask=(id,delta)=>this.editor.moveTask(id,delta);moveTaskToTop=(id)=>this.editor.moveTaskToTop(id);setStatus=(id,status)=>this.editor.setStatus(id,status);setWorkerReport=(id,report)=>this.editor.setWorkerReport(id,report);setPRStatus=(id,prStatus,repoName)=>this.editor.setPRStatus(id,prStatus,repoName);addSessionPr=(id,url)=>this.editor.addSessionPr(id,url);setSessionPrStatus=(id,url,status)=>this.editor.setSessionPrStatus(id,url,status);setLinkedWorkItem=(id,item)=>this.editor.setLinkedWorkItem(id,item);setQuotaResume=(id,state)=>this.editor.setQuotaResume(id,state);setScheduledStart=(id,state)=>this.editor.setScheduledStart(id,state);setAutoFixCi=(id,state)=>this.editor.setAutoFixCi(id,state);setPrompt=(id,prompt)=>this.editor.setPrompt(id,prompt);async deleteTask(id,opts){await this.deletions.deleteNow(id,opts)}async prepareTaskDeletion(id,opts){return this.deletions.prepare(id,opts)}async beginTaskDeletion(id){return this.deletions.begin(id)}async finishTaskDeletion(id){return this.deletions.finish(id)}async landPreflight(id,repo){let task=this.requireTask(id);if(assertOwnsBranch(task),task.deletion)throw new TaskDeletingError(String(task.id));return pickRepos(task,repo),task.extraRepos?.length?landPreflightAll(task,repo):landPreflight(task)}async landTask(id,opts){let task=this.requireTask(id);if(task.deletion)throw new TaskDeletingError(String(task.id));return landRouted(task,opts??{},{list:()=>this.store.list(),worktrees:this.worktrees,tearDownSession:this.tearDownSession,clearWorktreePath:(tid)=>this.clearWorktreePath(tid),clearRepoWorktreePath:(tid,name)=>this.clearRepoWorktreePath(tid,name)})}async clearRepoWorktreePath(id,repoName){let task=this.store.get(id);if(!task?.extraRepos?.some((x)=>x.name===repoName))return this.clearWorktreePath(id);await this.store.update(task.id,{extraRepos:task.extraRepos.map((x)=>x.name===repoName?{...x,worktreePath:""}:x)})}async forgetProject(repo){await this.mainTasks.forget(repo)}async discoverAdoptableWorktrees(repo){if(!repo)throw Error("discoverAdoptableWorktrees: repo is required");return this.worktreeCoordinator.discoverAdoptable(repo)}async adoptWorktree(input){if(!input.repo)throw Error("adoptWorktree: repo is required");if(!input.worktreePath)throw Error("adoptWorktree: worktreePath is required");return this.worktreeCoordinator.adopt(input)}requireTask(id){let task=this.store.get(id);if(!task)throw new TaskNotFoundError(String(id));return task}}
8
+ export{Orchestrator};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{isPathLikeArg,runOpenDirectory}from"./chunk-rjy900p4.js";import"./chunk-2r1fgng9.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-pn5ygqn6.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{runOpenDirectory,isPathLikeArg};
@@ -1,11 +1,11 @@
1
1
  // @bun
2
- import{deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailedAdapter,deriveTitleFromSession,deriveTitleFromSessionId,ensureTaskSessionAdapter,startTaskSessionWithPromptAdapter,tearDownTaskSessionAdapter}from"./chunk-bbksmb5a.js";import{resolvePluginBinPath}from"./chunk-b0ssvpht.js";import{Orchestrator}from"./chunk-sqxdfzdq.js";import{GH_PR_VIEW_FIELDS,classifyGhFailure,mapGhPrView,nextPrPoll,samePrStatus}from"./chunk-x20bs6bs.js";import"./chunk-c1dz3qg3.js";import{GitWorktreeManager,parseDirtyPaths}from"./chunk-kx53jjee.js";import{startChatChannels}from"./chunk-by6fjf7m.js";import{auditDeletionBranchKept,auditDeletionResidue,auditDeletionSalvaged,auditWorktreeResidue,auditWorktreeSalvaged,parseAheadBehind,startDaemonServer}from"./chunk-cyzp2btw.js";import"./chunk-a4hsqj1b.js";import"./chunk-d9e4ssy7.js";import"./chunk-1exwrmjd.js";import"./chunk-k67vbszb.js";import"./chunk-cpgnm1xr.js";import"./chunk-r3s2t9af.js";import"./chunk-6xr1prk6.js";import{preferredReleaseChannel}from"./chunk-0g3sjn8z.js";import"./chunk-xmwp6je4.js";import{cancelAllLogins}from"./chunk-pm91e38v.js";import{affectsActivityState,isEngineActivityKind}from"./chunk-17e2tgyg.js";import{PLACEHOLDER_TASK_TITLE}from"./chunk-q9zgj6kw.js";import"./chunk-mmq6922s.js";import"./chunk-7z463zkx.js";import"./chunk-4hestsjp.js";import"./chunk-qgrg9a5n.js";import"./chunk-2bgmby1d.js";import"./chunk-1rjq808h.js";import"./chunk-gq36nzg8.js";import"./chunk-5mhvjytn.js";import"./chunk-atn06ncf.js";import{RELAY_PREAMBLE}from"./chunk-sq6zj3fb.js";import{TaskIndexStore}from"./chunk-hfdk7j28.js";import"./chunk-qkehg2c1.js";import{autoStatusEnabled}from"./chunk-15fp4p9v.js";import{GENERIC_PROTOCOL,engineDisplayName,engineProtocolKey,getEngineProtocol,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol}from"./chunk-rzef6szc.js";import{teardownScope}from"./chunk-sz2xs0a2.js";import"./chunk-fvvb0072.js";import"./chunk-ggnq2vvk.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import{stateFileTabsPersistence}from"./chunk-783d1ba0.js";import{installDaemonCrashHandlers,logDaemonInfo}from"./chunk-rxqdw6s5.js";import"./chunk-tn9gtf18.js";import"./chunk-yzkhwn31.js";import{execHostForWorktreePath}from"./chunk-895yqjjz.js";import"./chunk-sak1tphp.js";import"./chunk-r5j8fycq.js";import"./chunk-xfgqae9t.js";import"./chunk-j1z9kgyd.js";import"./chunk-y1xnjx8x.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-qvnr8c18.js";import{createEngineTurnDetector,engineEntry,engineTitleTurnHint,vendorsWithQuotaProbe}from"./chunk-edrxs4cy.js";import{availableEngineIds}from"./chunk-03rbg95q.js";import"./chunk-w7zv47mp.js";import{spawnCapture}from"./chunk-2tfnscxk.js";import"./chunk-2w228s7t.js";import"./chunk-etfh8f4z.js";import"./chunk-h8t4xg1p.js";import{parsePorcelain}from"./chunk-55ajzrj6.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import{DEFAULT_BASE_REF_CANDIDATES,readHeadSha,readRefSha,resolveGitDirs}from"./chunk-kg9kmwt9.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{argvHasFlag,switchFlag}from"./chunk-zsfyj36g.js";import{connectOrStartDaemon,daemonSpawnReason}from"./chunk-387ecqb8.js";import{stopDaemonProcess}from"./chunk-kgs56yns.js";import"./chunk-4hnz77xf.js";import"./chunk-2brn8e1y.js";import"./chunk-6h4qdckd.js";import"./chunk-e01321zn.js";import"./chunk-7kysxgbq.js";import"./chunk-b68dd36h.js";import"./chunk-ec7f3wcm.js";import{DEFAULT_TASK_VENDOR,isTaskStatus}from"./chunk-xy8be792.js";import{BUILTIN_VENDORS,isBuiltinVendor}from"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import{migrateRoveDaemonStateLayout}from"./chunk-8t7qbr1e.js";import{SUBCOMMAND_VERBS}from"./chunk-tjj9bt7n.js";import{CURRENT_VERSION,checkLatestVersion}from"./chunk-w9cdc97q.js";import{backfillSavedReposFromProjects,getCustomEngineIds,getPersistedString,getRemoteRepoConfig,getSavedRepos,setPersistedString}from"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.js";import"./chunk-32j6y5d1.js";import{releaseSync}from"./chunk-bejhca6z.js";import"./chunk-z1nj22va.js";import{isProcessAlive,readPidFile}from"./chunk-br8r72bf.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-1ffqacz9.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient}from"./chunk-sza3tyhs.js";import"./chunk-96kvwzj8.js";import{rotateLogIfNeeded}from"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath}from"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import{readRoveHomeDirEnv}from"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";function sniffProtocolFromTitle(title){let trimmed=title?.trim();if(!trimmed)return null;let owners=new Map;for(let vendor of BUILTIN_VENDORS)for(let glyph of engineEntry(vendor).terminalTitle?.statusPrefixes??[])owners.set(glyph,[...owners.get(glyph)??[],vendor]);for(let glyph of[...owners.keys()].sort((a,b)=>b.length-a.length)){if(!trimmed.startsWith(glyph))continue;if(trimmed.slice(glyph.length).trim().length===0)continue;let vendors=owners.get(glyph)??[];if(vendors.length===1)return vendors[0];return null}return null}function protocolUpgradeFromLiveSession(task,evidence){let command=task.command?.trim();if(!command)return null;if(task.vendor&&isBuiltinVendor(task.vendor))return null;if(resolveCommandProtocol(command)!==GENERIC_PROTOCOL)return null;let walk=evidence.walkVendor,vendor=walk!==null&&isBuiltinVendor(walk)?walk:sniffProtocolFromTitle(evidence.title);return vendor?{command,vendor}:null}function protocolWriteBackFromLiveSession(task,evidence,customEngineIds){let id=task.vendor?.trim();if(!id)return null;if(getEngineProtocol(id)!==void 0)return null;if(!customEngineIds.includes(id))return null;let walk=evidence.walkVendor;return walk!==null&&isBuiltinVendor(walk)?{id,protocol:walk}:null}async function latestTranscriptMtime(vendor,worktree){if(!worktree)return 0;return protocolEntry(vendor).history.latestTranscriptMtimeForWorktree(worktree)}async function maybeAutoStart(orch,taskId,enabled=autoStatusEnabled){if(!enabled())return"skipped";let task=orch.getTask(taskId);if(!task)return"skipped";if((task.kind??"task")==="main")return"skipped";if(task.status!=="backlog")return"skipped";return await orch.setStatus(taskId,"in_progress"),"moved"}var SYNC_TIMEOUT_MS=60000,SYNC_CONFLICT="SYNC_CONFLICT",SYNC_DIRTY="SYNC_WORKTREE_DIRTY";async function git(cwd,args,signal,write=!1){return spawnCapture("git",args,{cwd,env:write?process.env:readOnlyGitProcessEnv(),signal})}function parseConflictedPaths(stdout){return stdout.split(`
2
+ import{deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailedAdapter,deriveTitleFromSession,deriveTitleFromSessionId,ensureTaskSessionAdapter,startTaskSessionWithPromptAdapter,tearDownTaskSessionAdapter}from"./chunk-n94vzkbe.js";import{buildCIPromptForWorktree}from"./chunk-vkz6kkmm.js";import{Orchestrator}from"./chunk-bv050ssb.js";import{GH_PR_VIEW_FIELDS,classifyGhFailure,mapGhPrView,nextPrPoll,samePrStatus}from"./chunk-1wwqf6kx.js";import"./chunk-c1dz3qg3.js";import{resolvePluginBinPath}from"./chunk-b0ssvpht.js";import{GitWorktreeManager,parseDirtyPaths}from"./chunk-3x5g06mw.js";import{startChatChannels}from"./chunk-kag7cfbr.js";import{auditDeletionBranchKept,auditDeletionResidue,auditDeletionSalvaged,auditWorktreeResidue,auditWorktreeSalvaged,parseAheadBehind,startDaemonServer}from"./chunk-s1308b3d.js";import"./chunk-1g6vhgfe.js";import"./chunk-a4hsqj1b.js";import"./chunk-6hcyvg0m.js";import{refreshOutdatedSkills}from"./chunk-5vrg6h71.js";import"./chunk-4dt1gfb3.js";import"./chunk-tq0h3vy6.js";import"./chunk-cpgnm1xr.js";import"./chunk-r3s2t9af.js";import"./chunk-6xr1prk6.js";import{preferredReleaseChannel,updateDelayHours}from"./chunk-m4z6kepz.js";import"./chunk-xmwp6je4.js";import{cancelAllLogins}from"./chunk-pm91e38v.js";import{PLACEHOLDER_TASK_TITLE}from"./chunk-2xsxcdp6.js";import{affectsActivityState,isEngineActivityKind}from"./chunk-17e2tgyg.js";import"./chunk-1vxtd8rh.js";import"./chunk-q8ffd1pc.js";import"./chunk-4hestsjp.js";import"./chunk-qgrg9a5n.js";import"./chunk-2bgmby1d.js";import"./chunk-n0rdn2fn.js";import"./chunk-5mhvjytn.js";import"./chunk-atn06ncf.js";import"./chunk-vp29jgbt.js";import"./chunk-gq36nzg8.js";import{RELAY_PREAMBLE}from"./chunk-sq6zj3fb.js";import{TaskIndexStore}from"./chunk-7kgcy89w.js";import"./chunk-qkehg2c1.js";import"./chunk-j1qy79fd.js";import"./chunk-z43e08kw.js";import"./chunk-w77s2df8.js";import"./chunk-mqfvzdpr.js";import"./chunk-cf3vpsm5.js";import{installDaemonCrashHandlers,logDaemonError,logDaemonInfo}from"./chunk-rxqdw6s5.js";import"./chunk-kxp04jnp.js";import{autoStatusEnabled}from"./chunk-61cn88e7.js";import{GENERIC_PROTOCOL,engineDisplayName,engineProtocolKey,getEngineProtocol,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol}from"./chunk-atg13xab.js";import"./chunk-r5j8fycq.js";import"./chunk-xfgqae9t.js";import"./chunk-j1z9kgyd.js";import{teardownScope}from"./chunk-g9ba25q5.js";import"./chunk-ggnq2vvk.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import{stateFileTabsPersistence}from"./chunk-mdc7b01e.js";import"./chunk-tn9gtf18.js";import{execHostForWorktreePath}from"./chunk-895yqjjz.js";import"./chunk-sak1tphp.js";import"./chunk-y1xnjx8x.js";import{connectOrStartDaemon,daemonSpawnReason}from"./chunk-e58kj4gm.js";import{stopDaemonProcess}from"./chunk-0a65j1zj.js";import"./chunk-bcyex84w.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-vvqwgcge.js";import{createEngineTurnDetector,engineEntry,engineTitleTurnHint,vendorsWithQuotaProbe}from"./chunk-x83xbzjd.js";import{spawnCapture}from"./chunk-2tfnscxk.js";import{availableEngineIds}from"./chunk-hzz020xx.js";import"./chunk-w7zv47mp.js";import"./chunk-2w228s7t.js";import"./chunk-etfh8f4z.js";import"./chunk-9rj2fx6v.js";import{backfillSavedReposFromProjects,getCustomEngineIds,getPersistedString,getRemoteRepoConfig,getSavedRepos,setPersistedString}from"./chunk-rfp0ayra.js";import{parsePorcelain}from"./chunk-0r1jf8m9.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-40dgv828.js";import{DEFAULT_BASE_REF_CANDIDATES,readHeadSha,readRefSha,resolveGitDirs}from"./chunk-kg9kmwt9.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{CURRENT_VERSION,checkLatestVersion}from"./chunk-9ckvdy0j.js";import{argvHasFlag,switchFlag}from"./chunk-zsfyj36g.js";import"./chunk-2brn8e1y.js";import"./chunk-7mwvvj46.js";import{SUBCOMMAND_VERBS}from"./chunk-qqr4tfhk.js";import{loadStateFile}from"./chunk-32j6y5d1.js";import"./chunk-wzss4pzh.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient}from"./chunk-qzzmd63x.js";import"./chunk-96kvwzj8.js";import"./chunk-7bakcyes.js";import"./chunk-8qkyd589.js";import"./chunk-7kysxgbq.js";import"./chunk-hz1x3hak.js";import"./chunk-x1jxxmtc.js";import{DEFAULT_TASK_VENDOR,isTaskStatus}from"./chunk-xy8be792.js";import{BUILTIN_VENDORS,isBuiltinVendor}from"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import{migrateRoveDaemonStateLayout}from"./chunk-8t7qbr1e.js";import{releaseSync}from"./chunk-bejhca6z.js";import{isProcessAlive,readPidFile}from"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{rotateLogIfNeeded}from"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath}from"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import{readRoveHomeDirEnv}from"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";function sniffProtocolFromTitle(title){let trimmed=title?.trim();if(!trimmed)return null;let owners=new Map;for(let vendor of BUILTIN_VENDORS)for(let glyph of engineEntry(vendor).terminalTitle?.statusPrefixes??[])owners.set(glyph,[...owners.get(glyph)??[],vendor]);for(let glyph of[...owners.keys()].sort((a,b)=>b.length-a.length)){if(!trimmed.startsWith(glyph))continue;if(trimmed.slice(glyph.length).trim().length===0)continue;let vendors=owners.get(glyph)??[];if(vendors.length===1)return vendors[0];return null}return null}function protocolUpgradeFromLiveSession(task,evidence){let command=task.command?.trim();if(!command)return null;if(task.vendor&&isBuiltinVendor(task.vendor))return null;if(resolveCommandProtocol(command)!==GENERIC_PROTOCOL)return null;let walk=evidence.walkVendor,vendor=walk!==null&&isBuiltinVendor(walk)?walk:sniffProtocolFromTitle(evidence.title);return vendor?{command,vendor}:null}function protocolWriteBackFromLiveSession(task,evidence,customEngineIds){let id=task.vendor?.trim();if(!id)return null;if(getEngineProtocol(id)!==void 0)return null;if(!customEngineIds.includes(id))return null;let walk=evidence.walkVendor;return walk!==null&&isBuiltinVendor(walk)?{id,protocol:walk}:null}async function latestTranscriptMtime(vendor,worktree){if(!worktree)return 0;return protocolEntry(vendor).history.latestTranscriptMtimeForWorktree(worktree)}async function maybeAutoStart(orch,taskId,enabled=autoStatusEnabled){if(!enabled())return"skipped";let task=orch.getTask(taskId);if(!task)return"skipped";if((task.kind??"task")==="main")return"skipped";if(task.status!=="backlog")return"skipped";return await orch.setStatus(taskId,"in_progress"),"moved"}var SYNC_TIMEOUT_MS=60000,SYNC_CONFLICT="SYNC_CONFLICT",SYNC_DIRTY="SYNC_WORKTREE_DIRTY";async function git(cwd,args,signal,write=!1){return spawnCapture("git",args,{cwd,env:write?process.env:readOnlyGitProcessEnv(),signal})}function parseConflictedPaths(stdout){return stdout.split(`
3
3
  `).map((line)=>line.trim()).filter((line)=>line.length>0)}async function syncWorktreeWithBase(worktreePath,baseRef,signal){let status=await git(worktreePath,["status","--porcelain=v1"],signal);if(status.status!==0)throw Error("git status failed");let dirty=parseDirtyPaths(status.stdout);if(dirty.length>0)throw Error(`${SYNC_DIRTY}: ${dirty.join(", ")}`);let before=await git(worktreePath,["rev-parse","HEAD"],signal),merge=await git(worktreePath,["merge","--no-edit",baseRef],signal,!0);if(merge.status!==0){let conflicted=await git(worktreePath,["diff","--name-only","--diff-filter=U"],signal),paths=parseConflictedPaths(conflicted.stdout);if(paths.length>0)throw Error(`${SYNC_CONFLICT}: ${paths.join(", ")}`);let detail=merge.stderr?.trim();throw Error(detail?`git merge ${baseRef} failed \u2014 ${detail}`:`git merge ${baseRef} failed`)}let after=await git(worktreePath,["rev-parse","HEAD"],signal);return{baseRef,alreadyCurrent:before.status===0&&before.stdout.trim()===after.stdout.trim()}}import{readFileSync}from"fs";import{join}from"path";var BASE_REF_TTL_MS=300000,cache=new Map;async function git2(worktreePath,args,signal){return await spawnCapture("git",[...args],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal})}async function refExists(worktreePath,ref,signal){return(await git2(worktreePath,["rev-parse","--verify","--quiet",ref],signal)).status===0}async function sharesHistory(worktreePath,ref,signal){return(await git2(worktreePath,["merge-base",ref,"HEAD"],signal)).status===0}async function baseCheckoutBranchFromGit(worktreePath,signal){let out=await git2(worktreePath,["worktree","list","--porcelain"],signal);if(out.status!==0)return null;return(out.stdout.split(`
4
4
 
5
5
  `,1)[0]??"").split(`
6
- `).find((l)=>l.startsWith("branch "))?.slice(7).trim().replace(/^refs\/heads\//,"")||null}function readRefSymbolic(dirs,ref){for(let dir of[dirs.gitDir,dirs.commonDir])try{let target=readFileSync(join(dir,ref),"utf8").trim().match(/^ref:\s*(.+)$/)?.[1]?.trim();if(target)return target}catch{}return null}function ladderFromFiles(worktreePath){let dirs=resolveGitDirs(worktreePath);if(!dirs)return;let originHead=readRefSymbolic(dirs,"refs/remotes/origin/HEAD")?.replace(/^refs\/remotes\//,""),baseCheckout=readRefSymbolic({gitDir:dirs.commonDir,commonDir:dirs.commonDir},"HEAD")?.replace(/^refs\/heads\//,""),names=[],head=readHeadSha(dirs),parts=[`HEAD=${head}`];for(let guess of[...originHead?[originHead]:[],...DEFAULT_BASE_REF_CANDIDATES,...baseCheckout?[baseCheckout]:[]]){if(names.includes(guess))continue;let sha=readRefSha(dirs,guess);if(sha===null)continue;names.push(guess),parts.push(`${guess}=${sha}`)}return{names,fingerprint:head===null?null:parts.join(",")}}async function ladderFromGit(worktreePath,signal){let head=await git2(worktreePath,["symbolic-ref","--short","refs/remotes/origin/HEAD"],signal),originHead=head.status===0?head.stdout.trim():"",baseCheckout=await baseCheckoutBranchFromGit(worktreePath,signal),names=[],candidates=[...originHead?[originHead]:[],...DEFAULT_BASE_REF_CANDIDATES,...baseCheckout?[baseCheckout]:[]];for(let guess of candidates){if(names.includes(guess))continue;if(await refExists(worktreePath,guess,signal))names.push(guess)}return{names,fingerprint:null}}async function resolveBaseRefCached(worktreePath,recordedBaseRef,signal,now=Date.now()){if(recordedBaseRef){let dirs=resolveGitDirs(worktreePath);if(dirs&&readRefSha(dirs,recordedBaseRef)!==null)return recordedBaseRef;if(await refExists(worktreePath,recordedBaseRef,signal))return recordedBaseRef}let hit=cache.get(worktreePath);if(hit&&now-hit.at<BASE_REF_TTL_MS)return hit.ref;let ladder=ladderFromFiles(worktreePath)??await ladderFromGit(worktreePath,signal);if(hit&&ladder.fingerprint!==null&&hit.fingerprint===ladder.fingerprint)return cache.set(worktreePath,{...hit,at:now}),hit.ref;let ref=null;for(let candidate of ladder.names)if(await sharesHistory(worktreePath,candidate,signal)){ref=candidate;break}return cache.set(worktreePath,{ref,at:now,fingerprint:ladder.fingerprint}),ref}var cache2=new Map;function readShas(worktreePath,baseRef){let dirs=resolveGitDirs(worktreePath);if(!dirs)return null;let head=readHeadSha(dirs),base=readRefSha(dirs,baseRef);return head&&base?{head,base}:null}async function driftCached(worktreePath,baseRef,compute){let shas=readShas(worktreePath,baseRef),hit=shas?cache2.get(worktreePath):void 0;if(shas&&hit&&hit.head===shas.head&&hit.base===shas.base)return hit.value;let value=await compute();if(shas&&value!==null)cache2.set(worktreePath,{...shas,value});return value}import{statSync}from"fs";function judgeWorktree(signals,nowMs){if(signals.dirty)return{verdict:"active",reason:"dirty"};if(signals.prState==="open")return{verdict:"active",reason:"prOpen"};if(signals.prState==="merged")return{verdict:"merged",reason:"prMerged"};if(signals.aheadOfDefault===0)return{verdict:"merged",reason:"inMain"};if(signals.prState==="closed")return{verdict:"stale",reason:"prClosed"};if(signals.lastActivityMs>0&&nowMs-signals.lastActivityMs>1209600000)return{verdict:"stale",reason:"idle"};return{verdict:"fresh",reason:"fresh"}}function parseGhPrList(stdout){let entries;try{entries=JSON.parse(stdout)}catch{return null}if(!Array.isArray(entries))return null;let rank={open:3,merged:2,closed:1},map=new Map;for(let entry of entries){let branch=entry?.headRefName,rawState=entry?.state;if(typeof branch!=="string"||typeof rawState!=="string")continue;let state=rawState.toLowerCase();if(state!=="open"&&state!=="merged"&&state!=="closed")continue;let cur=map.get(branch);if(!cur||rank[state]>rank[cur])map.set(branch,state)}return map}var TIMEOUT_MS=4000,manager=new GitWorktreeManager;function createdAt(path){try{let stat=statSync(path);return stat.birthtimeMs||stat.mtimeMs}catch{return 0}}async function run(path,argv){return execHostForWorktreePath(path).run(argv,{cwd:path,signal:AbortSignal.timeout(TIMEOUT_MS)})}async function branchOnRemote(path,branch){try{let out=await run(path,["git","ls-remote","--exit-code","--heads","origin",branch]);return out.exitCode===0?!0:out.exitCode===2?!1:null}catch{return null}}async function defaultRef(repo){try{let out=await run(repo,["git","symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(out.exitCode===0&&out.stdout.trim())return out.stdout.trim()}catch{}for(let guess of["origin/main","origin/master"])try{if((await run(repo,["git","rev-parse","--verify","--quiet",guess])).exitCode===0)return guess}catch{}return null}async function prStates(repo){try{let origin=await run(repo,["git","remote","get-url","origin"]);if(origin.exitCode!==0||!origin.stdout.includes("github.com"))return null;let out=await run(repo,["gh","pr","list","--state","all","--limit","200","--json","headRefName,state"]);return out.exitCode===0?parseGhPrList(out.stdout):null}catch{return null}}async function ahead(path,ref){if(!ref)return null;try{let out=await run(path,["git","rev-list","--count",`${ref}..HEAD`]),value=Number.parseInt(out.stdout.trim(),10);return out.exitCode===0&&!Number.isNaN(value)?value:null}catch{return null}}async function listWorktreeProjectsAdapter(network){let repos=getSavedRepos().filter((repo)=>!getRemoteRepoConfig(repo)),now=Date.now();return Promise.all(repos.map(async(repo)=>{let[worktrees,states,ref]=await Promise.all([manager.listAll(repo),network?prStates(repo):null,defaultRef(repo)]),rows=await Promise.all(worktrees.map(async(worktree)=>{let[remote,aheadBy]=await Promise.all([network?branchOnRemote(worktree.path,worktree.branch):null,ahead(worktree.path,ref)]),judgement=judgeWorktree({dirty:worktree.dirty===!0,prState:states?.get(worktree.branch)??null,aheadOfDefault:aheadBy,lastActivityMs:worktree.lastActivityMs},now);return{...worktree,repo,createdAtMs:createdAt(worktree.path),branchOnRemote:remote,verdict:judgement.verdict,verdictReason:judgement.reason}}));return{repo,worktrees:rows}}))}async function listUnreadableWorktreesAdapter(repo){return manager.listUnreadableWorktrees(repo)}async function removeWorktreeAdapter(path,force){let residue=null;return await manager.remove(path,{force,onSalvage:(record)=>{if(record)auditWorktreeSalvaged(path,record.ref,record.commit,record.uncaptured)},onResidue:(r)=>{residue={path:r.path,reason:r.reason},auditWorktreeResidue(r.path,r.reason)}}),residue}function resolveProtocolUpgradeAndLearnPreset(task,evidence){let preset=protocolWriteBackFromLiveSession(task,evidence,getCustomEngineIds());if(preset)setPersistedString(engineProtocolKey(preset.id),preset.protocol);return protocolUpgradeFromLiveSession(task,evidence)}var daemonRuntime={currentVersion:CURRENT_VERSION,tabsPersistence:stateFileTabsPersistence,startMachineHub(bus){let stop=null,stopped=!1;return import("./chunk-16ttm9x6.js").then(({startDaemonMachineHub})=>{if(!stopped)stop=startDaemonMachineHub(bus)}),()=>{stopped=!0,stop?.()}},async invokeApi(verb,params,socketPath,route){let[{executeVerb,errorEnvelope},{daemonCaller},{RoveDaemonClient:RoveDaemonClient2}]=await Promise.all([import("./chunk-ewqqwtrj.js"),import("./chunk-34tbcjdy.js"),import("./chunk-5dd7trgf.js")]);if(route?.forwarded){let{inboundRefusal}=await import("./chunk-p6334e7v.js"),denied=inboundRefusal(verb);if(denied){let{ApiError}=await import("./chunk-34tbcjdy.js");return errorEnvelope(new ApiError(`this machine refuses "${denied}" verbs from other machines (${verb} from ${route.from??"a hub"})`,"INBOUND_DENIED",{hint:`on this machine: rove machine inbound allow ${denied}`}))}}let client=new RoveDaemonClient2(socketPath);try{await client.connect();let{invokeRouted}=await import("./chunk-b5c7p22a.js");return await invokeRouted({verb,params,machine:route?.machine,forwarded:route?.forwarded,localTasks:()=>client.request("task.list").then((res)=>res.tasks??[]),isLocalTask:(id)=>client.request("task.get",{taskId:id}).then(()=>!0,()=>!1),runHere:()=>executeVerb(verb,{params},{client,caller:daemonCaller(route?.forwarded===!0)})})}catch(err){return errorEnvelope(err)}finally{client.close()}},defaultTaskVendor:DEFAULT_TASK_VENDOR,placeholderTaskTitle:PLACEHOLDER_TASK_TITLE,isTaskStatus,isEngineActivityKind,affectsActivityState,async foregroundEngines(pids){let rows=parsePsSnapshot(await psSnapshot([...pids])),out=new Map;for(let pid of pids){let found=foregroundEngineIn(rows,pid);out.set(pid,found?{vendor:found.vendor,pid:found.pid}:null)}return out},titleTurnHint:(vendor,title)=>engineTitleTurnHint(sessionProtocol(vendor),title),sessionProtocol,resolveProtocolUpgrade:resolveProtocolUpgradeAndLearnPreset,readEngineTurns:async(vendor,transcriptPath)=>await protocolEntry(vendor).readTurns?.(transcriptPath)??[],checkLatestVersion:()=>checkLatestVersion({channel:preferredReleaseChannel()}),latestTranscriptMtime,deriveTitleFromSession,deriveTitleFromSessionId,createEngineTurnDetector:(vendor)=>createEngineTurnDetector(sessionProtocol(vendor)),async runWorktreeStatus(worktreePath,signal,baseRef){let result=await spawnCapture("git",["status","--porcelain=v1"],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal});if(result.status!==0)throw Error("git status failed");let counts=parsePorcelain(result.stdout),base=await resolveBaseRefCached(worktreePath,baseRef,signal);if(!base)return counts;let drift=await driftCached(worktreePath,base,async()=>{let out=await spawnCapture("git",["rev-list","--left-right","--count",`${base}...HEAD`],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal});return out.status===0?parseAheadBehind(out.stdout):null});return drift===null?counts:{...counts,...drift}},maybeAutoStart:(orch,taskId)=>maybeAutoStart(orch,taskId),listWorktreeProjects:listWorktreeProjectsAdapter,listUnreadableWorktrees:listUnreadableWorktreesAdapter,removeWorktree:removeWorktreeAdapter,async syncWorktreeWithBase(worktreePath,recordedBaseRef){let controller=new AbortController,timer=setTimeout(()=>controller.abort(),SYNC_TIMEOUT_MS);try{let baseRef=await resolveBaseRefCached(worktreePath,recordedBaseRef,controller.signal);if(!baseRef)throw Error("no base ref resolves for this worktree");return await syncWorktreeWithBase(worktreePath,baseRef,controller.signal)}finally{clearTimeout(timer)}},availableEngineIds,engineDisplayName,roveApiInvocation,engineEntry,ensureTaskSession:ensureTaskSessionAdapter,startTaskSessionWithPrompt:startTaskSessionWithPromptAdapter,tearDownTaskSession:tearDownTaskSessionAdapter,async readEngineContextUsage(vendor,sessionId){let read=protocolEntry(vendor).history.readUsageSnapshot;if(!read)return null;let snapshot=await read(sessionId);if(snapshot?.context_tokens===void 0)return null;return{contextTokens:snapshot.context_tokens,...snapshot.context_window_tokens===void 0?{}:{contextWindowTokens:snapshot.context_window_tokens},...snapshot.context_tokens_approximate?{approximate:!0}:{},...snapshot.input_tokens===void 0?{}:{inputTokens:snapshot.input_tokens},...snapshot.output_tokens===void 0?{}:{outputTokens:snapshot.output_tokens},...snapshot.cache_read_input_tokens===void 0?{}:{cacheReadTokens:snapshot.cache_read_input_tokens},...snapshot.cache_creation_input_tokens===void 0?{}:{cacheCreationTokens:snapshot.cache_creation_input_tokens}}},quotaUsage:(vendor)=>engineEntry(vendor).quotaUsage?.()??Promise.resolve(null),vendorsWithQuotaProbe,deliverPromptToLiveEngine:deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailed:deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailed:deliverPromptToLiveEngineTabDetailedAdapter,getPersistedString,setPersistedString,getSavedRepos:()=>[...getSavedRepos()],prStatus:{viewFields:GH_PR_VIEW_FIELDS,mapView:(view,at)=>mapGhPrView(view,at),sameStatus:samePrStatus,nextPoll:(outcome,failures,now,config,random)=>nextPrPoll(outcome,failures,now,config,random),classify:classifyGhFailure}};import{homedir}from"os";async function createRoveCore(options={}){let homeDir=options.homeDir??readRoveHomeDirEnv()??homedir(),store=new TaskIndexStore({homeDir});await store.load();let worktrees=new GitWorktreeManager,orchestrator=new Orchestrator({store,worktrees,onSalvage:(taskId,salvage,repo)=>auditDeletionSalvaged(String(taskId),salvage.ref,salvage.commit,repo,salvage.uncaptured),onWorktreeResidue:(taskId,residue)=>auditDeletionResidue(String(taskId),residue.path,residue.reason),onBranchKept:(taskId,kept)=>auditDeletionBranchKept(String(taskId),kept.branch,kept.reason),tearDownSession:(taskId)=>tearDownTaskSessionAdapter(String(taskId),teardownScope(store.list(),String(taskId),new Set([String(taskId)])))}),backfilled=backfillSavedReposFromProjects(store.list().filter((task)=>task.kind==="main").map((task)=>task.repo));if(backfilled.length>0)console.error(`[rove] added ${backfilled.length} existing project(s) to the new-task picker`);return{homeDir,orchestrator,store,worktrees,async close(){orchestrator.dispose()}}}import{existsSync,readFileSync as readFileSync2,readdirSync,statSync as statSync2,unlinkSync}from"fs";import{join as join2}from"path";var TMP_MAX_AGE_MS=300000;function sweepIndexLeftovers(stateDir,now=Date.now()){let tmp=[],tmpBytes=0,entries;try{entries=readdirSync(stateDir)}catch{return{tmp,tmpBytes,lock:!1}}for(let name of entries){if(!name.startsWith("tasks.json.")||!name.endsWith(".tmp"))continue;let path=join2(stateDir,name);try{let stat=statSync2(path);if(now-stat.mtimeMs<TMP_MAX_AGE_MS)continue;unlinkSync(path),tmp.push(path),tmpBytes+=stat.size}catch{}}return{tmp,tmpBytes,lock:clearStaleLock(join2(stateDir,"tasks.json.lock"))}}function clearStaleLock(lockPath){let holder;try{holder=readFileSync2(lockPath,"utf8").trim()}catch{return!1}let pid=Number.parseInt(holder,10);if(Number.isFinite(pid)&&pid>0&&isProcessAlive(pid))return!1;return releaseSync(lockPath,holder),!existsSync(lockPath)}var CLI_NAME=ROVE_PRODUCT_NAME;function printDaemonUsage(out){out.write([`Usage: ${CLI_NAME} daemon <command>`,"","Commands:"," status Print the running daemon's status JSON (default)"," start Run the daemon in the foreground (this process becomes it)"," --resident: never idle-stop when the last TUI leaves (headless host)"," stop Ask the running daemon to shut down"," restart Stop the daemon (graceful \u2192 SIGTERM \u2192 SIGKILL) and respawn it",""].join(`
6
+ `).find((l)=>l.startsWith("branch "))?.slice(7).trim().replace(/^refs\/heads\//,"")||null}function readRefSymbolic(dirs,ref){for(let dir of[dirs.gitDir,dirs.commonDir])try{let target=readFileSync(join(dir,ref),"utf8").trim().match(/^ref:\s*(.+)$/)?.[1]?.trim();if(target)return target}catch{}return null}function ladderFromFiles(worktreePath){let dirs=resolveGitDirs(worktreePath);if(!dirs)return;let originHead=readRefSymbolic(dirs,"refs/remotes/origin/HEAD")?.replace(/^refs\/remotes\//,""),baseCheckout=readRefSymbolic({gitDir:dirs.commonDir,commonDir:dirs.commonDir},"HEAD")?.replace(/^refs\/heads\//,""),names=[],head=readHeadSha(dirs),parts=[`HEAD=${head}`];for(let guess of[...originHead?[originHead]:[],...DEFAULT_BASE_REF_CANDIDATES,...baseCheckout?[baseCheckout]:[]]){if(names.includes(guess))continue;let sha=readRefSha(dirs,guess);if(sha===null)continue;names.push(guess),parts.push(`${guess}=${sha}`)}return{names,fingerprint:head===null?null:parts.join(",")}}async function ladderFromGit(worktreePath,signal){let head=await git2(worktreePath,["symbolic-ref","--short","refs/remotes/origin/HEAD"],signal),originHead=head.status===0?head.stdout.trim():"",baseCheckout=await baseCheckoutBranchFromGit(worktreePath,signal),names=[],candidates=[...originHead?[originHead]:[],...DEFAULT_BASE_REF_CANDIDATES,...baseCheckout?[baseCheckout]:[]];for(let guess of candidates){if(names.includes(guess))continue;if(await refExists(worktreePath,guess,signal))names.push(guess)}return{names,fingerprint:null}}async function resolveBaseRefCached(worktreePath,recordedBaseRef,signal,now=Date.now()){if(recordedBaseRef){let dirs=resolveGitDirs(worktreePath);if(dirs&&readRefSha(dirs,recordedBaseRef)!==null)return recordedBaseRef;if(await refExists(worktreePath,recordedBaseRef,signal))return recordedBaseRef}let hit=cache.get(worktreePath);if(hit&&now-hit.at<BASE_REF_TTL_MS)return hit.ref;let ladder=ladderFromFiles(worktreePath)??await ladderFromGit(worktreePath,signal);if(hit&&ladder.fingerprint!==null&&hit.fingerprint===ladder.fingerprint)return cache.set(worktreePath,{...hit,at:now}),hit.ref;let ref=null;for(let candidate of ladder.names)if(await sharesHistory(worktreePath,candidate,signal)){ref=candidate;break}return cache.set(worktreePath,{ref,at:now,fingerprint:ladder.fingerprint}),ref}var cache2=new Map;function readShas(worktreePath,baseRef){let dirs=resolveGitDirs(worktreePath);if(!dirs)return null;let head=readHeadSha(dirs),base=readRefSha(dirs,baseRef);return head&&base?{head,base}:null}async function driftCached(worktreePath,baseRef,compute){let shas=readShas(worktreePath,baseRef),hit=shas?cache2.get(worktreePath):void 0;if(shas&&hit&&hit.head===shas.head&&hit.base===shas.base)return hit.value;let value=await compute();if(shas&&value!==null)cache2.set(worktreePath,{...shas,value});return value}import{statSync}from"fs";function judgeWorktree(signals,nowMs){if(signals.dirty)return{verdict:"active",reason:"dirty"};if(signals.prState==="open")return{verdict:"active",reason:"prOpen"};if(signals.prState==="merged")return{verdict:"merged",reason:"prMerged"};if(signals.aheadOfDefault===0)return{verdict:"merged",reason:"inMain"};if(signals.prState==="closed")return{verdict:"stale",reason:"prClosed"};if(signals.lastActivityMs>0&&nowMs-signals.lastActivityMs>1209600000)return{verdict:"stale",reason:"idle"};return{verdict:"fresh",reason:"fresh"}}function parseGhPrList(stdout){let entries;try{entries=JSON.parse(stdout)}catch{return null}if(!Array.isArray(entries))return null;let rank={open:3,merged:2,closed:1},map=new Map;for(let entry of entries){let branch=entry?.headRefName,rawState=entry?.state;if(typeof branch!=="string"||typeof rawState!=="string")continue;let state=rawState.toLowerCase();if(state!=="open"&&state!=="merged"&&state!=="closed")continue;let cur=map.get(branch);if(!cur||rank[state]>rank[cur])map.set(branch,state)}return map}var TIMEOUT_MS=4000,manager=new GitWorktreeManager;function createdAt(path){try{let stat=statSync(path);return stat.birthtimeMs||stat.mtimeMs}catch{return 0}}async function run(path,argv){return execHostForWorktreePath(path).run(argv,{cwd:path,signal:AbortSignal.timeout(TIMEOUT_MS)})}async function branchOnRemote(path,branch){try{let out=await run(path,["git","ls-remote","--exit-code","--heads","origin",branch]);return out.exitCode===0?!0:out.exitCode===2?!1:null}catch{return null}}async function defaultRef(repo){try{let out=await run(repo,["git","symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(out.exitCode===0&&out.stdout.trim())return out.stdout.trim()}catch{}for(let guess of["origin/main","origin/master"])try{if((await run(repo,["git","rev-parse","--verify","--quiet",guess])).exitCode===0)return guess}catch{}return null}async function prStates(repo){try{let origin=await run(repo,["git","remote","get-url","origin"]);if(origin.exitCode!==0||!origin.stdout.includes("github.com"))return null;let out=await run(repo,["gh","pr","list","--state","all","--limit","200","--json","headRefName,state"]);return out.exitCode===0?parseGhPrList(out.stdout):null}catch{return null}}async function ahead(path,ref){if(!ref)return null;try{let out=await run(path,["git","rev-list","--count",`${ref}..HEAD`]),value=Number.parseInt(out.stdout.trim(),10);return out.exitCode===0&&!Number.isNaN(value)?value:null}catch{return null}}async function listWorktreeProjectsAdapter(network){let repos=getSavedRepos().filter((repo)=>!getRemoteRepoConfig(repo)),now=Date.now();return Promise.all(repos.map(async(repo)=>{let[worktrees,states,ref]=await Promise.all([manager.listAll(repo),network?prStates(repo):null,defaultRef(repo)]),rows=await Promise.all(worktrees.map(async(worktree)=>{let[remote,aheadBy]=await Promise.all([network?branchOnRemote(worktree.path,worktree.branch):null,ahead(worktree.path,ref)]),judgement=judgeWorktree({dirty:worktree.dirty===!0,prState:states?.get(worktree.branch)??null,aheadOfDefault:aheadBy,lastActivityMs:worktree.lastActivityMs},now);return{...worktree,repo,createdAtMs:createdAt(worktree.path),branchOnRemote:remote,verdict:judgement.verdict,verdictReason:judgement.reason}}));return{repo,worktrees:rows}}))}async function listUnreadableWorktreesAdapter(repo){return manager.listUnreadableWorktrees(repo)}async function removeWorktreeAdapter(path,force){let residue=null;return await manager.remove(path,{force,onSalvage:(record)=>{if(record)auditWorktreeSalvaged(path,record.ref,record.commit,record.uncaptured)},onResidue:(r)=>{residue={path:r.path,reason:r.reason},auditWorktreeResidue(r.path,r.reason)}}),residue}function resolveProtocolUpgradeAndLearnPreset(task,evidence){let preset=protocolWriteBackFromLiveSession(task,evidence,getCustomEngineIds());if(preset)setPersistedString(engineProtocolKey(preset.id),preset.protocol);return protocolUpgradeFromLiveSession(task,evidence)}var daemonRuntime={currentVersion:CURRENT_VERSION,tabsPersistence:stateFileTabsPersistence,startMachineHub(bus){let stop=null,stopped=!1;return import("./chunk-ccmke5sx.js").then(({startDaemonMachineHub})=>{if(!stopped)stop=startDaemonMachineHub(bus)}),()=>{stopped=!0,stop?.()}},async invokeApi(verb,params,socketPath,route){let[{executeVerb,errorEnvelope},{daemonCaller},{RoveDaemonClient:RoveDaemonClient2}]=await Promise.all([import("./chunk-vpsarakj.js"),import("./chunk-34tbcjdy.js"),import("./chunk-fq2pefc5.js")]);if(route?.forwarded){let{inboundRefusal}=await import("./chunk-37fas9aj.js"),denied=inboundRefusal(verb);if(denied){let{ApiError}=await import("./chunk-34tbcjdy.js");return errorEnvelope(new ApiError(`this machine refuses "${denied}" verbs from other machines (${verb} from ${route.from??"a hub"})`,"INBOUND_DENIED",{hint:`on this machine: rove machine inbound allow ${denied}`}))}}let client=new RoveDaemonClient2(socketPath);try{await client.connect();let{invokeRouted}=await import("./chunk-j9tsdn1d.js");return await invokeRouted({verb,params,machine:route?.machine,forwarded:route?.forwarded,localTasks:()=>client.request("task.list").then((res)=>res.tasks??[]),isLocalTask:(id)=>client.request("task.get",{taskId:id}).then(()=>!0,()=>!1),runHere:()=>executeVerb(verb,{params},{client,caller:daemonCaller(route?.forwarded===!0)})})}catch(err){return errorEnvelope(err)}finally{client.close()}},defaultTaskVendor:DEFAULT_TASK_VENDOR,placeholderTaskTitle:PLACEHOLDER_TASK_TITLE,isTaskStatus,isEngineActivityKind,affectsActivityState,async foregroundEngines(pids){let rows=parsePsSnapshot(await psSnapshot([...pids])),out=new Map;for(let pid of pids){let found=foregroundEngineIn(rows,pid);out.set(pid,found?{vendor:found.vendor,pid:found.pid}:null)}return out},titleTurnHint:(vendor,title)=>engineTitleTurnHint(sessionProtocol(vendor),title),sessionProtocol,resolveProtocolUpgrade:resolveProtocolUpgradeAndLearnPreset,readEngineTurns:async(vendor,transcriptPath)=>await protocolEntry(vendor).readTurns?.(transcriptPath)??[],checkLatestVersion:()=>{let state=loadStateFile();return checkLatestVersion({channel:preferredReleaseChannel(state),minAgeHours:updateDelayHours(state)})},latestTranscriptMtime,deriveTitleFromSession,deriveTitleFromSessionId,createEngineTurnDetector:(vendor)=>createEngineTurnDetector(sessionProtocol(vendor)),async runWorktreeStatus(worktreePath,signal,baseRef){let result=await spawnCapture("git",["status","--porcelain=v1"],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal});if(result.status!==0)throw Error("git status failed");let counts=parsePorcelain(result.stdout),base=await resolveBaseRefCached(worktreePath,baseRef,signal);if(!base)return counts;let drift=await driftCached(worktreePath,base,async()=>{let out=await spawnCapture("git",["rev-list","--left-right","--count",`${base}...HEAD`],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal});return out.status===0?parseAheadBehind(out.stdout):null});return drift===null?counts:{...counts,...drift}},maybeAutoStart:(orch,taskId)=>maybeAutoStart(orch,taskId),listWorktreeProjects:listWorktreeProjectsAdapter,listUnreadableWorktrees:listUnreadableWorktreesAdapter,removeWorktree:removeWorktreeAdapter,async syncWorktreeWithBase(worktreePath,recordedBaseRef){let controller=new AbortController,timer=setTimeout(()=>controller.abort(),SYNC_TIMEOUT_MS);try{let baseRef=await resolveBaseRefCached(worktreePath,recordedBaseRef,controller.signal);if(!baseRef)throw Error("no base ref resolves for this worktree");return await syncWorktreeWithBase(worktreePath,baseRef,controller.signal)}finally{clearTimeout(timer)}},availableEngineIds,engineDisplayName,roveApiInvocation,engineEntry,ensureTaskSession:ensureTaskSessionAdapter,startTaskSessionWithPrompt:startTaskSessionWithPromptAdapter,tearDownTaskSession:tearDownTaskSessionAdapter,async readEngineContextUsage(vendor,sessionId){let read=protocolEntry(vendor).history.readUsageSnapshot;if(!read)return null;let snapshot=await read(sessionId);if(snapshot?.context_tokens===void 0)return null;return{contextTokens:snapshot.context_tokens,...snapshot.context_window_tokens===void 0?{}:{contextWindowTokens:snapshot.context_window_tokens},...snapshot.context_tokens_approximate?{approximate:!0}:{},...snapshot.input_tokens===void 0?{}:{inputTokens:snapshot.input_tokens},...snapshot.output_tokens===void 0?{}:{outputTokens:snapshot.output_tokens},...snapshot.cache_read_input_tokens===void 0?{}:{cacheReadTokens:snapshot.cache_read_input_tokens},...snapshot.cache_creation_input_tokens===void 0?{}:{cacheCreationTokens:snapshot.cache_creation_input_tokens}}},quotaUsage:(vendor)=>engineEntry(vendor).quotaUsage?.()??Promise.resolve(null),vendorsWithQuotaProbe,deliverPromptToLiveEngine:deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailed:deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailed:deliverPromptToLiveEngineTabDetailedAdapter,getPersistedString,buildCIPrompt:(worktree,state)=>buildCIPromptForWorktree(worktree,state),setPersistedString,getSavedRepos:()=>[...getSavedRepos()],prStatus:{viewFields:GH_PR_VIEW_FIELDS,mapView:(view,at)=>mapGhPrView(view,at),sameStatus:samePrStatus,nextPoll:(outcome,failures,now,config,random)=>nextPrPoll(outcome,failures,now,config,random),classify:classifyGhFailure}};import{homedir}from"os";async function createRoveCore(options={}){let homeDir=options.homeDir??readRoveHomeDirEnv()??homedir(),store=new TaskIndexStore({homeDir});await store.load();let worktrees=new GitWorktreeManager,orchestrator=new Orchestrator({store,worktrees,onSalvage:(taskId,salvage,repo)=>auditDeletionSalvaged(String(taskId),salvage.ref,salvage.commit,repo,salvage.uncaptured),onWorktreeResidue:(taskId,residue)=>auditDeletionResidue(String(taskId),residue.path,residue.reason),onBranchKept:(taskId,kept)=>auditDeletionBranchKept(String(taskId),kept.branch,kept.reason),tearDownSession:(taskId)=>tearDownTaskSessionAdapter(String(taskId),teardownScope(store.list(),String(taskId),new Set([String(taskId)])))}),backfilled=backfillSavedReposFromProjects(store.list().filter((task)=>task.kind==="main").map((task)=>task.repo));if(backfilled.length>0)console.error(`[rove] added ${backfilled.length} existing project(s) to the new-task picker`);return{homeDir,orchestrator,store,worktrees,async close(){orchestrator.dispose()}}}import{existsSync,readFileSync as readFileSync2,readdirSync,statSync as statSync2,unlinkSync}from"fs";import{join as join2}from"path";var TMP_MAX_AGE_MS=300000;function sweepIndexLeftovers(stateDir,now=Date.now()){let tmp=[],tmpBytes=0,entries;try{entries=readdirSync(stateDir)}catch{return{tmp,tmpBytes,lock:!1}}for(let name of entries){if(!name.startsWith("tasks.json.")||!name.endsWith(".tmp"))continue;let path=join2(stateDir,name);try{let stat=statSync2(path);if(now-stat.mtimeMs<TMP_MAX_AGE_MS)continue;unlinkSync(path),tmp.push(path),tmpBytes+=stat.size}catch{}}return{tmp,tmpBytes,lock:clearStaleLock(join2(stateDir,"tasks.json.lock"))}}function clearStaleLock(lockPath){let holder;try{holder=readFileSync2(lockPath,"utf8").trim()}catch{return!1}let pid=Number.parseInt(holder,10);if(Number.isFinite(pid)&&pid>0&&isProcessAlive(pid))return!1;return releaseSync(lockPath,holder),!existsSync(lockPath)}var CLI_NAME=ROVE_PRODUCT_NAME;function printDaemonUsage(out){out.write([`Usage: ${CLI_NAME} daemon <command>`,"","Commands:"," status Print the running daemon's status JSON (default)"," start Run the daemon in the foreground (this process becomes it)"," --resident: never idle-stop when the last TUI leaves (headless host)"," stop Ask the running daemon to shut down"," restart Stop the daemon (graceful \u2192 SIGTERM \u2192 SIGKILL) and respawn it",""].join(`
7
7
  `))}async function runDaemonSubcommand(argv){let[command="status",...flags]=argv,socketPath=defaultDaemonSocketPath(),pidPath=defaultDaemonPidPath();if(command==="--help"||command==="-h"||command==="help"){printDaemonUsage(process.stdout);return}if(!SUBCOMMAND_VERBS.daemon.includes(command))process.stderr.write(`${CLI_NAME} daemon: unknown command "${command}"
8
8
 
9
- `),printDaemonUsage(process.stderr),process.exit(2);if(command==="relay"){let pty=argvHasFlag(flags,"--pty"),{ensureDaemonReachable}=await import("./chunk-3v9s3bcz.js"),{ensurePtyHostReachable}=await import("./chunk-78v1q5mz.js"),target=pty?await ensurePtyHostReachable():await ensureDaemonReachable(void 0,"autospawn"),{connect}=await import("net"),socket=connect(target);if(argvHasFlag(flags,"--preamble"))process.stdout.write(RELAY_PREAMBLE);process.stdin.pipe(socket),socket.pipe(process.stdout),await new Promise((resolve)=>{socket.on("close",()=>resolve()),process.stdin.on("end",()=>socket.end())}),process.exit(0)}let badFlag=flags.find((flag)=>!(command==="start"&&argvHasFlag([flag],"--resident"))&&!(command==="status"&&flag==="--json")&&!(command==="relay"&&flag==="--pty")),resident=switchFlag(flags,"--resident");if(badFlag!==void 0||resident===void 0)process.stderr.write(`${CLI_NAME} daemon ${command}: bad option "${badFlag??flags.join(" ")}"
9
+ `),printDaemonUsage(process.stderr),process.exit(2);if(command==="relay"){let pty=argvHasFlag(flags,"--pty"),{ensureDaemonReachable}=await import("./chunk-n2zgej1a.js"),{ensurePtyHostReachable}=await import("./chunk-bagwgxsg.js"),target=pty?await ensurePtyHostReachable():await ensureDaemonReachable(void 0,"autospawn"),{connect}=await import("net"),socket=connect(target);if(argvHasFlag(flags,"--preamble"))process.stdout.write(RELAY_PREAMBLE);process.stdin.pipe(socket),socket.pipe(process.stdout),await new Promise((resolve)=>{socket.on("close",()=>resolve()),process.stdin.on("end",()=>socket.end())}),process.exit(0)}let badFlag=flags.find((flag)=>!(command==="start"&&argvHasFlag([flag],"--resident"))&&!(command==="status"&&flag==="--json")&&!(command==="relay"&&flag==="--pty")),resident=switchFlag(flags,"--resident");if(badFlag!==void 0||resident===void 0)process.stderr.write(`${CLI_NAME} daemon ${command}: bad option "${badFlag??flags.join(" ")}"
10
10
 
11
- `),printDaemonUsage(process.stderr),process.exit(2);if(command==="status"){let client=new RoveDaemonClient(socketPath);try{let status=await client.request("daemon.status");console.log(JSON.stringify({...status,relayPreamble:!0},null,2))}catch{let pid=await readPidFile(pidPath);if(pid)console.log(`${CLI_NAME} daemon: no daemon socket at ${socketPath} (stale pidfile pid=${pid})`);else console.log(`${CLI_NAME} daemon: no daemon running at ${socketPath}`);process.exitCode=1}finally{client.close()}return}if(command==="stop"){let client=new RoveDaemonClient(socketPath);try{await client.request("daemon.stop"),console.log(`${CLI_NAME} daemon: stop requested`)}catch{console.log(`${CLI_NAME} daemon: no daemon running at ${socketPath}`)}finally{client.close()}return}if(command==="restart"){await stopDaemonProcess(socketPath,pidPath,{reason:"restart"}),(await connectOrStartDaemon("explicit-restart")).close(),console.log(`${CLI_NAME} daemon: restarted, listening on ${socketPath}`);return}rotateLogIfNeeded(defaultDaemonLogPath()),installDaemonCrashHandlers(),logDaemonInfo("boot",`daemon starting \u2014 ${daemonSpawnReason()} (pid ${process.pid}, v${CURRENT_VERSION})`);let core,channels=[],server=await startDaemonServer(async()=>{let migration=migrateRoveDaemonStateLayout();for(let warning of migration.warnings)console.error(`[rove] daemon state migration will retry: ${warning}`);core=await createRoveCore();let swept=sweepIndexLeftovers(core.store.stateDir);if(swept.lock||swept.tmp.length>0)console.error(`[rove] swept crash leftovers in ${core.store.stateDir}: ${swept.tmp.length} orphaned staging file(s) (${swept.tmpBytes} bytes)${swept.lock?", stale task-index lockfile":""}`);return core.orchestrator},{runtime:daemonRuntime,socketPath,pidPath,plugins:{binPath:resolvePluginBinPath()},keepAlive:()=>channels.some((channel)=>channel.keepAlive()),resident,onStop:async()=>{cancelAllLogins();for(let channel of channels)channel.stop();await core?.close()}});console.log(`${CLI_NAME} daemon: listening on ${server.socketPath}`),channels=await startChatChannels({socketPath:server.socketPath,log:logDaemonInfo,keepAliveChanged:()=>server.reevaluateIdle()});let shutdown=async()=>{await server.close(),process.exit(0)};process.once("SIGINT",()=>void shutdown()),process.once("SIGTERM",()=>void shutdown())}export{runDaemonSubcommand};
11
+ `),printDaemonUsage(process.stderr),process.exit(2);if(command==="status"){let client=new RoveDaemonClient(socketPath);try{let status=await client.request("daemon.status");console.log(JSON.stringify({...status,relayPreamble:!0},null,2))}catch{let pid=await readPidFile(pidPath);if(pid)console.log(`${CLI_NAME} daemon: no daemon socket at ${socketPath} (stale pidfile pid=${pid})`);else console.log(`${CLI_NAME} daemon: no daemon running at ${socketPath}`);process.exitCode=1}finally{client.close()}return}if(command==="stop"){let client=new RoveDaemonClient(socketPath);try{await client.request("daemon.stop"),console.log(`${CLI_NAME} daemon: stop requested`)}catch{console.log(`${CLI_NAME} daemon: no daemon running at ${socketPath}`)}finally{client.close()}return}if(command==="restart"){await stopDaemonProcess(socketPath,pidPath,{reason:"restart"}),(await connectOrStartDaemon("explicit-restart")).close(),console.log(`${CLI_NAME} daemon: restarted, listening on ${socketPath}`);return}rotateLogIfNeeded(defaultDaemonLogPath()),installDaemonCrashHandlers(),logDaemonInfo("boot",`daemon starting \u2014 ${daemonSpawnReason()} (pid ${process.pid}, v${CURRENT_VERSION})`);let core,channels=[],server=await startDaemonServer(async()=>{let migration=migrateRoveDaemonStateLayout();for(let warning of migration.warnings)console.error(`[rove] daemon state migration will retry: ${warning}`);core=await createRoveCore();let swept=sweepIndexLeftovers(core.store.stateDir);if(swept.lock||swept.tmp.length>0)console.error(`[rove] swept crash leftovers in ${core.store.stateDir}: ${swept.tmp.length} orphaned staging file(s) (${swept.tmpBytes} bytes)${swept.lock?", stale task-index lockfile":""}`);return core.orchestrator},{runtime:daemonRuntime,socketPath,pidPath,plugins:{binPath:resolvePluginBinPath()},keepAlive:()=>channels.some((channel)=>channel.keepAlive()),resident,onStop:async()=>{cancelAllLogins();for(let channel of channels)channel.stop();await core?.close()}});console.log(`${CLI_NAME} daemon: listening on ${server.socketPath}`);try{for(let{target,error}of refreshOutdatedSkills())if(error)logDaemonError("skill-refresh",Error(`${target.dir}: ${error}`));else logDaemonInfo("skill-refresh",`refreshed ${target.dir}`)}catch(err){logDaemonError("skill-refresh",err)}channels=await startChatChannels({socketPath:server.socketPath,log:logDaemonInfo,keepAliveChanged:()=>server.reevaluateIdle()});let shutdown=async()=>{await server.close(),process.exit(0)};process.once("SIGINT",()=>void shutdown()),process.once("SIGTERM",()=>void shutdown())}export{runDaemonSubcommand};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{Orchestrator}from"./chunk-bv050ssb.js";import"./chunk-1wwqf6kx.js";import"./chunk-c1dz3qg3.js";import"./chunk-3x5g06mw.js";import"./chunk-4dt1gfb3.js";import"./chunk-xmwp6je4.js";import{PLACEHOLDER_TASK_TITLE}from"./chunk-2xsxcdp6.js";import"./chunk-1vxtd8rh.js";import"./chunk-gq36nzg8.js";import"./chunk-mqfvzdpr.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-895yqjjz.js";import"./chunk-sak1tphp.js";import"./chunk-y1xnjx8x.js";import"./chunk-2tfnscxk.js";import"./chunk-9rj2fx6v.js";import"./chunk-rfp0ayra.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-40dgv828.js";import"./chunk-6xppe9bj.js";import"./chunk-2brn8e1y.js";import"./chunk-7mwvvj46.js";import"./chunk-32j6y5d1.js";import"./chunk-cmp64q4j.js";import"./chunk-7kysxgbq.js";import"./chunk-x1jxxmtc.js";import"./chunk-xy8be792.js";import"./chunk-h2m7em1e.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{PLACEHOLDER_TASK_TITLE,Orchestrator};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{MachineHub}from"./chunk-fs51wx31.js";import"./chunk-4d1v6066.js";import"./chunk-6xr1prk6.js";import"./chunk-atn06ncf.js";import"./chunk-zaw71cf4.js";import"./chunk-sq6zj3fb.js";import"./chunk-y1xnjx8x.js";import"./chunk-55ajzrj6.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-6xppe9bj.js";import"./chunk-387ecqb8.js";import"./chunk-kgs56yns.js";import"./chunk-4hnz77xf.js";import"./chunk-7kysxgbq.js";import"./chunk-xy8be792.js";import"./chunk-w9cdc97q.js";import"./chunk-40dgv828.js";import"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import"./chunk-z1nj22va.js";import"./chunk-br8r72bf.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-cmp64q4j.js";import"./chunk-sza3tyhs.js";import"./chunk-96kvwzj8.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";var PUBLISH_DEBOUNCE_MS=50;function startDaemonMachineHub(bus){let hub=new MachineHub,timer=null,publish=()=>{timer=null;let live=hub.liveSignal().get(),payload={machines:hub.machinesSignal().get(),tasks:hub.tasksSignal().get().map((task)=>({...task,kind:task.kind??"task",pinned:task.pinned??!1})),live:{activity:Object.fromEntries(live.activity),changes:Object.fromEntries(live.changes),tokens:Object.fromEntries(live.tokens)}};bus.publish("machines",payload)},schedule=()=>{timer??=setTimeout(publish,PUBLISH_DEBOUNCE_MS)},stops=[hub.machinesSignal(),hub.tasksSignal(),hub.liveSignal()].map((signal)=>signal.subscribe(schedule)),lastRegistry=null,offState=bus.onPublish((event)=>{if(event.channel!=="state")return;let state=event.payload.state,text=JSON.stringify(state.machines??null);if(text===lastRegistry)return;lastRegistry=text,hub.sync()});return hub.sync(),publish(),()=>{offState();for(let stop of stops)stop();if(timer)clearTimeout(timer);hub.dispose(),bus.publish("machines",{machines:[],tasks:[],live:{activity:{},changes:{},tokens:{}}})}}export{startDaemonMachineHub};
2
+ import{MachineHub}from"./chunk-qs359899.js";import"./chunk-v6v272b3.js";import"./chunk-6xr1prk6.js";import"./chunk-atn06ncf.js";import"./chunk-vytrhm50.js";import"./chunk-sq6zj3fb.js";import"./chunk-y1xnjx8x.js";import"./chunk-e58kj4gm.js";import"./chunk-0a65j1zj.js";import"./chunk-bcyex84w.js";import"./chunk-0r1jf8m9.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-40dgv828.js";import"./chunk-6xppe9bj.js";import"./chunk-9ckvdy0j.js";import"./chunk-32j6y5d1.js";import"./chunk-cmp64q4j.js";import"./chunk-qzzmd63x.js";import"./chunk-96kvwzj8.js";import"./chunk-7kysxgbq.js";import"./chunk-xy8be792.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";var PUBLISH_DEBOUNCE_MS=50;function startDaemonMachineHub(bus){let hub=new MachineHub,timer=null,publish=()=>{timer=null;let live=hub.liveSignal().get(),payload={machines:hub.machinesSignal().get(),tasks:hub.tasksSignal().get().map((task)=>({...task,kind:task.kind??"task",pinned:task.pinned??!1})),live:{activity:Object.fromEntries(live.activity),changes:Object.fromEntries(live.changes),tokens:Object.fromEntries(live.tokens)}};bus.publish("machines",payload)},schedule=()=>{timer??=setTimeout(publish,PUBLISH_DEBOUNCE_MS)},stops=[hub.machinesSignal(),hub.tasksSignal(),hub.liveSignal()].map((signal)=>signal.subscribe(schedule)),lastRegistry=null,offState=bus.onPublish((event)=>{if(event.channel!=="state")return;let state=event.payload.state,text=JSON.stringify(state.machines??null);if(text===lastRegistry)return;lastRegistry=text,hub.sync()});return hub.sync(),publish(),()=>{offState();for(let stop of stops)stop();if(timer)clearTimeout(timer);hub.dispose(),bus.publish("machines",{machines:[],tasks:[],live:{activity:{},changes:{},tokens:{}}})}}export{startDaemonMachineHub};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{BUNDLED_THEME_JSONS,parseRgbLiteral}from"./chunk-kxp04jnp.js";import{DEFAULT_LOCALE,isLocaleId}from"./chunk-wzss4pzh.js";import{kvStatePath}from"./chunk-z1nj22va.js";import{RGBA as RGBA3}from"@opentui/core";import{RGBA}from"@opentui/core";var BREATH_TICKS=30,SPEED_MIN=0.2,SPEED_SWING=0.9333333333333333,INK_FLOOR=0.3;function breathLevel(tick){return 0.5-0.5*Math.cos(2*Math.PI*tick/BREATH_TICKS)}function breathPhase(tick){let wave=tick/2-BREATH_TICKS/(4*Math.PI)*Math.sin(2*Math.PI*tick/BREATH_TICKS);return SPEED_MIN*tick+SPEED_SWING*wave}function breathGlyph(frames,tick){let index=Math.floor(breathPhase(tick)+0.000000001)%frames.length;return frames[index]??frames[0]??""}function mixInk(accent,muted,share){if(accent.intent!=="rgb"||muted.intent!=="rgb")return accent;let[ar,ag,ab,aa]=accent.toInts(),[mr,mg,mb]=muted.toInts(),mix=(a,m)=>Math.round(m+(a-m)*share);return RGBA.fromInts(mix(ar,mr),mix(ag,mg),mix(ab,mb),aa)}function breathColor(accent,muted,tick){return mixInk(accent,muted,INK_FLOOR+(1-INK_FLOOR)*breathLevel(tick))}var toLinear=(v)=>v<=0.04045?v/12.92:((v+0.055)/1.055)**2.4,toGamma=(v)=>v<=0.0031308?12.92*v:1.055*v**0.4166666666666667-0.055;function toOklch(color){let[r,g,b]=color.toInts().map((v)=>toLinear(v/255)),l=Math.cbrt(0.4122214708*r+0.5363325363*g+0.0514459929*b),m=Math.cbrt(0.2119034982*r+0.6806995451*g+0.1073969566*b),s=Math.cbrt(0.0883024619*r+0.2817188376*g+0.6299787005*b),L=0.2104542553*l+0.793617785*m-0.0040720468*s,A=1.9779984951*l-2.428592205*m+0.4505937099*s,B=0.0259040371*l+0.7827717662*m-0.808675766*s;return{l:L,c:Math.hypot(A,B),h:Math.atan2(B,A)}}function linearRgb({l:L,c,h}){let A=c*Math.cos(h),B=c*Math.sin(h),l=(L+0.3963377774*A+0.2158037573*B)**3,m=(L-0.1055613458*A-0.0638541728*B)**3,s=(L-0.0894841775*A-1.291485548*B)**3;return[4.0767416621*l-3.3077115913*m+0.2309699292*s,-1.2684380046*l+2.6097574011*m-0.3413193965*s,-0.0041960863*l-0.7034186147*m+1.707614701*s]}function fromOklch(color,alpha){let ch=(v)=>Math.round(Math.min(1,Math.max(0,toGamma(v)))*255),[r,g,b]=linearRgb(color);return RGBA.fromInts(ch(r),ch(g),ch(b),alpha)}function turnHue(accent,deg,lightness=1,chroma=1){if(accent.intent!=="rgb")return accent;let base=toOklch(accent),turned={l:base.l*lightness,c:base.c*chroma,h:base.h+deg*Math.PI/180};return fromOklch(turned,accent.toInts()[3])}var inGamut=(color)=>linearRgb(color).every((v)=>v>=-0.0001&&v<=1.0001);function inkAtHue(deg,lightness,chroma,alpha=255){let h=deg*Math.PI/180,c=chroma;while(c>0&&!inGamut({l:lightness,c,h}))c-=0.005;return fromOklch({l:lightness,c:Math.max(0,c),h},alpha)}function oklchCusp(deg){let key=Math.round(deg)%360,hit=cuspCache.get(key);if(hit)return hit;let h=key*Math.PI/180,best={l:1,c:0};for(let l=0.2;l<=1;l+=0.01){let lo=0,hi=0.4;for(let i=0;i<16;i++){let mid=(lo+hi)/2;if(inGamut({l,c:mid,h}))lo=mid;else hi=mid}if(lo>best.c)best={l,c:lo}}return cuspCache.set(key,best),best}var cuspCache=new Map;import{RGBA as RGBA2}from"@opentui/core";var HOST_TEXT_MIN_CONTRAST=4.5;function srgbChannelToLinear(c){let v=c/255;return v<=0.04045?v/12.92:((v+0.055)/1.055)**2.4}function relativeLuminance([r,g,b]){return 0.2126*srgbChannelToLinear(r)+0.7152*srgbChannelToLinear(g)+0.0722*srgbChannelToLinear(b)}function contrastRatioTriplet(fg,bg){let l1=relativeLuminance(fg),l2=relativeLuminance(bg),lighter=Math.max(l1,l2),darker=Math.min(l1,l2);return(lighter+0.05)/(darker+0.05)}function rgbToHsl([r,g,b]){let rn=r/255,gn=g/255,bn=b/255,max=Math.max(rn,gn,bn),min=Math.min(rn,gn,bn),l=(max+min)/2;if(max===min)return[0,0,l];let d=max-min,s=l>0.5?d/(2-max-min):d/(max+min),h;if(max===rn)h=((gn-bn)/d+(gn<bn?6:0))*60;else if(max===gn)h=((bn-rn)/d+2)*60;else h=((rn-gn)/d+4)*60;return[h,s,l]}function hslToRgb(h,s,l){if(s===0){let v=Math.round(l*255);return[v,v,v]}let q=l<0.5?l*(1+s):l+s-l*s,p=2*l-q,channel=(t)=>{let tn=t;if(tn<0)tn+=1;if(tn>1)tn-=1;let c=tn<0.16666666666666666?p+(q-p)*6*tn:tn<0.5?q:tn<0.6666666666666666?p+(q-p)*(0.6666666666666666-tn)*6:p;return Math.round(c*255)};return[channel(h/360+0.3333333333333333),channel(h/360),channel(h/360-0.3333333333333333)]}var MID_HOST_LUMINANCE=0.5;function ensureContrast(fg,bg,minRatio=HOST_TEXT_MIN_CONTRAST){let fgInts=fg.toInts(),bgInts=bg.toInts(),fgTriplet=[fgInts[0],fgInts[1],fgInts[2]],bgTriplet=[bgInts[0],bgInts[1],bgInts[2]];if(contrastRatioTriplet(fgTriplet,bgTriplet)>=minRatio)return fg;let[h,s,l]=rgbToHsl(fgTriplet),lighten=relativeLuminance(bgTriplet)<=MID_HOST_LUMINANCE,at=(lightness)=>contrastRatioTriplet(hslToRgb(h,s,lightness),bgTriplet),lo=lighten?l:0,hi=lighten?1:l;for(let i=0;i<32;i++){let mid=(lo+hi)/2;if(at(mid)>=minRatio)if(lighten)hi=mid;else lo=mid;else if(lighten)lo=mid;else hi=mid}let[r,g,b]=hslToRgb(h,s,lighten?hi:lo);return RGBA2.fromInts(r,g,b,fgInts[3])}var BUNDLED_THEMES=BUNDLED_THEME_JSONS,DEFAULT_THEME="claude";function hasBundledTheme(name){return Boolean(BUNDLED_THEMES[name])}var THEME_MODE_PREFERENCES=["dark","light","auto"],DEFAULT_THEME_MODE="dark";function resolveThemeMode(preference,detected){return preference==="auto"?detected??"dark":preference}var FOCUS_ACCENT_SLOTS=["primary","success","info"];function resolveTheme(theme,mode="dark"){let defs=theme.defs??{};function resolve(c,chain=[]){if(typeof c==="string"){if(c==="transparent"||c==="none")return RGBA3.fromInts(0,0,0,0);if(c.startsWith("#"))return RGBA3.fromHex(c);let rgb=parseRgbLiteral(c);if(rgb)return RGBA3.fromInts(rgb.r,rgb.g,rgb.b,rgb.a);if(chain.includes(c))return RGBA3.fromInts(0,0,0);let next=defs[c]??theme.theme[c];if(next===void 0)return RGBA3.fromInts(0,0,0);return resolve(next,[...chain,c])}return resolve(c[mode],chain)}let out={};for(let[k,v]of Object.entries(theme.theme))out[k]=resolve(v);let text=out.text??RGBA3.fromHex("#ffffff"),background=out.background??RGBA3.fromHex("#000000");return{...{primary:out.primary??text,secondary:out.secondary??text,accent:out.accent??out.primary??text,error:out.error??text,warning:out.warning??text,warningOnHost:out.warning??text,success:out.success??text,info:out.info??text,text,textMuted:out.textMuted??text,background,backgroundPanel:out.backgroundPanel??background,backgroundElement:out.backgroundElement??background,backgroundMenu:out.backgroundMenu??out.backgroundElement??background,backgroundDialog:out.backgroundDialog??out.backgroundPanel??background,border:out.border??text,borderActive:out.borderActive??out.border??text,borderSubtle:out.borderSubtle??out.border??text,diffAdded:out.diffAdded??out.success??text,diffRemoved:out.diffRemoved??out.error??text,diffContext:out.diffContext??out.textMuted??text,diffHunkHeader:out.diffHunkHeader??out.textMuted??text,diffAddedBg:out.diffAddedBg??background,diffRemovedBg:out.diffRemovedBg??background,selectedListItemText:out.selectedListItemText??background},...out}}function applyDisplayOverlay(base,focusAccent,transparentBackground,hostBackground){let v={...base,focusAccent:base[focusAccent]??base.primary,warningOnHost:base.warning};if(!transparentBackground)return v;let[backgroundR,backgroundG,backgroundB]=base.background.toInts(),[panelR,panelG,panelB]=base.backgroundPanel.toInts(),transparent={...v,background:RGBA3.fromInts(backgroundR,backgroundG,backgroundB,0),backgroundPanel:RGBA3.fromInts(panelR,panelG,panelB,0)};if(!hostBackground)return transparent;return{...transparent,text:ensureContrast(transparent.text,hostBackground),textMuted:ensureContrast(transparent.textMuted,hostBackground),warningOnHost:ensureContrast(transparent.warning,hostBackground)}}function diffInks(theme,colorblind){let added=(ink)=>colorblind?turnHue(ink,60,1,0.71):ink;return{added:added(theme.diffAdded),removed:theme.diffRemoved,addedBg:added(theme.diffAddedBg),removedBg:theme.diffRemovedBg}}import{readFileSync}from"fs";var LOCALE_KEY="locale";function defaultTransparentBackground(platform=process.platform){return platform!=="win32"}function readPersistedUiPrefs(fallbackTheme,isKnownTheme=hasBundledTheme){try{let parsed=JSON.parse(readFileSync(kvStatePath(),"utf8")),theme=typeof parsed.activeTheme==="string"&&isKnownTheme(parsed.activeTheme)?parsed.activeTheme:fallbackTheme,themeMode=THEME_MODE_PREFERENCES.includes(parsed.themeMode)?parsed.themeMode:DEFAULT_THEME_MODE,transparent=typeof parsed.transparentBackground==="boolean"?parsed.transparentBackground:defaultTransparentBackground(),focusAccent=typeof parsed.focusAccent==="string"&&FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent)?parsed.focusAccent:null,locale=isLocaleId(parsed[LOCALE_KEY])?parsed[LOCALE_KEY]:DEFAULT_LOCALE;return{theme,themeMode,transparent,focusAccent,locale}}catch{return{theme:fallbackTheme,themeMode:DEFAULT_THEME_MODE,transparent:defaultTransparentBackground(),focusAccent:null,locale:DEFAULT_LOCALE}}}
3
+ export{breathGlyph,mixInk,breathColor,toOklch,turnHue,inkAtHue,oklchCusp,relativeLuminance,ensureContrast,BUNDLED_THEMES,DEFAULT_THEME,THEME_MODE_PREFERENCES,DEFAULT_THEME_MODE,resolveThemeMode,FOCUS_ACCENT_SLOTS,resolveTheme,applyDisplayOverlay,diffInks,LOCALE_KEY,defaultTransparentBackground,readPersistedUiPrefs};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{ReleaseNotesBody,bootPaneHost,pageCloseBindings,runShellUpdater,useBindings,useT,useTheme}from"./chunk-rmsdybs3.js";import"./chunk-x85rcep5.js";import{$jsx,$jsxs,useRenderer}from"./chunk-29kvfqeh.js";import{require_react_production}from"./chunk-2wd4744g.js";import"./chunk-v6v272b3.js";import"./chunk-cpgnm1xr.js";import"./chunk-r3s2t9af.js";import"./chunk-6xr1prk6.js";import"./chunk-764bsyb0.js";import"./chunk-xte4phd1.js";import"./chunk-atn06ncf.js";import"./chunk-cf3vpsm5.js";import"./chunk-rxqdw6s5.js";import"./chunk-kxp04jnp.js";import"./chunk-mdc7b01e.js";import"./chunk-tn9gtf18.js";import"./chunk-e58kj4gm.js";import"./chunk-0a65j1zj.js";import"./chunk-bcyex84w.js";import"./chunk-0r1jf8m9.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-40dgv828.js";import"./chunk-6xppe9bj.js";import{BREAKING_VERSIONS,CURRENT_VERSION,breakingVersionsCrossed,fetchReleaseNotes,fetchReleaseSummaries,updateCommandFor}from"./chunk-9ckvdy0j.js";import"./chunk-2r1fgng9.js";import"./chunk-2brn8e1y.js";import"./chunk-32j6y5d1.js";import{currentLang}from"./chunk-mjyhy152.js";import"./chunk-wzss4pzh.js";import"./chunk-cmp64q4j.js";import"./chunk-qzzmd63x.js";import"./chunk-96kvwzj8.js";import"./chunk-xy8be792.js";import"./chunk-7ghwdjdc.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import{__toESM}from"./chunk-91sykzqq.js";import{TextAttributes}from"@opentui/core";var import_react2=__toESM(require_react_production(),1);var RELEASE_LIMIT=20;function VersionsPage(props){let{theme}=useTheme(),t=useT(),renderer=useRenderer(),[releases,setReleases]=import_react2.useState(null),[cursor,setCursor]=import_react2.useState(0),[notes,setNotes]=import_react2.useState({}),[status,setStatus]=import_react2.useState(null),selected=releases?.[cursor],crossings=selected?breakingVersionsCrossed(CURRENT_VERSION,selected.version):[],loadSummaries=props.fetchSummaries??fetchReleaseSummaries,loadNotes=props.fetchNotes??fetchReleaseNotes;import_react2.useEffect(()=>{loadSummaries(RELEASE_LIMIT).then((fetched)=>setReleases(fetched))},[loadSummaries]);let selectedVersion=selected?.version;import_react2.useEffect(()=>{if(selectedVersion===void 0||notes[selectedVersion]!==void 0)return;setNotes((cache)=>({...cache,[selectedVersion]:"loading"})),loadNotes(selectedVersion,currentLang()).then((fetched)=>{setNotes((cache)=>({...cache,[selectedVersion]:fetched??"missing"}))})},[selectedVersion,notes,loadNotes]);function move(delta){let count=releases?.length??0;if(count===0)return;setCursor((index)=>(index+delta+count)%count)}async function installSelected(){if(!selected)return;setStatus(t("update.statusRunningUpdater")),await new Promise((resolve)=>setTimeout(resolve,30)),await runShellUpdater({renderer,t,targetLabel:selected.version,command:updateCommandFor(selected.version)})}useBindings(()=>({bindings:[{key:"up",cmd:()=>move(-1)},{key:"down",cmd:()=>move(1)},{key:"k",cmd:()=>move(-1)},{key:"j",cmd:()=>move(1)},{key:"return",cmd:()=>void installSelected()},{key:"u",cmd:()=>void installSelected()},...pageCloseBindings(props.onClose)]}));let selectedNotes=selectedVersion===void 0?void 0:notes[selectedVersion];return $jsxs("box",{flexDirection:"column",flexGrow:1,paddingTop:1,paddingLeft:2,paddingRight:2,children:[$jsxs("box",{flexDirection:"row",justifyContent:"space-between",flexShrink:0,children:[$jsx("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"none",children:t("update.versions.pageTitle")}),$jsx("text",{fg:theme.textMuted,wrapMode:"none",onMouseUp:props.onClose,children:t("update.versions.closeHint")})]}),$jsxs("box",{flexDirection:"row",flexGrow:1,paddingTop:1,gap:2,children:[$jsxs("box",{flexDirection:"column",width:26,flexShrink:0,children:[releases===null?$jsx("text",{fg:theme.textMuted,children:t("update.versions.loading")}):null,releases!==null&&releases.length===0?$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("update.versions.unavailable")}):null,(releases??[]).map((release,index)=>{let active=index===cursor,tags=[release.version===CURRENT_VERSION?t("update.versions.tagCurrent"):"",index===0?t("update.versions.tagLatest"):"",BREAKING_VERSIONS.includes(release.version)?t("update.versions.tagBreaking"):""].filter(Boolean).join(" ");return $jsxs("box",{flexDirection:"row",gap:1,paddingLeft:1,backgroundColor:active?theme.primary:void 0,onMouseUp:()=>setCursor(index),children:[$jsxs("text",{fg:active?theme.selectedListItemText:theme.text,attributes:release.version===CURRENT_VERSION?TextAttributes.BOLD:void 0,wrapMode:"none",children:["v",release.version]}),tags?$jsx("text",{fg:active?theme.selectedListItemText:theme.textMuted,wrapMode:"none",children:tags}):null]},release.version)})]}),$jsxs("box",{flexDirection:"column",flexGrow:1,flexShrink:1,children:[$jsx("scrollbox",{flexGrow:1,flexShrink:1,stickyScroll:!1,verticalScrollbarOptions:{trackOptions:{foregroundColor:"transparent"}},children:$jsxs("box",{flexDirection:"column",paddingRight:1,paddingBottom:1,gap:0,children:[selectedNotes==="loading"?$jsx("text",{fg:theme.textMuted,children:t("update.loadingNotes")}):null,selectedNotes==="missing"?$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("update.notesUnavailable")}):null,selectedNotes!==void 0&&selectedNotes!=="loading"&&selectedNotes!=="missing"?$jsx(ReleaseNotesBody,{body:selectedNotes.body}):null]})}),crossings.length>0?$jsx("text",{fg:theme.warning,wrapMode:"word",children:t("update.versions.breakingWarning",{versions:crossings.join(", ")})}):null]})]}),status?$jsx("text",{fg:theme.info,wrapMode:"word",children:status}):null,$jsx("box",{flexShrink:0,paddingTop:1,children:$jsx("text",{fg:theme.textMuted,attributes:TextAttributes.DIM,wrapMode:"none",children:t("update.versions.footerHint")})})]})}var INLINE_ROWS=24;async function startVersionsHost(){await bootPaneHost({inlineRows:INLINE_ROWS,setup:()=>({root:()=>$jsx(VersionsPage,{onClose:()=>process.exit(0)})})})}export{startVersionsHost,VersionsPage};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{getKeychainPassword,isKeychainSupported,remoteKeychainRef,setKeychainPassword}from"./chunk-sak1tphp.js";import{RemoteExecHost}from"./chunk-y1xnjx8x.js";import{errorMessage}from"./chunk-2brn8e1y.js";import"./chunk-7kysxgbq.js";import{addRemoteRepo,isRemoteProjectsEnabled}from"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.js";import"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import{remoteControlSocketPath}from"./chunk-z1nj22va.js";import"./chunk-br8r72bf.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{createInterface}from"readline";var CLI_NAME=ROVE_PRODUCT_NAME,USAGE=`Usage: ${CLI_NAME} add --remote --host <host> --user <user> --path <basePath>
2
+ import{getKeychainPassword,isKeychainSupported,remoteKeychainRef,setKeychainPassword}from"./chunk-sak1tphp.js";import{RemoteExecHost}from"./chunk-y1xnjx8x.js";import{addRemoteRepo,isRemoteProjectsEnabled}from"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import{errorMessage}from"./chunk-2brn8e1y.js";import"./chunk-7mwvvj46.js";import"./chunk-32j6y5d1.js";import"./chunk-7kysxgbq.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import{remoteControlSocketPath}from"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{createInterface}from"readline";var CLI_NAME=ROVE_PRODUCT_NAME,USAGE=`Usage: ${CLI_NAME} add --remote --host <host> --user <user> --path <basePath>
3
3
  [--port N] [--key [path] | --password]
4
4
 
5
5
  Register an experimental SSH-backed project. Worktrees are created on <host>
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{stopDaemonProcess}from"./chunk-kgs56yns.js";import{spawnDetachedDaemon}from"./chunk-4hnz77xf.js";import{isProcessAlive,readPidFile}from"./chunk-br8r72bf.js";import{DAEMON_PROTOCOL_VERSION,RoveDaemonClient}from"./chunk-sza3tyhs.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath}from"./chunk-eqdbfgq5.js";import{ROVE_PRODUCT_NAME}from"./chunk-nc46gddg.js";import{closeSync,existsSync,mkdirSync,openSync,statSync,unlinkSync}from"fs";import{dirname,resolve}from"path";import{fileURLToPath}from"url";var DAEMON_START_ARGS=["daemon","start"],DAEMON_HELLO_TIMEOUT_MS=3000,BUSY_DAEMON_GRACE_MS=15000;function insideEngineSession(env=process.env){return typeof env.ROVE_TASK_ID==="string"&&env.ROVE_TASK_ID!==""}function autospawnDaemonEnv(env=process.env,reason="autospawn"){let{ROVE_TASK_ID:_task,ROVE_TAB_ID:_tab,ROVE_TUI:_tui,ROVE_TERMINAL_PTY:_pty,ROVE_TASK_ID:_roveTask,ROVE_TAB_ID:_roveTab,ROVE_TUI:_roveTui,ROVE_TERMINAL_PTY:_rovePty,...rest}=env;return{...rest,ROVE_DAEMON_AUTOSPAWNED:"1",ROVE_DAEMON_SPAWN_REASON:reason}}function daemonSpawnReason(env=process.env){let raw=env.ROVE_DAEMON_SPAWN_REASON;if(raw==="explicit-restart"||raw==="autospawn")return raw;return env.ROVE_DAEMON_AUTOSPAWNED==="1"?"autospawn":"manual"}var SPAWN_LOCK_STALE_MS=40000,SPAWN_LOCK_WAIT_MS=30000;function tryAcquireSpawnLock(lockPath,staleMs=SPAWN_LOCK_STALE_MS){let create=()=>{return mkdirSync(dirname(lockPath),{recursive:!0}),closeSync(openSync(lockPath,"wx")),!0};try{return create()}catch{try{if(Date.now()-statSync(lockPath).mtimeMs<=staleMs)return!1;return unlinkSync(lockPath),create()}catch{return!1}}}async function ensureDaemonReachable(resolveSpawn=resolveRoveSpawn,spawnReason="autospawn"){let socketPath=defaultDaemonSocketPath(),state=await probeDaemonSocket(socketPath);if(state==="alive")return socketPath;if(state==="wedged"&&insideEngineSession())throw Error(`rove: daemon at ${socketPath} is not answering hello (busy or wedged); not restarting it from inside an engine session \u2014 retry, or run \`rove daemon restart\` from a regular shell`);let lockPath=`${defaultDaemonPidPath()}.spawn-lock`;if(!tryAcquireSpawnLock(lockPath)){let deadline=Date.now()+SPAWN_LOCK_WAIT_MS;while(Date.now()<deadline){if(await testDaemonResponds(socketPath))return socketPath;await new Promise((resolveTimer)=>setTimeout(resolveTimer,150))}throw Error(`rove: another process is starting the daemon but it never became reachable at ${socketPath}; check ${defaultDaemonLogPath()} or run \`rove doctor\``)}try{if(await probeDaemonSocket(socketPath)==="alive")return socketPath;let livePid=await readPidFile(defaultDaemonPidPath());if(livePid!==null&&livePid!==process.pid&&isProcessAlive(livePid)){let deadline2=Date.now()+BUSY_DAEMON_GRACE_MS;while(Date.now()<deadline2){if(await new Promise((resolveTimer)=>setTimeout(resolveTimer,250)),await testDaemonResponds(socketPath))return socketPath;if(!isProcessAlive(livePid))break}}let[command,...args]=resolveSpawn(DAEMON_START_ARGS);await stopDaemonProcess(socketPath,defaultDaemonPidPath()).catch(()=>{}),spawnDetachedDaemon(command,args,autospawnDaemonEnv(process.env,spawnReason),defaultDaemonLogPath());let deadline=Date.now()+5000;while(Date.now()<deadline){if(await testDaemonResponds(socketPath))return socketPath;await new Promise((resolveTimer)=>setTimeout(resolveTimer,100))}throw Error(`rove: daemon did not start (or stayed wedged) at ${socketPath}; check ${defaultDaemonLogPath()} or run \`rove doctor\``)}finally{try{unlinkSync(lockPath)}catch{}}}async function connectOrStartDaemon(spawnReason="autospawn"){let socketPath=await ensureDaemonReachable(resolveRoveSpawn,spawnReason),client=new RoveDaemonClient(socketPath);return await client.connect(),client}async function connectIfRunning(){let socketPath=defaultDaemonSocketPath();if(!await testDaemonResponds(socketPath))return null;let client=new RoveDaemonClient(socketPath);return await client.connect(),client}async function probeDaemonSocket(socketPath,timeoutMs=DAEMON_HELLO_TIMEOUT_MS){let probe=new RoveDaemonClient(socketPath);try{await probe.connect()}catch{return probe.close(),"absent"}let droppedByPeer=!1,offClose=probe.onLifecycle("close",()=>{droppedByPeer=!0}),replied=probe.request("hello",{protocolVersion:DAEMON_PROTOCOL_VERSION}).then(()=>!0).catch(()=>!0),timer,timedOut=new Promise((resolve2)=>{timer=setTimeout(()=>resolve2(!1),timeoutMs)}),settled=await Promise.race([replied,timedOut]);if(timer)clearTimeout(timer);if(offClose(),probe.close(),droppedByPeer)return"absent";return settled?"alive":"wedged"}async function testDaemonResponds(socketPath,timeoutMs=DAEMON_HELLO_TIMEOUT_MS){return await probeDaemonSocket(socketPath,timeoutMs)==="alive"}class StaleInstallError extends Error{candidates;constructor(cliName,dir,candidates){super(`${cliName}: this process is running from an install that no longer exists on disk \u2014 no ${cliName} entry near ${dir} (checked ${candidates.join(", ")}). Reinstall (\`npm install -g @sma1lboy/rove\`) and relaunch Rove.`);this.name="StaleInstallError",this.candidates=candidates}}function isStaleInstallError(err){return err instanceof Error&&err.name==="StaleInstallError"}function resolveRoveSpawn(subcommand,env=process.env,moduleFile=fileURLToPath(import.meta.url)){let here=moduleFile;if(here.startsWith("/$bunfs")||here.startsWith("B:\\~BUN"))return[process.execPath,...subcommand];let dir=dirname(here),cliName=ROVE_PRODUCT_NAME,candidates=[resolve(dir,`../cli/${cliName}.ts`),resolve(dir,`../../../rove/src/cli/${cliName}.ts`),resolve(dir,`../cli/${cliName}.js`),resolve(dir,"../cli/index.ts"),resolve(dir,"../../../rove/src/cli/index.ts"),resolve(dir,"../cli/index.js")],entry=candidates.find((candidate)=>existsSync(candidate));if(entry)return[process.execPath,entry,...subcommand];throw new StaleInstallError(cliName,dir,candidates)}
2
+ import{stopDaemonProcess}from"./chunk-0a65j1zj.js";import{spawnDetachedDaemon}from"./chunk-bcyex84w.js";import{DAEMON_PROTOCOL_VERSION,RoveDaemonClient}from"./chunk-qzzmd63x.js";import{isProcessAlive,readPidFile}from"./chunk-br8r72bf.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath}from"./chunk-eqdbfgq5.js";import{ROVE_PRODUCT_NAME}from"./chunk-nc46gddg.js";import{closeSync,existsSync,mkdirSync,openSync,statSync,unlinkSync}from"fs";import{dirname,resolve}from"path";import{fileURLToPath}from"url";var DAEMON_START_ARGS=["daemon","start"],DAEMON_HELLO_TIMEOUT_MS=3000,BUSY_DAEMON_GRACE_MS=15000;function insideEngineSession(env=process.env){return typeof env.ROVE_TASK_ID==="string"&&env.ROVE_TASK_ID!==""}function autospawnDaemonEnv(env=process.env,reason="autospawn"){let{ROVE_TASK_ID:_task,ROVE_TAB_ID:_tab,ROVE_TUI:_tui,ROVE_TERMINAL_PTY:_pty,ROVE_TASK_ID:_roveTask,ROVE_TAB_ID:_roveTab,ROVE_TUI:_roveTui,ROVE_TERMINAL_PTY:_rovePty,...rest}=env;return{...rest,ROVE_DAEMON_AUTOSPAWNED:"1",ROVE_DAEMON_SPAWN_REASON:reason}}function daemonSpawnReason(env=process.env){let raw=env.ROVE_DAEMON_SPAWN_REASON;if(raw==="explicit-restart"||raw==="autospawn")return raw;return env.ROVE_DAEMON_AUTOSPAWNED==="1"?"autospawn":"manual"}var SPAWN_LOCK_STALE_MS=40000,SPAWN_LOCK_WAIT_MS=30000;function tryAcquireSpawnLock(lockPath,staleMs=SPAWN_LOCK_STALE_MS){let create=()=>{return mkdirSync(dirname(lockPath),{recursive:!0}),closeSync(openSync(lockPath,"wx")),!0};try{return create()}catch{try{if(Date.now()-statSync(lockPath).mtimeMs<=staleMs)return!1;return unlinkSync(lockPath),create()}catch{return!1}}}async function ensureDaemonReachable(resolveSpawn=resolveRoveSpawn,spawnReason="autospawn"){let socketPath=defaultDaemonSocketPath(),state=await probeDaemonSocket(socketPath);if(state==="alive")return socketPath;if(state==="wedged"&&insideEngineSession())throw Error(`rove: daemon at ${socketPath} is not answering hello (busy or wedged); not restarting it from inside an engine session \u2014 retry, or run \`rove daemon restart\` from a regular shell`);let lockPath=`${defaultDaemonPidPath()}.spawn-lock`;if(!tryAcquireSpawnLock(lockPath)){let deadline=Date.now()+SPAWN_LOCK_WAIT_MS;while(Date.now()<deadline){if(await testDaemonResponds(socketPath))return socketPath;await new Promise((resolveTimer)=>setTimeout(resolveTimer,150))}throw Error(`rove: another process is starting the daemon but it never became reachable at ${socketPath}; check ${defaultDaemonLogPath()} or run \`rove doctor\``)}try{if(await probeDaemonSocket(socketPath)==="alive")return socketPath;let livePid=await readPidFile(defaultDaemonPidPath());if(livePid!==null&&livePid!==process.pid&&isProcessAlive(livePid)){let deadline2=Date.now()+BUSY_DAEMON_GRACE_MS;while(Date.now()<deadline2){if(await new Promise((resolveTimer)=>setTimeout(resolveTimer,250)),await testDaemonResponds(socketPath))return socketPath;if(!isProcessAlive(livePid))break}}let[command,...args]=resolveSpawn(DAEMON_START_ARGS);await stopDaemonProcess(socketPath,defaultDaemonPidPath()).catch(()=>{}),spawnDetachedDaemon(command,args,autospawnDaemonEnv(process.env,spawnReason),defaultDaemonLogPath());let deadline=Date.now()+5000;while(Date.now()<deadline){if(await testDaemonResponds(socketPath))return socketPath;await new Promise((resolveTimer)=>setTimeout(resolveTimer,100))}throw Error(`rove: daemon did not start (or stayed wedged) at ${socketPath}; check ${defaultDaemonLogPath()} or run \`rove doctor\``)}finally{try{unlinkSync(lockPath)}catch{}}}async function connectOrStartDaemon(spawnReason="autospawn"){let socketPath=await ensureDaemonReachable(resolveRoveSpawn,spawnReason),client=new RoveDaemonClient(socketPath);return await client.connect(),client}async function connectIfRunning(){let socketPath=defaultDaemonSocketPath();if(!await testDaemonResponds(socketPath))return null;let client=new RoveDaemonClient(socketPath);return await client.connect(),client}async function probeDaemonSocket(socketPath,timeoutMs=DAEMON_HELLO_TIMEOUT_MS){let probe=new RoveDaemonClient(socketPath);try{await probe.connect()}catch{return probe.close(),"absent"}let droppedByPeer=!1,offClose=probe.onLifecycle("close",()=>{droppedByPeer=!0}),replied=probe.request("hello",{protocolVersion:DAEMON_PROTOCOL_VERSION}).then(()=>!0).catch(()=>!0),timer,timedOut=new Promise((resolve2)=>{timer=setTimeout(()=>resolve2(!1),timeoutMs)}),settled=await Promise.race([replied,timedOut]);if(timer)clearTimeout(timer);if(offClose(),probe.close(),droppedByPeer)return"absent";return settled?"alive":"wedged"}async function testDaemonResponds(socketPath,timeoutMs=DAEMON_HELLO_TIMEOUT_MS){return await probeDaemonSocket(socketPath,timeoutMs)==="alive"}class StaleInstallError extends Error{candidates;constructor(cliName,dir,candidates){super(`${cliName}: this process is running from an install that no longer exists on disk \u2014 no ${cliName} entry near ${dir} (checked ${candidates.join(", ")}). Reinstall (\`npm install -g @sma1lboy/rove\`) and relaunch Rove.`);this.name="StaleInstallError",this.candidates=candidates}}function isStaleInstallError(err){return err instanceof Error&&err.name==="StaleInstallError"}function resolveRoveSpawn(subcommand,env=process.env,moduleFile=fileURLToPath(import.meta.url)){let here=moduleFile;if(here.startsWith("/$bunfs")||here.startsWith("B:\\~BUN"))return[process.execPath,...subcommand];let dir=dirname(here),cliName=ROVE_PRODUCT_NAME,candidates=[resolve(dir,`../cli/${cliName}.ts`),resolve(dir,`../../../rove/src/cli/${cliName}.ts`),resolve(dir,`../cli/${cliName}.js`),resolve(dir,"../cli/index.ts"),resolve(dir,"../../../rove/src/cli/index.ts"),resolve(dir,"../cli/index.js")],entry=candidates.find((candidate)=>existsSync(candidate));if(entry)return[process.execPath,entry,...subcommand];throw new StaleInstallError(cliName,dir,candidates)}
3
3
  export{autospawnDaemonEnv,daemonSpawnReason,tryAcquireSpawnLock,ensureDaemonReachable,connectOrStartDaemon,connectIfRunning,probeDaemonSocket,testDaemonResponds,StaleInstallError,isStaleInstallError,resolveRoveSpawn};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{MachineFeed,decodeMachines}from"./chunk-9zswhjkv.js";import"./chunk-qs359899.js";import"./chunk-v6v272b3.js";import"./chunk-6xr1prk6.js";import"./chunk-atn06ncf.js";import"./chunk-vytrhm50.js";import"./chunk-sq6zj3fb.js";import"./chunk-y1xnjx8x.js";import"./chunk-e58kj4gm.js";import"./chunk-0a65j1zj.js";import"./chunk-bcyex84w.js";import"./chunk-0r1jf8m9.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-40dgv828.js";import"./chunk-6xppe9bj.js";import"./chunk-9ckvdy0j.js";import"./chunk-32j6y5d1.js";import"./chunk-cmp64q4j.js";import"./chunk-qzzmd63x.js";import"./chunk-96kvwzj8.js";import"./chunk-7kysxgbq.js";import"./chunk-xy8be792.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{decodeMachines,MachineFeed};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{SUBCOMMAND_VERBS,TOP_LEVEL_SUBCOMMANDS}from"./chunk-tjj9bt7n.js";import{join}from"path";import{fileURLToPath}from"url";var SHELLS=["bash","zsh","fish"];function isShellKind(value){return SHELLS.some((shell)=>shell===value)}var SHIPPED_COMPLETIONS_DIR=fileURLToPath(new URL("../completions/",import.meta.url));function shippedCompletionsPath(shell,cliName,dir=SHIPPED_COMPLETIONS_DIR){return join(dir,`${cliName}.${shell}`)}function mergeSubVerbs(apiVerbs){let merged={...SUBCOMMAND_VERBS,api:apiVerbs};return Object.keys(merged).sort().map((command)=>[command,merged[command]??[]])}function generateCompletions(shell,cliName,subVerbs){return shell==="bash"?generateBashCompletions(cliName,subVerbs):shell==="zsh"?generateZshCompletions(cliName,subVerbs):generateFishCompletions(cliName,subVerbs)}function generateBashCompletions(cliName,subVerbs){let subcommands=TOP_LEVEL_SUBCOMMANDS.join(" "),fn=`_${cliName}`;return[`# ${cliName} bash completions`,`# Source: ${cliName} completions bash`,"",`${fn}() {`," local cur prev"," COMPREPLY=()",' cur="${COMP_WORDS[COMP_CWORD]}"',' prev="${COMP_WORDS[COMP_CWORD-1]}"'," if [[ ${COMP_CWORD} -eq 1 ]]; then",` COMPREPLY=( $(compgen -W "${subcommands}" -- "\${cur}") )`," return"," fi"," if [[ ${COMP_CWORD} -eq 2 ]]; then",' case "${prev}" in',...subVerbs.map(([command,verbs])=>` ${command}) COMPREPLY=( $(compgen -W "${verbs.join(" ")}" -- "\${cur}") ) ;;`)," esac"," fi","}",`complete -F ${fn} ${cliName}`,""].join(`
2
+ import{SUBCOMMAND_VERBS,TOP_LEVEL_SUBCOMMANDS}from"./chunk-qqr4tfhk.js";import{join}from"path";import{fileURLToPath}from"url";var SHELLS=["bash","zsh","fish"];function isShellKind(value){return SHELLS.some((shell)=>shell===value)}var SHIPPED_COMPLETIONS_DIR=fileURLToPath(new URL("../completions/",import.meta.url));function shippedCompletionsPath(shell,cliName,dir=SHIPPED_COMPLETIONS_DIR){return join(dir,`${cliName}.${shell}`)}function mergeSubVerbs(apiVerbs){let merged={...SUBCOMMAND_VERBS,api:apiVerbs};return Object.keys(merged).sort().map((command)=>[command,merged[command]??[]])}function generateCompletions(shell,cliName,subVerbs){return shell==="bash"?generateBashCompletions(cliName,subVerbs):shell==="zsh"?generateZshCompletions(cliName,subVerbs):generateFishCompletions(cliName,subVerbs)}function generateBashCompletions(cliName,subVerbs){let subcommands=TOP_LEVEL_SUBCOMMANDS.join(" "),fn=`_${cliName}`;return[`# ${cliName} bash completions`,`# Source: ${cliName} completions bash`,"",`${fn}() {`," local cur prev"," COMPREPLY=()",' cur="${COMP_WORDS[COMP_CWORD]}"',' prev="${COMP_WORDS[COMP_CWORD-1]}"'," if [[ ${COMP_CWORD} -eq 1 ]]; then",` COMPREPLY=( $(compgen -W "${subcommands}" -- "\${cur}") )`," return"," fi"," if [[ ${COMP_CWORD} -eq 2 ]]; then",' case "${prev}" in',...subVerbs.map(([command,verbs])=>` ${command}) COMPREPLY=( $(compgen -W "${verbs.join(" ")}" -- "\${cur}") ) ;;`)," esac"," fi","}",`complete -F ${fn} ${cliName}`,""].join(`
3
3
  `)}function generateZshCompletions(cliName,subVerbs){let subcommandsList=TOP_LEVEL_SUBCOMMANDS.map((s)=>`"${s}"`).join(" "),fn=`_${cliName}`;return[`#compdef ${cliName}`,`# ${cliName} zsh completions`,`# Source: ${cliName} completions zsh`,"",`${fn}() {`," local -a subcommands verbs",` subcommands=(${subcommandsList})`,""," if (( CURRENT == 2 )); then"," _describe -t commands 'subcommand' subcommands"," return"," fi",""," verbs=()",' case "${words[2]}" in',...subVerbs.map(([command,verbs])=>` ${command}) verbs=(${verbs.map((v)=>`"${v}"`).join(" ")}) ;;`)," esac"," if (( CURRENT == 3 && ${#verbs} > 0 )); then"," _describe -t verbs 'verb' verbs"," fi","}","","# Autoloaded from $fpath -> run as the completion function;","# sourced directly -> register with compdef instead.",`if [ "\${funcstack[1]}" = "${fn}" ]; then`,` ${fn} "$@"`,"elif (( $+functions[compdef] )); then",` compdef ${fn} ${cliName}`,"fi",""].join(`
4
4
  `)}function generateFishCompletions(cliName,subVerbs){let lines=[...TOP_LEVEL_SUBCOMMANDS.map((s)=>`complete -c ${cliName} -f -n __fish_use_subcommand -a ${s}`),...subVerbs.map(([command,verbs])=>`complete -c ${cliName} -f -n "__fish_seen_subcommand_from ${command}" -a "${verbs.join(" ")}"`)];return`# ${cliName} fish completions
5
5
  # Source: ${cliName} completions fish
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{REPEAT_SEP,VerbArgs,defaultApiRuntime,findVerb,parseFlags,unknownVerbError,validateAgainstSpec}from"./chunk-kyt9ytz2.js";import{ensurePluginEnginesLoaded}from"./chunk-jwedmkm8.js";import{ApiError,cliCaller,requireVerbInstalled,splitDaemonCode}from"./chunk-r5j8fycq.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{ROVE_API_VERSION}from"./chunk-96kvwzj8.js";function toApiError(err){if(err instanceof ApiError)return err;let message=errorMessage(err);if(/task not found/i.test(message))return new ApiError(message,"TASK_NOT_FOUND",{hint:"that task id does not exist (deleted or mistyped) \u2014 list live tasks and retry with a real id",nextCommandArgs:["api","list"]});if(/unknown daemon request:/i.test(message))return versionSkewError(message);let coded=splitDaemonCode(message);if(coded)return new ApiError(coded.rest,coded.code);return new ApiError(message,"RPC_ERROR")}function versionSkewError(message){return new ApiError(message,"DAEMON_VERSION_SKEW",{hint:"the running daemon is a different build than this CLI, so it does not serve this verb \u2014 restart the daemon to pick up this build, then retry (if the verb is gone for good, `rove api schema` lists what this daemon actually serves)",nextCommandArgs:["daemon","restart"]})}function errorBody(err){return{message:err.message,code:err.code,...err.data}}function successEnvelope(result){return{apiVersion:ROVE_API_VERSION,ok:!0,result}}function errorEnvelope(err){return{apiVersion:ROVE_API_VERSION,ok:!1,error:errorBody(toApiError(err))}}function flagText(name,spec,value){let type=spec?.type;if(typeof value==="string")return value;if(spec?.repeatable&&Array.isArray(value)&&value.every((item)=>typeof item==="string"))return value.join(REPEAT_SEP);if(typeof value==="boolean"&&(type==="bool"||type===void 0))return String(value);if(typeof value==="number"&&(type==="int"||type==="uint"||type===void 0))return String(value);if(Array.isArray(value)&&(type==="csv"||type===void 0)){if(!value.every((item)=>typeof item==="string"&&!item.includes(",")))throw new ApiError(`param ${name} must be a list of strings without ","`,"BAD_FLAG");return value.join(",")}throw new ApiError(`param ${name} does not fit its ${type} flag`,"BAD_FLAG")}function paramsToFlags(verb,params){let flags=new Map;for(let[name,value]of Object.entries(params))flags.set(name,flagText(name,verb.flags.find((f)=>f.name===name),value));return flags}function booleanFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.type==="bool").map((f)=>f.name))}function repeatableFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.repeatable).map((f)=>f.name))}async function callVerb(verb,flags,deps){requireVerbInstalled(verb);let caller=deps.caller??cliCaller();try{return await verb.handler({args:new VerbArgs(verb,flags,caller),caller,client:deps.client,runtime:deps.runtime??defaultApiRuntime})}catch(err){throw toApiError(err)}}async function runVerb(verbName,input,deps){ensurePluginEnginesLoaded();let verb=findVerb(verbName);if(!verb)throw unknownVerbError(verbName);if(verb.cliOnly&&!(deps.caller??cliCaller()).cli)throw new ApiError(`"${verbName}" streams over stdout/stdin, so only \`rove api\` can run it`,"CLI_ONLY_VERB",{hint:"run it as a CLI command on the machine whose daemon you are talking to"});let flags="argv"in input?parseFlags(input.argv,booleanFlagsOf(verb),repeatableFlagsOf(verb)).flags:paramsToFlags(verb,input.params);return validateAgainstSpec(verb,flags),callVerb(verb,flags,deps)}async function executeVerb(verbName,input,deps){try{return successEnvelope(await runVerb(verbName,input,deps))}catch(err){return errorEnvelope(err)}}function argvToParams(verbName,argv){let verb=findVerb(verbName);if(!verb)throw unknownVerbError(verbName);let{flags}=parseFlags(argv,booleanFlagsOf(verb),repeatableFlagsOf(verb));return Object.fromEntries(flags)}
2
+ import{REPEAT_SEP,VerbArgs,defaultApiRuntime,findVerb,parseFlags,unknownVerbError,validateAgainstSpec}from"./chunk-z3wrbtcq.js";import{ensurePluginEnginesLoaded}from"./chunk-kdnk8m5j.js";import{ApiError,cliCaller,requireVerbInstalled,splitDaemonCode}from"./chunk-r5j8fycq.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{ROVE_API_VERSION}from"./chunk-96kvwzj8.js";function toApiError(err){if(err instanceof ApiError)return err;let message=errorMessage(err);if(/task not found/i.test(message))return new ApiError(message,"TASK_NOT_FOUND",{hint:"that task id does not exist (deleted or mistyped) \u2014 list live tasks and retry with a real id",nextCommandArgs:["api","list"]});if(/unknown daemon request:/i.test(message))return versionSkewError(message);let coded=splitDaemonCode(message);if(coded)return new ApiError(coded.rest,coded.code);return new ApiError(message,"RPC_ERROR")}function versionSkewError(message){return new ApiError(message,"DAEMON_VERSION_SKEW",{hint:"the running daemon is a different build than this CLI, so it does not serve this verb \u2014 restart the daemon to pick up this build, then retry (if the verb is gone for good, `rove api schema` lists what this daemon actually serves)",nextCommandArgs:["daemon","restart"]})}function errorBody(err){return{message:err.message,code:err.code,...err.data}}function successEnvelope(result){return{apiVersion:ROVE_API_VERSION,ok:!0,result}}function errorEnvelope(err){return{apiVersion:ROVE_API_VERSION,ok:!1,error:errorBody(toApiError(err))}}function flagText(name,spec,value){let type=spec?.type;if(typeof value==="string")return value;if(spec?.repeatable&&Array.isArray(value)&&value.every((item)=>typeof item==="string"))return value.join(REPEAT_SEP);if(typeof value==="boolean"&&(type==="bool"||type===void 0))return String(value);if(typeof value==="number"&&(type==="int"||type==="uint"||type===void 0))return String(value);if(Array.isArray(value)&&(type==="csv"||type===void 0)){if(!value.every((item)=>typeof item==="string"&&!item.includes(",")))throw new ApiError(`param ${name} must be a list of strings without ","`,"BAD_FLAG");return value.join(",")}throw new ApiError(`param ${name} does not fit its ${type} flag`,"BAD_FLAG")}function paramsToFlags(verb,params){let flags=new Map;for(let[name,value]of Object.entries(params))flags.set(name,flagText(name,verb.flags.find((f)=>f.name===name),value));return flags}function booleanFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.type==="bool").map((f)=>f.name))}function repeatableFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.repeatable).map((f)=>f.name))}async function callVerb(verb,flags,deps){requireVerbInstalled(verb);let caller=deps.caller??cliCaller();try{return await verb.handler({args:new VerbArgs(verb,flags,caller),caller,client:deps.client,runtime:deps.runtime??defaultApiRuntime})}catch(err){throw toApiError(err)}}async function runVerb(verbName,input,deps){ensurePluginEnginesLoaded();let verb=findVerb(verbName);if(!verb)throw unknownVerbError(verbName);if(verb.cliOnly&&!(deps.caller??cliCaller()).cli)throw new ApiError(`"${verbName}" streams over stdout/stdin, so only \`rove api\` can run it`,"CLI_ONLY_VERB",{hint:"run it as a CLI command on the machine whose daemon you are talking to"});let flags="argv"in input?parseFlags(input.argv,booleanFlagsOf(verb),repeatableFlagsOf(verb)).flags:paramsToFlags(verb,input.params);return validateAgainstSpec(verb,flags),callVerb(verb,flags,deps)}async function executeVerb(verbName,input,deps){try{return successEnvelope(await runVerb(verbName,input,deps))}catch(err){return errorEnvelope(err)}}function argvToParams(verbName,argv){let verb=findVerb(verbName);if(!verb)throw unknownVerbError(verbName);let{flags}=parseFlags(argv,booleanFlagsOf(verb),repeatableFlagsOf(verb));return Object.fromEntries(flags)}
3
3
  export{toApiError,errorBody,successEnvelope,errorEnvelope,paramsToFlags,booleanFlagsOf,repeatableFlagsOf,callVerb,runVerb,executeVerb,argvToParams};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{ConnectTimeoutError,RoveDaemonClient,RpcTimeoutError}from"./chunk-sza3tyhs.js";import"./chunk-96kvwzj8.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{RpcTimeoutError,RoveDaemonClient,ConnectTimeoutError};
2
+ import{ConnectTimeoutError,RoveDaemonClient,RpcTimeoutError}from"./chunk-qzzmd63x.js";import"./chunk-96kvwzj8.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{RpcTimeoutError,RoveDaemonClient,ConnectTimeoutError};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{detectLegacyInstalls,isRovePluginEnabled,migrationHint}from"./chunk-7bakcyes.js";import"./chunk-8qkyd589.js";import"./chunk-7kysxgbq.js";import"./chunk-hz1x3hak.js";import"./chunk-x1jxxmtc.js";import"./chunk-xy8be792.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-8t7qbr1e.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{migrationHint,isRovePluginEnabled,detectLegacyInstalls};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ var aliasKey=(alias)=>`${alias.taskId}::${alias.tabId}`;function isUnderKey(key,base){return key===base||key.startsWith(`${base}::`)}function aliasedKeys(tasks){return tasks.flatMap((task)=>task.ptyAlias?[aliasKey(task.ptyAlias)]:[])}function teardownScope(tasks,taskId,leaving){let spare=tasks.filter((t)=>t.ptyAlias?.taskId===taskId&&!leaving.has(t.id)&&!t.deletion).map((t)=>aliasKey(t.ptyAlias)),own=tasks.find((t)=>t.id===taskId)?.ptyAlias;return{spare,also:own&&own.taskId!==taskId?[aliasKey(own)]:[]}}function ptyScopeFor(tasks,taskId){let alias=tasks.find((t)=>t.id===taskId)?.ptyAlias;return{...alias?{alias}:{},claimed:aliasedKeys(tasks)}}function unclaimedSessions(sessions,scope){let own=scope?.alias?aliasKey(scope.alias):void 0,foreign=(scope?.claimed??[]).filter((base)=>base!==own);return sessions.filter((s)=>!foreign.some((base)=>isUnderKey(s.key,base)))}function hostedKeyFor(taskId,tabId,scope){if(scope?.alias&&scope.alias.tabId===tabId)return aliasKey(scope.alias);let key=`${taskId}::${tabId}`;return unclaimedSessions([{key}],scope).length>0?key:null}
3
+ export{aliasKey,isUnderKey,aliasedKeys,teardownScope,ptyScopeFor,unclaimedSessions,hostedKeyFor};