@sma1lboy/rove 0.9.150 → 0.9.152

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 (134) hide show
  1. package/dist/cli/chunk-04zz7j1j.js +2 -0
  2. package/dist/cli/chunk-08yqc66n.js +5 -0
  3. package/dist/cli/chunk-0dbz76mq.js +2 -0
  4. package/dist/cli/chunk-12458wct.js +2 -0
  5. package/dist/cli/chunk-1cjd7h8p.js +6 -0
  6. package/dist/cli/chunk-1t0xygy1.js +2 -0
  7. package/dist/cli/chunk-21eenkyj.js +8 -0
  8. package/dist/cli/{chunk-1gpx52qj.js → chunk-3737ysrb.js} +2 -2
  9. package/dist/cli/chunk-4ezfas24.js +3 -0
  10. package/dist/cli/chunk-4gaqhq8t.js +2 -0
  11. package/dist/cli/chunk-4nm1axc3.js +2 -0
  12. package/dist/cli/{chunk-1z9ae43v.js → chunk-4ppq1q86.js} +1 -1
  13. package/dist/cli/{chunk-6zwg6me2.js → chunk-51y7sjhe.js} +1 -1
  14. package/dist/cli/{chunk-5gn4chrm.js → chunk-5hs3w3xy.js} +1 -1
  15. package/dist/cli/{chunk-2w4zymkv.js → chunk-5w9bxd45.js} +1 -1
  16. package/dist/cli/{chunk-q6579rbz.js → chunk-5wzap7r2.js} +1 -1
  17. package/dist/cli/chunk-6kz81dkc.js +17 -0
  18. package/dist/cli/chunk-71tq5yn8.js +3 -0
  19. package/dist/cli/chunk-7jkf3tvr.js +3 -0
  20. package/dist/cli/{chunk-bjat0fyy.js → chunk-86aac2wh.js} +1 -1
  21. package/dist/cli/{chunk-nm6adny4.js → chunk-86bjqmg1.js} +1 -1
  22. package/dist/cli/{chunk-k3ksts6g.js → chunk-87y9qjcm.js} +1 -1
  23. package/dist/cli/{chunk-e1g9mvww.js → chunk-8gsqypg8.js} +1 -1
  24. package/dist/cli/{chunk-fbg4fkmg.js → chunk-9ejxh7a6.js} +1 -1
  25. package/dist/cli/{chunk-8x0h6rwa.js → chunk-9kyf6d1j.js} +1 -1
  26. package/dist/cli/{chunk-7j8zpeer.js → chunk-9nbg0nzh.js} +1 -1
  27. package/dist/cli/chunk-9twmp5m9.js +4 -0
  28. package/dist/cli/{chunk-2ffbcsm6.js → chunk-9yhr7fxf.js} +4 -4
  29. package/dist/cli/chunk-a3a9gdc4.js +3 -0
  30. package/dist/cli/chunk-a3zmjcbd.js +3 -0
  31. package/dist/cli/{chunk-g1ybdgad.js → chunk-a6sxg2gx.js} +2 -2
  32. package/dist/cli/{chunk-6xhtf9jx.js → chunk-adr88eqt.js} +1 -1
  33. package/dist/cli/{chunk-mrwee8sj.js → chunk-aeq5xmtq.js} +1 -1
  34. package/dist/cli/{chunk-nceh1q3d.js → chunk-bjcjrh1v.js} +1 -1
  35. package/dist/cli/chunk-bkba9c4n.js +4 -0
  36. package/dist/cli/{chunk-db57060b.js → chunk-bkpsjg6y.js} +1 -1
  37. package/dist/cli/chunk-bnxz5nha.js +2 -0
  38. package/dist/cli/{chunk-mfvy10ep.js → chunk-bqhq43zx.js} +1 -1
  39. package/dist/cli/chunk-cexxmfva.js +2 -0
  40. package/dist/cli/{chunk-6h9tc9vn.js → chunk-ch1jvgg1.js} +1 -1
  41. package/dist/cli/chunk-d393z1mj.js +4 -0
  42. package/dist/cli/chunk-e8493db0.js +4 -0
  43. package/dist/cli/chunk-ezty9a39.js +4 -0
  44. package/dist/cli/{chunk-k2gfqqmg.js → chunk-frp2wp3m.js} +1 -1
  45. package/dist/cli/{chunk-2hgt2esq.js → chunk-g226rgg2.js} +2 -2
  46. package/dist/cli/chunk-g2advs8t.js +3 -0
  47. package/dist/cli/{chunk-cjxwzzq7.js → chunk-gbd5dyhh.js} +1 -1
  48. package/dist/cli/{chunk-sm1vhrb6.js → chunk-gfh0ws3y.js} +1 -1
  49. package/dist/cli/chunk-gkskp5j9.js +2 -0
  50. package/dist/cli/{chunk-f6zsej8e.js → chunk-gvhq08an.js} +1 -1
  51. package/dist/cli/{chunk-5kkm6tjz.js → chunk-h5a7necv.js} +2 -2
  52. package/dist/cli/chunk-he0st4f1.js +2 -0
  53. package/dist/cli/chunk-hhvzpps8.js +9 -0
  54. package/dist/cli/chunk-j2j1kq3m.js +2 -0
  55. package/dist/cli/{chunk-yeytzg1x.js → chunk-kp85rcmt.js} +1 -1
  56. package/dist/cli/chunk-m57cv76y.js +3 -0
  57. package/dist/cli/{chunk-z3va3mt0.js → chunk-m7zq502q.js} +21 -19
  58. package/dist/cli/{chunk-r2w8qgyd.js → chunk-mewacahf.js} +3 -3
  59. package/dist/cli/{chunk-y34fff4t.js → chunk-mff9s59g.js} +1 -1
  60. package/dist/cli/{chunk-fhfqyfpf.js → chunk-mw20v2nd.js} +2 -2
  61. package/dist/cli/chunk-mwzyk91k.js +2 -0
  62. package/dist/cli/chunk-n4dmwkfa.js +4 -0
  63. package/dist/cli/chunk-n5sssj80.js +3 -0
  64. package/dist/cli/chunk-nw91q4x3.js +2 -0
  65. package/dist/cli/chunk-nywh3yb9.js +3 -0
  66. package/dist/cli/chunk-pxtx1gcf.js +3 -0
  67. package/dist/cli/chunk-r1p9bwja.js +7 -0
  68. package/dist/cli/{chunk-hg5x2wr0.js → chunk-r2pztgm6.js} +2 -2
  69. package/dist/cli/{chunk-vapjtyv9.js → chunk-rgh49bm3.js} +1 -1
  70. package/dist/cli/{chunk-khp1ga68.js → chunk-rhp5f301.js} +1 -1
  71. package/dist/cli/chunk-rz8zs7j8.js +3 -0
  72. package/dist/cli/{chunk-b24ykek8.js → chunk-rzvzcqvx.js} +1 -1
  73. package/dist/cli/chunk-s239r4nv.js +2 -0
  74. package/dist/cli/{chunk-zb56ck5f.js → chunk-s433recg.js} +1 -1
  75. package/dist/cli/{chunk-71m4ak4t.js → chunk-s8xyh1bt.js} +1 -1
  76. package/dist/cli/chunk-s9a974w9.js +2 -0
  77. package/dist/cli/{chunk-ppe5myr0.js → chunk-sexavazm.js} +1 -1
  78. package/dist/cli/{chunk-7sgtpyr0.js → chunk-svtpb8v2.js} +1 -1
  79. package/dist/cli/chunk-t2es32fg.js +2 -0
  80. package/dist/cli/{chunk-a0rxr20w.js → chunk-ty56f8fe.js} +1 -1
  81. package/dist/cli/{chunk-1ps9qzp1.js → chunk-w2denxak.js} +2 -2
  82. package/dist/cli/{chunk-jyv82gf0.js → chunk-w7zv47mp.js} +3 -4
  83. package/dist/cli/{chunk-jsss3ze7.js → chunk-xe62kmhp.js} +2 -2
  84. package/dist/cli/chunk-xkggb529.js +2 -0
  85. package/dist/cli/chunk-xtr7ks02.js +3 -0
  86. package/dist/cli/{chunk-13s8rnk5.js → chunk-y9ksfpbp.js} +1 -1
  87. package/dist/cli/{chunk-p7kztgsd.js → chunk-ybsdy2w8.js} +1 -1
  88. package/dist/cli/chunk-z7eavs6b.js +2 -0
  89. package/dist/cli/{chunk-0h4cfy44.js → chunk-z8khnqxg.js} +3 -3
  90. package/dist/cli/{chunk-tfdx5ve6.js → chunk-zaxdx0wd.js} +1 -1
  91. package/dist/cli/index.js +10 -10
  92. package/dist/cli/kobe.js +1 -1
  93. package/dist/cli/pty-host-node.mjs +25 -2
  94. package/dist/cli/rove.js +1 -1
  95. package/dist/web-ui/assets/{harness-CYGJ4uoR.js → harness-DvVhbIUb.js} +1 -1
  96. package/dist/web-ui/assets/{index-BTJ-X_4s.js → index-DrVCJr6C.js} +2 -2
  97. package/dist/web-ui/index.html +1 -1
  98. package/package.json +2 -1
  99. package/dist/cli/chunk-003tvywc.js +0 -3
  100. package/dist/cli/chunk-0kn7xxqm.js +0 -6
  101. package/dist/cli/chunk-0tw42ad1.js +0 -2
  102. package/dist/cli/chunk-1cqtm24j.js +0 -8
  103. package/dist/cli/chunk-1kktfmtg.js +0 -4
  104. package/dist/cli/chunk-1rkb0j1j.js +0 -3
  105. package/dist/cli/chunk-20pt9yqh.js +0 -4
  106. package/dist/cli/chunk-2jxdzw4r.js +0 -3
  107. package/dist/cli/chunk-2pqqvse3.js +0 -2
  108. package/dist/cli/chunk-2t62vct7.js +0 -2
  109. package/dist/cli/chunk-4d7bbh87.js +0 -2
  110. package/dist/cli/chunk-5b6940vk.js +0 -2
  111. package/dist/cli/chunk-5hhvrvgm.js +0 -8
  112. package/dist/cli/chunk-7nhdvr5v.js +0 -4
  113. package/dist/cli/chunk-84aks78e.js +0 -3
  114. package/dist/cli/chunk-99devyxr.js +0 -2
  115. package/dist/cli/chunk-awsawfrm.js +0 -2
  116. package/dist/cli/chunk-aynx3d2a.js +0 -2
  117. package/dist/cli/chunk-bzz4ta7g.js +0 -17
  118. package/dist/cli/chunk-dk3pvrw6.js +0 -2
  119. package/dist/cli/chunk-fyjj9ast.js +0 -2
  120. package/dist/cli/chunk-gjf604bb.js +0 -3
  121. package/dist/cli/chunk-gm3vj0fv.js +0 -5
  122. package/dist/cli/chunk-h79zfebh.js +0 -7
  123. package/dist/cli/chunk-h9xpeqn6.js +0 -2
  124. package/dist/cli/chunk-hpj8xjxb.js +0 -2
  125. package/dist/cli/chunk-k5k0ecka.js +0 -4
  126. package/dist/cli/chunk-mfqjdpd8.js +0 -2
  127. package/dist/cli/chunk-ncsgawg7.js +0 -2
  128. package/dist/cli/chunk-qtke4x42.js +0 -3
  129. package/dist/cli/chunk-r26g5ree.js +0 -2
  130. package/dist/cli/chunk-vwjec3m6.js +0 -3
  131. package/dist/cli/chunk-vws0j65e.js +0 -4
  132. package/dist/cli/chunk-vzvy1s1d.js +0 -2
  133. package/dist/cli/chunk-w5xy83ze.js +0 -2
  134. /package/dist/cli/{chunk-h82kzdsn.js → chunk-bq3fyc0p.js} +0 -0
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{engineProcessIn,foregroundEngine,foregroundEngineIn,hasAncestor,parsePsSnapshot,psSnapshot,vendorFromArgv}from"./chunk-rzvzcqvx.js";import"./chunk-m7zq502q.js";import"./chunk-n4dmwkfa.js";import"./chunk-w7zv47mp.js";import"./chunk-gwcneg6c.js";import"./chunk-ezty9a39.js";import"./chunk-7jkf3tvr.js";import"./chunk-2fypr41w.js";import"./chunk-87y9qjcm.js";import"./chunk-m57cv76y.js";import"./chunk-nywh3yb9.js";import"./chunk-xy8be792.js";import"./chunk-a3a9gdc4.js";import"./chunk-2brn8e1y.js";import"./chunk-9c1e5z6a.js";import"./chunk-6dn6h640.js";import"./chunk-bq3fyc0p.js";import"./chunk-ef11h2am.js";import"./chunk-8ddjfc2v.js";import"./chunk-cb2fpajx.js";import"./chunk-grh6dvfv.js";import"./chunk-pw6yfj10.js";import"./chunk-x760j10f.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";export{vendorFromArgv,psSnapshot,parsePsSnapshot,hasAncestor,foregroundEngineIn,foregroundEngine,engineProcessIn};
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import{canonPath,normalizeMainRepo,randomDirTaskSuffix,repoWorkingDir,samePrStatus,titleFromRepo}from"./chunk-71tq5yn8.js";import{readLastActiveTaskId,writeLastActiveTaskId}from"./chunk-s433recg.js";import{GitWorktreeManager,defaultExecDeps}from"./chunk-hhvzpps8.js";import{canonicalize,listWorktreeDirNames}from"./chunk-4ezfas24.js";import{resolvePreferredVendor}from"./chunk-gvhq08an.js";import{READ_ONLY_GIT_ENV}from"./chunk-6xppe9bj.js";import{CannotDeleteMainTaskError,DirtyWorktreeError,EmptyBranchDirtyWorktreeError,EmptyBranchError,GitCommandFailedError,IllegalTransitionError,LandConflictError,MainCheckoutDirtyError,MissingRefError,PLACEHOLDER_TASK_TITLE,TaskDeletingError,TaskNotFoundError,WorktreeRemoveFailedError,isPlaceholderDerivedBranch}from"./chunk-aq912vvw.js";import{detectLanguage}from"./chunk-hape5vwj.js";import{createStateCell}from"./chunk-sdyzxayr.js";import{addSavedRepo,getSavedRepos,isGitRepo,removeSavedRepo,resolveRepoRoot}from"./chunk-87y9qjcm.js";import{DEFAULT_TASK_VENDOR}from"./chunk-xy8be792.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{projectRejection,rejectionReason}from"./chunk-9c1e5z6a.js";async function createTaskRow(deps,input){if(!input.repo)throw Error("createTask: repo is required");let repo=(await deps.mainTasks.ensureIfEligible(input.repo,input.projectIntent??"explicit"))?.repo??normalizeMainRepo(input.repo).repo,title=(input.title??PLACEHOLDER_TASK_TITLE).trim()||PLACEHOLDER_TASK_TITLE;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.groupId?{groupId:input.groupId}:{},...input.dispatcher?{dispatcher:input.dispatcher}:{},...input.routine?{routine:input.routine}:{},...input.baseRef?.trim()?{baseRef:input.baseRef.trim()}:{}})}async function openDirectoryTaskRow(deps,input){if(!input.dir)throw Error("openDirectoryTask: dir is required");let dir=canonPath(input.dir);return deps.store.create({repo:dir,title:input.scratch?"":`${titleFromRepo(dir)}-${randomDirTaskSuffix()}`,branch:"",worktreePath:dir,status:"backlog",kind:"dir",...input.scratch?{scratch:!0}:{},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)})}function parseDirtyPaths(stdout){return stdout.split(`
3
+ `).filter((line)=>line.length>3).map((line)=>line.slice(3).trim()).filter((line)=>line.length>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(await landGit(exec,dir,["status","--porcelain"],{readOnly:!0})).stdout.trim().length>0}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(task.repo,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(task.repo,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)}}import path from"path";async function landTaskWithCleanup(task,opts,deps){if(task.kind==="main")throw Error("landTask: a main task has no branch to land");if(task.kind==="dir")throw Error("landTask: a directory task has no Rove-managed branch to land");let result=await landTask(task,{strategy:opts.strategy}),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)await deps.worktrees.deleteBranch(task.repo,result.branch,{force:!0,onAnchor:(record)=>{branchAnchor=record}});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(wt===canonicalize(task.repo))return{removed:!1,reason:"refusing to remove the base checkout"};if(callerCwd){let rel=path.relative(wt,canonicalize(callerCwd));if(rel===""||!rel.startsWith("..")&&!path.isAbsolute(rel))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(`
4
+ `).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(task.repo,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()}}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&&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)}}}function promotableDirTasks(deps){let claimed=new Set(deps.tasks.filter((task)=>task.kind==="main").map((task)=>task.repo)),seen=new Set,out=[];for(let task of deps.tasks){if(task.kind!=="dir"||task.scratch===!0)continue;let path2=task.repo;if(!path2||claimed.has(path2)||seen.has(path2))continue;if(!deps.isRepoRoot(path2))continue;seen.add(path2),out.push(task)}return out}class TaskDeletionCoordinator{store;worktrees;forgetTask;onSalvage;onResidue;constructor(store,worktrees,forgetTask,onSalvage,onResidue){this.store=store;this.worktrees=worktrees;this.forgetTask=forgetTask;this.onSalvage=onSalvage;this.onResidue=onResidue}async prepare(id,opts){let task=this.store.get(id);if(!task)return!1;if(task.kind==="main")throw new CannotDeleteMainTaskError;if(task.deletion?.phase==="queued"||task.deletion?.phase==="running")return!0;let force=opts?.force===!0;if(task.worktreePath&&!force&&task.kind!=="dir"){let dirty=!1,ignored=[];try{if(dirty=await this.worktrees.isDirty(task.worktreePath),!dirty)ignored=await this.worktrees.ignoredWork(task.worktreePath)}catch{}if(dirty||ignored.length>0)throw new DirtyWorktreeError(task.id,ignored)}return await this.store.update(task.id,{deletion:{phase:"queued",force,deleteBranch:opts?.deleteBranch===!0,requestedAt:new Date().toISOString()}}),!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;try{if(task.worktreePath&&task.kind!=="dir")await this.worktrees.remove(task.worktreePath,{force:task.deletion.force,deleteBranch:task.deletion.deleteBranch===!0,repo:task.repo,branch:task.branch,onSalvage:(record)=>{if(record)this.onSalvage?.(task.id,record)},onResidue:(residue)=>this.onResidue?.(task.id,residue)})}catch(cause){let failure=new WorktreeRemoveFailedError(task.id,cause);throw await this.store.update(task.id,{deletion:{...task.deletion,phase:"error",error:errorMessage(failure)}}),failure}await this.store.remove(task.id),this.forgetTask(task.id)}async deleteNow(id,opts){if(!await this.prepare(id,opts))return;if(!await this.begin(id))return;await this.finish(id)}}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","kobe"]);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){return tokens.join("-").slice(0,32).replace(/-+$/,"")}function deriveConventionBranch(title,style){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)||"task"}`}return capSlug(tokens)||"task"}function uniqueBranchName(base,taken,taskId){if(!taken.has(base))return base;for(let n=2;n<=99;n+=1){let candidate=`${base}-${n}`;if(!taken.has(candidate))return candidate}return`${base}-${taskId.slice(-6).toLowerCase()}`}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){let trimmed=title.trim();if(!trimmed)throw Error("setTitle: title is required (empty or whitespace-only rejected)");let task=this.requireTask(id);if(task.title===trimmed&&task.scratch!==!0)return;await this.store.update(task.id,{title:trimmed,...task.scratch===!0?{scratch:!1}:{}}),await this.followBranchToTitle(task,trimmed)}async followBranchToTitle(taskBefore,newTitle){if(taskBefore.kind==="main"||!taskBefore.worktreePath)return;if(!isPlaceholderDerivedBranch(taskBefore.branch,taskBefore.id))return;try{let names=await this.worktrees.listBranchNames(taskBefore.repo),base=deriveConventionBranch(newTitle,inferBranchStyle(names));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(task.kind==="main")throw Error("setBranch: a main task tracks the repo's own branch; rename it with git directly");if(task.kind==="dir")throw Error("setBranch: a directory task tracks its own checkout; rename branches with git directly");if(task.branch===trimmed)return;if(task.worktreePath)await this.worktrees.renameBranch(task.worktreePath,task.branch,trimmed);await this.store.update(task.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){let task=this.requireTask(id),nextEffort=effort?.trim()?effort.trim():void 0,effortChanges=effort!==void 0&&task.modelEffort!==nextEffort;if(task.vendor===vendor&&!effortChanges)return;await this.store.update(task.id,{vendor,...effort!==void 0?{modelEffort:nextEffort}:{}})}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);if(task.command===trimmed&&(vendor===void 0||task.vendor===vendor))return;await this.store.update(task.id,{command:trimmed,...vendor?{vendor}:{}})}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 moveTask(id,delta){let task=this.requireTask(id),isMain=task.kind==="main",groupIds=this.store.list().filter((t)=>isMain?(t.kind??"task")==="main":(t.kind??"task")!=="main"&&t.repo===task.repo&&(t.pinned??!1)===(task.pinned??!1)).map((t)=>String(t.id));await this.store.move(task.id,delta,groupIds)}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 setPRStatus(id,prStatus){let task=this.requireTask(id);if((task.prStatus?.lastError??null)===(prStatus?.lastError??null)&&samePrStatus(task.prStatus,prStatus??void 0))return;await this.store.update(task.id,{prStatus:prStatus??void 0})}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 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((resolve)=>{release=resolve}),await previous;try{return await this.pickLocked(repo)}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)}}class WorktreeCoordinator{store;worktrees;canonPath;ensureProject;slugs;worktreeLocks=new Map;adoptLocks=new Map;reservedBranches=new Set;constructor(store,worktrees,canonPath2,ensureProject){this.store=store,this.worktrees=worktrees,this.canonPath=canonPath2,this.ensureProject=ensureProject,this.slugs=new SlugAllocator((repo)=>this.store.list().filter((t)=>t.repo===repo&&t.kind!=="main").map((t)=>{return t.worktreePath.match(/([^/\\]+)[/\\]*$/)?.[1]??""}).filter((s)=>s.length>0))}forget(id){this.worktreeLocks.delete(id)}async ensure(task){let inflight=this.worktreeLocks.get(task.id);if(inflight)return inflight;let work=this.createWorktree(task);this.worktreeLocks.set(task.id,work);try{return await work}finally{this.worktreeLocks.delete(task.id)}}async createWorktree(task){let slug=await this.slugs.allocate(task.repo),branch=task.branch||await this.deriveAutoBranch(task),baseRef=task.baseRef,info;try{info=await this.worktrees.createForTask({repo:task.repo,slug,branch,baseRef})}catch(err){throw this.slugs.cancel(task.repo,slug),err}finally{this.reservedBranches.delete(branch)}try{await this.store.update(task.id,{worktreePath:info.path,branch})}catch(err){throw await this.rollbackWorktree(info.path),this.slugs.cancel(task.repo,slug),err}return this.slugs.commit(task.repo,slug),info.path}async deriveAutoBranch(task){let names=await this.worktrees.listBranchNames(task.repo),base=deriveConventionBranch(task.title,inferBranchStyle(names)),taken=new Set([...names,...this.reservedBranches]),branch=uniqueBranchName(base,taken,task.id);return this.reservedBranches.add(branch),branch}async rollbackWorktree(worktreePath){try{await this.worktrees.remove(worktreePath,{force:!0})}catch(err){console.error(`[rove] ensureWorktree rollback failed for ${worktreePath}:`,err)}}async discoverAdoptable(repo){let all=await this.worktrees.listAll(repo),linked=new Set(this.store.list().filter((t)=>t.worktreePath).map((t)=>this.canonPath(t.worktreePath)));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 existing=this.store.list().find((t)=>t.worktreePath&&this.canonPath(t.worktreePath)===target);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;worktreeCoordinator;editor;deletions;tasksAcc;activeTaskAcc;unsubscribeStore;mainTasks;tearDownSession;constructor(deps){this.store=deps.store,this.worktrees=deps.worktrees,this.tearDownSession=deps.tearDownSession,this.worktreeCoordinator=new WorktreeCoordinator(this.store,this.worktrees,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,(id)=>this.worktreeCoordinator.forget(id),deps.onSalvage,deps.onWorktreeResidue),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:(path2)=>isGitRepo(path2)&&resolveRepoRoot(path2)===path2});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),this.store.touchRecency(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)=>createTaskRow({store:this.store,mainTasks:this.mainTasks},input);openDirectoryTask=(input)=>openDirectoryTaskRow({store:this.store},input);adoptScratchRepo=(id,repo)=>adoptScratchRepoRow({store:this.store},id,repo);async ensureMainTask(repo){return await this.mainTasks.ensure(repo)}async ensureWorktree(id){let task=this.requireTask(id);if(task.deletion)throw new TaskDeletingError(String(task.id));if(task.kind==="main")return repoWorkingDir(task.repo);if(task.kind==="dir")return task.worktreePath;if(task.worktreePath){if(await this.worktrees.pathExists(task.worktreePath))return task.worktreePath;return await this.worktrees.pruneWorktrees(task.repo),await this.store.update(task.id,{worktreePath:""}),this.worktreeCoordinator.ensure({...task,worktreePath:""})}return this.worktreeCoordinator.ensure(task)}async clearWorktreePath(id){let task=this.store.get(id);if(!task||!task.worktreePath)return;if(task.kind==="main"||task.kind==="dir")return;await this.store.update(task.id,{worktreePath:""}),this.worktreeCoordinator.forget(task.id)}setTitle=(id,title)=>this.editor.setTitle(id,title);setBranch=(id,branch)=>this.editor.setBranch(id,branch);observeLanguage=(id,text)=>this.editor.observeLanguage(id,text);setVendor=(id,vendor,effort)=>this.editor.setVendor(id,vendor,effort);setCommand=(id,command,vendor)=>this.editor.setCommand(id,command,vendor);setPinned=(id,pinned)=>this.editor.setPinned(id,pinned);moveTask=(id,delta)=>this.editor.moveTask(id,delta);setStatus=(id,status)=>this.editor.setStatus(id,status);setPRStatus=(id,prStatus)=>this.editor.setPRStatus(id,prStatus);setLinkedWorkItem=(id,item)=>this.editor.setLinkedWorkItem(id,item);setQuotaResume=(id,state)=>this.editor.setQuotaResume(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){let task=this.requireTask(id);if(task.kind==="main")throw Error("landTask: a main task has no branch to land");if(task.kind==="dir")throw Error("landTask: a directory task has no Rove-managed branch to land");if(task.deletion)throw new TaskDeletingError(String(task.id));return landPreflight(task)}async landTask(id,opts){let task=this.requireTask(id);if(task.deletion)throw new TaskDeletingError(String(task.id));return landTaskWithCleanup(task,opts??{},{worktrees:this.worktrees,clearWorktreePath:(tid)=>this.clearWorktreePath(tid),tearDownSession:this.tearDownSession})}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}}
5
+ export{parseDirtyPaths,Orchestrator};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{GitWorktreeManager}from"./chunk-hhvzpps8.js";import"./chunk-4ezfas24.js";import"./chunk-6xppe9bj.js";import"./chunk-n5sssj80.js";import"./chunk-bkba9c4n.js";import"./chunk-g2advs8t.js";import"./chunk-2fypr41w.js";import"./chunk-87y9qjcm.js";import"./chunk-m57cv76y.js";import"./chunk-a3a9gdc4.js";import"./chunk-9c1e5z6a.js";import"./chunk-6dn6h640.js";import"./chunk-cb2fpajx.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";export{GitWorktreeManager};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{writeOutdatedCache}from"./chunk-86bjqmg1.js";import{PluginCliError,installPlugin}from"./chunk-86aac2wh.js";import{loadPluginRegistry,pluginCheckoutDir,pluginConfigDir,pluginDataDir,pluginStateDir,removePluginEntry,savePluginRegistry}from"./chunk-d393z1mj.js";import"./chunk-a3zmjcbd.js";import"./chunk-kwe61yx7.js";import"./chunk-w7zv47mp.js";import"./chunk-6dn6h640.js";import{activeCliName}from"./chunk-ef11h2am.js";import"./chunk-8ddjfc2v.js";import"./chunk-cb2fpajx.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";import{execFileSync}from"child_process";import{existsSync,mkdirSync,readdirSync,renameSync}from"fs";import{join}from"path";function gitOut(args,cwd){try{return execFileSync("git",args,{cwd,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}}function checkEntry(entry){let repoSpec=entry.source.spec.split("/").slice(0,2).join("/"),localSha=gitOut(["rev-parse","HEAD"],pluginCheckoutDir(entry.id)),remote=gitOut(["ls-remote",`https://github.com/${repoSpec}.git`,"HEAD"]),remoteSha=remote?remote.split(/\s+/)[0]??null:null;return{id:entry.id,spec:entry.source.spec,version:entry.version,localSha,remoteSha,behind:Boolean(localSha&&remoteSha&&localSha!==remoteSha)}}function listOutdated(){let rows=loadPluginRegistry().plugins.filter((p)=>p.source.kind==="github").map(checkEntry);return writeOutdatedCache(rows.filter((r)=>r.behind).map((r)=>r.id)),rows}function printOutdated(){let rows=listOutdated();if(rows.length===0){console.log("no GitHub-installed plugins.");return}for(let r of rows){let status=r.behind?"update available":r.remoteSha===null?"remote unreachable":r.localSha===null?"local sha unreadable":"up to date";console.log(`${r.id} v${r.version} ${status}`)}}function migrateRenamedPlugin(oldId,newId){for(let dirOf of[pluginConfigDir,pluginStateDir]){let from=dirOf(oldId),to=dirOf(newId);if(!existsSync(from))continue;mkdirSync(to,{recursive:!0});for(let entry of readdirSync(from)){if(existsSync(join(to,entry)))continue;renameSync(join(from,entry),join(to,entry))}}savePluginRegistry(removePluginEntry(loadPluginRegistry(),oldId)),console.log(`renamed ${oldId} \u2192 ${newId}: settings and state carried over, old entry unregistered`),console.log(` leftover checkout (safe to remove): ${pluginDataDir(oldId)}`)}async function updatePlugins(ids,opts){let rows=listOutdated(),targets;if(opts.all){if(targets=rows.filter((r)=>r.behind),targets.length===0){console.log("all plugins up to date.");return}}else{if(ids.length===0)throw new PluginCliError("update takes plugin ids or --all");targets=ids.map((id)=>{let row=rows.find((r)=>r.id===id);if(!row)throw new PluginCliError(`\`${id}\` is not a GitHub-installed plugin; see \`${activeCliName()} plugin list\``);return row})}for(let target of targets){if(!target.behind&&!ids.includes(target.id))continue;console.log(`updating ${target.id} (${target.spec})\u2026`);let installedId=await installPlugin(target.spec,{yes:opts.yes});if(installedId!==target.id)migrateRenamedPlugin(target.id,installedId)}listOutdated()}export{updatePlugins,printOutdated};
@@ -0,0 +1,6 @@
1
+ // @bun
2
+ function toError(err){if(err instanceof Error)return err;let text;try{text=typeof err==="string"?err:JSON.stringify(err)}catch{text=String(err)}return Error(text)}function formatCrashEntry(kind,err,now=new Date){let e=toError(err);return`[${now.toISOString()}] daemon ${kind}: ${e.stack??`${e.name}: ${e.message}`}
3
+ `}function formatDaemonError(subsystem,err,now=new Date){let e=toError(err);return`[${now.toISOString()}] daemon error [${subsystem}]: ${e.stack??`${e.name}: ${e.message}`}
4
+ `}function logDaemonError(subsystem,err){process.stderr.write(formatDaemonError(subsystem,err))}function formatDaemonInfo(subsystem,message,now=new Date){return`[${now.toISOString()}] daemon [${subsystem}]: ${message}
5
+ `}function logDaemonInfo(subsystem,message){process.stderr.write(formatDaemonInfo(subsystem,message))}var onRejection,onException;function installDaemonCrashHandlers(log=(l)=>process.stderr.write(l)){if(onRejection||onException)return;onRejection=(reason)=>log(formatCrashEntry("unhandledRejection",reason)),onException=(err)=>log(formatCrashEntry("uncaughtException",err)),process.on("unhandledRejection",onRejection),process.on("uncaughtException",onException)}
6
+ export{logDaemonError,logDaemonInfo,installDaemonCrashHandlers};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{applyOnboardingChoices,detectShell,envReadyForTasks,installCompletions,maybeRunOnboarding}from"./chunk-h5a7necv.js";import"./chunk-4ppq1q86.js";import"./chunk-9kyf6d1j.js";import"./chunk-bqhq43zx.js";import"./chunk-a6sxg2gx.js";import"./chunk-a3zmjcbd.js";import"./chunk-gfh0ws3y.js";import"./chunk-zaxdx0wd.js";import"./chunk-sexavazm.js";import"./chunk-sdyzxayr.js";import"./chunk-jvxh93nz.js";import"./chunk-rzvzcqvx.js";import"./chunk-m7zq502q.js";import"./chunk-n4dmwkfa.js";import"./chunk-w7zv47mp.js";import"./chunk-gwcneg6c.js";import"./chunk-ezty9a39.js";import"./chunk-7jkf3tvr.js";import"./chunk-2fypr41w.js";import"./chunk-87y9qjcm.js";import"./chunk-m57cv76y.js";import"./chunk-nywh3yb9.js";import"./chunk-xy8be792.js";import"./chunk-a3a9gdc4.js";import"./chunk-2brn8e1y.js";import"./chunk-9c1e5z6a.js";import"./chunk-6dn6h640.js";import"./chunk-bq3fyc0p.js";import"./chunk-ef11h2am.js";import"./chunk-8ddjfc2v.js";import"./chunk-cb2fpajx.js";import"./chunk-grh6dvfv.js";import"./chunk-pw6yfj10.js";import"./chunk-x760j10f.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";export{maybeRunOnboarding,installCompletions,envReadyForTasks,detectShell,applyOnboardingChoices};
@@ -0,0 +1,8 @@
1
+ // @bun
2
+ import{isEngineActivityKind}from"./chunk-3ry6prwn.js";import{loadPluginRegistry,readPluginManifest}from"./chunk-d393z1mj.js";import{flagValue}from"./chunk-jvxh93nz.js";import{connectIfRunning}from"./chunk-5bn91n19.js";import{createEngineHookAdapter}from"./chunk-m7zq502q.js";import{getPersistedString,setPersistedString}from"./chunk-87y9qjcm.js";import{ALL_VENDORS}from"./chunk-bq3fyc0p.js";import{activeCliName}from"./chunk-ef11h2am.js";import{readRoveEnv}from"./chunk-3bfm4hrs.js";import{__require}from"./chunk-wqfp8d9w.js";import{join,resolve}from"path";var STDIN_READ_TIMEOUT_MS=500;async function readTextWithTimeout(read,timeoutMs=STDIN_READ_TIMEOUT_MS){let raceTimer;try{return await Promise.race([read(),new Promise((resolve2)=>{raceTimer=setTimeout(()=>resolve2(""),timeoutMs)})])}finally{if(raceTimer!==void 0)clearTimeout(raceTimer)}}async function readStdinPayload(){try{let text=await readTextWithTimeout(()=>Bun.stdin.text());if(!text.trim())return{};let parsed=JSON.parse(text);return parsed&&typeof parsed==="object"&&!Array.isArray(parsed)?parsed:{}}catch{return{}}}async function runHookSubcommand(argv){let[verb,...rest]=argv;if(verb==="setup"){await runHookSetup(rest);return}if(verb==="cleanup"){await runHookCleanup();return}try{if(!verb||!isEngineActivityKind(verb))return;let payload=await readStdinPayload(),taskId=flagValue(rest,"--task-id"),cwd=typeof payload.cwd==="string"&&payload.cwd?payload.cwd:process.cwd(),envTaskId=process.env.KOBE_TASK_ID,envTabId=process.env.KOBE_TAB_ID,engine=flagValue(rest,"--engine"),adapters=activityHookAdapters().filter((a)=>!engine||a.vendor===engine),detail;for(let adapter of adapters)if(detail=adapter.activityDetailFromPayload(verb,payload),detail)break;let session;for(let adapter of adapters)if(session=adapter.sessionFromPayload(payload),session)break;let client=await connectIfRunning();if(!client)return;try{let effectiveTaskId=taskId??envTaskId;await client.request("engine.reportEvent",{...effectiveTaskId?{taskId:effectiveTaskId}:{cwd},kind:verb,...engine?{engine}:{},...envTabId?{tabId:envTabId}:{},...detail?{detail}:{},...session?{sessionId:session.sessionId}:{},...session?.transcriptPath?{transcriptPath:session.transcriptPath}:{}})}finally{client.close()}}catch(err){if(readRoveEnv("HOOK_DEBUG"))console.error(`[rove hook] ${verb} failed:`,err instanceof Error?err.message:String(err))}}var SYNC_SETTING_KEY="externalWorktreeSync";function worktreeSyncAdapters(){return ALL_VENDORS.map((v)=>createEngineHookAdapter(v)).filter((a)=>a.supportsWorktreeSync())}function activityHookAdapters(){return ALL_VENDORS.map((v)=>createEngineHookAdapter(v)).filter((a)=>a.supportsHooks())}function globalSettingsPath(){return worktreeSyncAdapters()[0]?.globalSettingsPath()}function persistedSyncPath(stored){if(!stored||stored==="off")return;if(stored==="global")return globalSettingsPath();if(stored.startsWith("repo:"))return join(resolve(stored.slice(5)),".claude","settings.json");return stored}async function ensureGlobalKobeHooks(){try{let{isRovePluginEnabled,detectLegacyInstalls,migrationHint}=await import("./chunk-9twmp5m9.js"),pluginMode=isRovePluginEnabled();if(pluginMode){let hint=migrationHint(detectLegacyInstalls(),activeCliName());if(hint)process.stderr.write(`
3
+ ${hint}
4
+ `)}let toolEvents=pluginsWantToolEvents();for(let a of activityHookAdapters()){if(pluginMode&&a.vendor==="claude")continue;let enginePath=a.globalSettingsPath();if(!enginePath)continue;await a.installActivityHooks(enginePath,{toolEvents}),await a.removeWorktreeWatchHook(enginePath)}await cleanupWorktreeSyncHook()}catch{}}function pluginsWantToolEvents(){try{for(let entry of loadPluginRegistry().plugins){if(!entry.enabled)continue;try{if(readPluginManifest(entry.root).manifest.events.some((e)=>e.on.startsWith("tool.")))return!0}catch{}}}catch{}return!1}async function cleanupWorktreeSyncHook(){let adapters=worktreeSyncAdapters();if(adapters.length===0)return;let stored=getPersistedString(SYNC_SETTING_KEY),paths=new Set;for(let adapter of adapters){let file=adapter.globalSettingsPath();if(file)paths.add(file)}let prev=persistedSyncPath(stored);if(prev)paths.add(prev);for(let a of adapters)for(let p of paths)await a.removeWorktreeSyncHook(p);if(stored!=="off")setPersistedString(SYNC_SETTING_KEY,"off")}async function runHookCleanup(){let claude=activityHookAdapters().find((a)=>a.vendor==="claude"),path=claude?.globalSettingsPath();if(!claude||!path){process.stdout.write(`rove hook cleanup: no Claude hook adapter \u2014 nothing to do.
5
+ `);return}await claude.removeActivityHooks(path),await claude.removeWorktreeWatchHook(path),process.stdout.write([`rove hook cleanup: removed Rove's settings-managed hooks from ${path}.`,"Only Rove's own entries were touched; your other hooks are intact.","The Claude Code plugin's hooks.json now carries these events (if the plugin","is not installed, the next Rove launch reinstalls the settings-managed set).",""].join(`
6
+ `))}async function runHookSetup(_argv){await cleanupWorktreeSyncHook(),process.stdout.write(["rove hook setup is deprecated and now a no-op (cleanup only).","","The old external-worktree sync used a global WorktreeCreate hook, which is","a VCS provider hook \u2014 its presence broke `claude --worktree` / EnterWorktree","in every repo. Any hook Rove previously installed has been removed.","","Sync is now automatic: a `claude --worktree` (or any session) started in a","worktree under a repo Rove already tracks is adopted as a task on launch.","To adopt existing worktrees on demand, use the New Task dialog or `rove adopt`.",""].join(`
7
+ `))}
8
+ export{readTextWithTimeout,runHookSubcommand,ensureGlobalKobeHooks};