@sma1lboy/rove 0.9.272 → 0.9.273

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-aac92ra2.js → chunk-03rbg95q.js} +8 -8
  2. package/dist/cli/{chunk-f38caghx.js → chunk-0ppkvphp.js} +1 -1
  3. package/dist/cli/{chunk-qdvhg3rg.js → chunk-15fp4p9v.js} +1 -1
  4. package/dist/cli/{chunk-xfps1yce.js → chunk-1h1ejknz.js} +1 -1
  5. package/dist/cli/{chunk-0dh1vfer.js → chunk-1n7tcq6a.js} +1 -1
  6. package/dist/cli/{chunk-35jbdbkm.js → chunk-22d4fjjb.js} +1 -1
  7. package/dist/cli/chunk-34tbcjdy.js +2 -0
  8. package/dist/cli/{chunk-3249q53j.js → chunk-387ecqb8.js} +1 -1
  9. package/dist/cli/{chunk-a3jqg8g2.js → chunk-3v9s3bcz.js} +1 -1
  10. package/dist/cli/{chunk-16x3ftbg.js → chunk-4d50eg3p.js} +8 -8
  11. package/dist/cli/{chunk-hknt4xza.js → chunk-4dxc29mm.js} +1 -1
  12. package/dist/cli/{chunk-7vkvfawc.js → chunk-4hnz77xf.js} +1 -1
  13. package/dist/cli/{chunk-9kje0m11.js → chunk-5c4vr45n.js} +1 -1
  14. package/dist/cli/{chunk-wve9y405.js → chunk-5dd7trgf.js} +1 -1
  15. package/dist/cli/{chunk-js50sr3w.js → chunk-5zsdjrgz.js} +1 -1
  16. package/dist/cli/{chunk-sk19nvdj.js → chunk-63t3sh8c.js} +1 -1
  17. package/dist/cli/{chunk-9fynr5t1.js → chunk-64f3ncxx.js} +1 -1
  18. package/dist/cli/{chunk-xdzd6v4r.js → chunk-655kt3e1.js} +1 -1
  19. package/dist/cli/{chunk-6b91pz64.js → chunk-68wpp5y8.js} +2 -2
  20. package/dist/cli/{chunk-k0hp75n2.js → chunk-6d3espha.js} +1 -1
  21. package/dist/cli/{chunk-0j7j051w.js → chunk-6h4qdckd.js} +1 -1
  22. package/dist/cli/{chunk-r5h3dc1y.js → chunk-6jj82bej.js} +1 -1
  23. package/dist/cli/chunk-6xr1prk6.js +3 -0
  24. package/dist/cli/{chunk-hyydmt77.js → chunk-78v1q5mz.js} +1 -1
  25. package/dist/cli/{chunk-nrwyxhtz.js → chunk-80t9z2b2.js} +1 -1
  26. package/dist/cli/chunk-8zw085pc.js +3 -0
  27. package/dist/cli/chunk-97m9nyw7.js +2 -0
  28. package/dist/cli/{chunk-ma74dbah.js → chunk-98s0fhmv.js} +3 -3
  29. package/dist/cli/chunk-9bae7f3m.js +4 -0
  30. package/dist/cli/chunk-9eqh0yph.js +2 -0
  31. package/dist/cli/{chunk-pe8affmn.js → chunk-a4x8h7zv.js} +1 -1
  32. package/dist/cli/chunk-adj65prq.js +11 -0
  33. package/dist/cli/chunk-ajad94vy.js +2 -0
  34. package/dist/cli/chunk-b5c7p22a.js +2 -0
  35. package/dist/cli/chunk-b5pytfgm.js +2 -0
  36. package/dist/cli/chunk-b68dd36h.js +3 -0
  37. package/dist/cli/{chunk-27bn9kmy.js → chunk-bbksmb5a.js} +1 -1
  38. package/dist/cli/{chunk-t6wwh4jk.js → chunk-bm0qe5gy.js} +2 -2
  39. package/dist/cli/{chunk-yb26ekcg.js → chunk-bmg5y8eh.js} +1 -1
  40. package/dist/cli/chunk-bp0en3f7.js +2 -0
  41. package/dist/cli/{chunk-ncvesyjr.js → chunk-cyzp2btw.js} +5 -5
  42. package/dist/cli/{chunk-dyzvnvjz.js → chunk-e01321zn.js} +1 -1
  43. package/dist/cli/{chunk-2cg3b07h.js → chunk-ec7f3wcm.js} +2 -2
  44. package/dist/cli/{chunk-1sg8c2ke.js → chunk-ecrw211n.js} +1 -1
  45. package/dist/cli/{chunk-9m4gje59.js → chunk-edrxs4cy.js} +3 -3
  46. package/dist/cli/{chunk-83pzw53w.js → chunk-f5fk30x1.js} +1 -1
  47. package/dist/cli/{chunk-0hmwkm43.js → chunk-f6hgkc7g.js} +1 -1
  48. package/dist/cli/{chunk-t4epmfd1.js → chunk-fexzfgd1.js} +1 -1
  49. package/dist/cli/chunk-fkn6z4yq.js +55 -0
  50. package/dist/cli/{chunk-2kx64z0b.js → chunk-fy4eyzbt.js} +1 -1
  51. package/dist/cli/{chunk-pss7gg43.js → chunk-g263457v.js} +1 -1
  52. package/dist/cli/{chunk-m9qg3yck.js → chunk-g53yqr82.js} +3 -3
  53. package/dist/cli/{chunk-rx11ezbp.js → chunk-hfdk7j28.js} +1 -1
  54. package/dist/cli/{chunk-fsb81hkp.js → chunk-jbdg5ggt.js} +1 -1
  55. package/dist/cli/{chunk-x564fc4v.js → chunk-jcrg7pjv.js} +1 -1
  56. package/dist/cli/{chunk-62ka1j1s.js → chunk-jd7p0y5x.js} +1 -1
  57. package/dist/cli/{chunk-6vhr65h3.js → chunk-jwedmkm8.js} +1 -1
  58. package/dist/cli/chunk-jwta8rp8.js +3 -0
  59. package/dist/cli/{chunk-x5j66mvs.js → chunk-k5k2jpxk.js} +3 -3
  60. package/dist/cli/chunk-k9zarq1x.js +96 -0
  61. package/dist/cli/{chunk-67nb2sdd.js → chunk-kgs56yns.js} +1 -1
  62. package/dist/cli/{chunk-zjq52kfk.js → chunk-kx53jjee.js} +1 -1
  63. package/dist/cli/chunk-m1n1rszx.js +2 -0
  64. package/dist/cli/{chunk-djkmaabe.js → chunk-mmq6922s.js} +2 -2
  65. package/dist/cli/{chunk-hk9p0gjs.js → chunk-ms92andd.js} +1 -1
  66. package/dist/cli/{chunk-n2cb06nr.js → chunk-n2ejvjrd.js} +1 -1
  67. package/dist/cli/{chunk-682xhgvd.js → chunk-nf3kg9rz.js} +3 -3
  68. package/dist/cli/{chunk-ec08vr9c.js → chunk-nyn1j7cf.js} +1 -1
  69. package/dist/cli/chunk-pm91e38v.js +4 -0
  70. package/dist/cli/{chunk-nkd5as36.js → chunk-qd72kqg9.js} +12 -8
  71. package/dist/cli/{chunk-07daqn0z.js → chunk-qfe1wf5c.js} +1 -1
  72. package/dist/cli/{chunk-mkb88wzr.js → chunk-qnaxnx5w.js} +1 -1
  73. package/dist/cli/{chunk-4vad6e9t.js → chunk-qsqde2kj.js} +1 -1
  74. package/dist/cli/{chunk-zpvwb2z6.js → chunk-qvnr8c18.js} +1 -1
  75. package/dist/cli/chunk-r5j8fycq.js +3 -0
  76. package/dist/cli/chunk-ra18rjj4.js +20 -0
  77. package/dist/cli/chunk-rkn2fby6.js +3 -0
  78. package/dist/cli/{chunk-0vq7yqj0.js → chunk-rzef6szc.js} +1 -1
  79. package/dist/cli/{chunk-32yhdeak.js → chunk-sap4tggv.js} +1 -1
  80. package/dist/cli/{chunk-182qa2d6.js → chunk-snqa07h6.js} +1 -1
  81. package/dist/cli/chunk-sqxdfzdq.js +5 -0
  82. package/dist/cli/{chunk-adv5s59k.js → chunk-sz2xs0a2.js} +1 -1
  83. package/dist/cli/{chunk-cynqvjk8.js → chunk-sza3tyhs.js} +2 -2
  84. package/dist/cli/{chunk-k8bevbmr.js → chunk-t34kjg5y.js} +4 -2
  85. package/dist/cli/{chunk-e31be3k4.js → chunk-vjp7drne.js} +1 -1
  86. package/dist/cli/{chunk-ar2zhdhh.js → chunk-vw34sdkk.js} +2 -2
  87. package/dist/cli/chunk-w2xdmnnf.js +2 -0
  88. package/dist/cli/{chunk-k0hwkm75.js → chunk-wqvmvgaf.js} +1 -1
  89. package/dist/cli/{chunk-eq8kb4kj.js → chunk-x20bs6bs.js} +1 -1
  90. package/dist/cli/{chunk-6cvgvmgg.js → chunk-x6xevrtz.js} +1 -1
  91. package/dist/cli/{chunk-jeve2yf8.js → chunk-x7yxf5b5.js} +2 -2
  92. package/dist/cli/{chunk-7drxm3c8.js → chunk-xgm43xm3.js} +1 -1
  93. package/dist/cli/{chunk-rwnd0t4w.js → chunk-xkmdxcjg.js} +1 -1
  94. package/dist/cli/{chunk-9rtw4k38.js → chunk-xmqv1zx6.js} +1 -1
  95. package/dist/cli/{chunk-ct896m44.js → chunk-xn7bk4f9.js} +1 -1
  96. package/dist/cli/chunk-zaw71cf4.js +13 -0
  97. package/dist/cli/{chunk-pn2f24wx.js → chunk-zxkjpfp5.js} +1 -1
  98. package/dist/cli/chunk-zybjm7d5.js +2 -0
  99. package/dist/cli/index.js +4 -4
  100. package/dist/cli/rove.js +1 -1
  101. package/dist/completions/rove.bash +1 -1
  102. package/dist/completions/rove.fish +1 -1
  103. package/dist/completions/rove.zsh +1 -1
  104. package/dist/skills/rove/SKILL.md +7 -2
  105. package/dist/skills/rove/references/api-flags.md +24 -18
  106. package/dist/web/assets/index-Bc0LgdLr.js +113 -0
  107. package/dist/web/assets/{index-DfqQoIzt.css → index-sA2EEQJw.css} +1 -1
  108. package/dist/web/index.html +2 -2
  109. package/package.json +3 -3
  110. package/dist/cli/chunk-08gdekk1.js +0 -3
  111. package/dist/cli/chunk-0ay6bvfg.js +0 -2
  112. package/dist/cli/chunk-3w2vpggj.js +0 -5
  113. package/dist/cli/chunk-4hf2y0k4.js +0 -2
  114. package/dist/cli/chunk-6v5mw6t7.js +0 -3
  115. package/dist/cli/chunk-ay9ys27w.js +0 -3
  116. package/dist/cli/chunk-crhbpmfp.js +0 -3
  117. package/dist/cli/chunk-effc77em.js +0 -4
  118. package/dist/cli/chunk-f09ex46c.js +0 -3
  119. package/dist/cli/chunk-gb3zemrx.js +0 -2
  120. package/dist/cli/chunk-kzq2mqjz.js +0 -2
  121. package/dist/cli/chunk-mrdxvf76.js +0 -3
  122. package/dist/cli/chunk-p5mtfzn3.js +0 -55
  123. package/dist/cli/chunk-phpaypqx.js +0 -3
  124. package/dist/cli/chunk-px1gd2zk.js +0 -96
  125. package/dist/cli/chunk-qkbvxa7b.js +0 -2
  126. package/dist/cli/chunk-r17r542e.js +0 -2
  127. package/dist/cli/chunk-s4nrmbqh.js +0 -13
  128. package/dist/cli/chunk-s70mf80s.js +0 -11
  129. package/dist/cli/chunk-t19001hn.js +0 -2
  130. package/dist/cli/chunk-v967kt9x.js +0 -2
  131. package/dist/cli/chunk-x9y8yx2f.js +0 -2
  132. package/dist/cli/chunk-yyzpqxt5.js +0 -2
  133. package/dist/cli/chunk-zetj4rnw.js +0 -20
  134. package/dist/web/assets/index-CJgzqO7A.js +0 -111
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{buildEngineSessionLaunch,trustEngineWorktree}from"./chunk-qdvhg3rg.js";import{engineForkArgv,engineResumeArgv,withPinnedSessionId}from"./chunk-0vq7yqj0.js";import{engineEntry,engineStatusPrefixes,isEnginePlaceholderTitle,stripEngineStatusPrefix}from"./chunk-9m4gje59.js";import{tildify}from"./chunk-2r1fgng9.js";import{t}from"./chunk-bj6sa371.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";function engineTabArgv(tab,base,live,fallbackVendor){if(tab.forkFrom&&!tab.spawned&&!live){let forked=tab.vendor?engineForkArgv(base,tab.vendor,tab.forkFrom,tab.sessionId??null):null;if(forked)return forked}if(!tab.sessionId)return base;let vendor=tab.vendor??fallbackVendor;if(tab.spawned&&!live)return engineResumeArgv(base,vendor,tab.sessionId)??base;return withPinnedSessionId(base,vendor,()=>tab.sessionId).argv}function engineTabSpawnFor(state,tab,base,opts){let{live,shell,prompt}=opts,firstEngine=state.tabs.find((t2)=>t2.kind==="engine"),fresh=!tab.spawned&&!live,tabPrompt=fresh?tab.initialPrompt?.trim():void 0,wantsPrompt=!!prompt&&tab.id===firstEngine?.id&&fresh,isFreshFirstEngine=tab.id===firstEngine?.id&&fresh,promptIntent=tabPrompt?{kind:"explicit",prompt:tabPrompt}:wantsPrompt?{kind:"new-task",prompt}:isFreshFirstEngine?{kind:"repo-init"}:{kind:"none"},ref=tab.ptyTask;trustEngineWorktree(tab.vendor??opts.task.vendor,ref?.worktree??opts.worktreePath);let launch=buildEngineSessionLaunch({task:ref?{...opts.task,id:ref.id,kind:"task"}:opts.task,worktreePath:ref?.worktree??opts.worktreePath,shell,argv:engineTabArgv(tab,base,live,opts.task.vendor),promptIntent,protocolGates:opts.protocolGates,inlineImages:opts.inlineImages,tabId:ref?ref.tabId??"tab-1":tab.id});return{command:launch.command,...launch.firstMessage?{firstMessage:launch.firstMessage,engineBin:base[0]}:{}}}function tabExitAction(tab,deadOnAttach,resumeTried){if(tab.kind==="engine"&&deadOnAttach&&!!tab.sessionId&&tab.spawned&&!resumeTried)return"resume";return"close"}import*as fs from"fs";import*as os from"os";function pathLeaf(p){return p.slice(lastSeparator(p)+1)}function lastSeparator(p){return pathSyntax(p).sep==="\\"||process.platform==="win32"&&!p.startsWith("/")?Math.max(p.lastIndexOf("/"),p.lastIndexOf("\\")):p.lastIndexOf("/")}function expandHome(p){if(p==="~")return os.homedir();if(p.startsWith("~/")||process.platform==="win32"&&p.startsWith("~\\"))return os.homedir()+p.slice(1);return p}function stripTrailingSlash(p){let root=pathSyntax(p).parse(p).root,end=p.length;while(end>root.length&&lastSeparator(p.slice(0,end))===end-1)end--;return p.slice(0,end)}function splitPathForDirSuggest(value){if(!value)return{base:"",filter:""};let expanded=expandHome(value==="~"?"~/":value),lastSlash=lastSeparator(expanded);if(lastSlash===-1)return{base:"",filter:expanded};return{base:expanded.slice(0,lastSlash+1),filter:expanded.slice(lastSlash+1)}}function listSubdirs(base){if(!base)return[];try{let entries=fs.readdirSync(base,{withFileTypes:!0}),out=[];for(let e of entries)if(e.isDirectory())out.push(e.name);return out.sort((a,b)=>a.localeCompare(b))}catch{return[]}}function filterSubdirs(all,filter){let f=filter.toLowerCase(),visible=f.startsWith(".")?all:all.filter((n)=>!n.startsWith("."));if(!f)return visible;return visible.filter((n)=>n.toLowerCase().startsWith(f))}function joinDrill(typedValue,baseExpanded,name){let out=`${baseExpanded+name}/`;if(typedValue.startsWith("~")){let short=tildify(out);if(short.startsWith("~"))return short==="~"?"~/":`${short}/`}return out}function joinPicked(typedValue,baseExpanded,name){let out=baseExpanded+name;if(typedValue.startsWith("~"))return tildify(out);return out}function depth(node){return node.kind==="leaf"?0:1+Math.max(...node.children.map(depth))}function siblingCount(root,id,orientation){let find=(node)=>{if(node.kind==="leaf")return null;if(node.orientation===orientation&&node.children.some((c)=>c.kind==="leaf"&&c.id===id))return node.children.length;for(let child of node.children){let n=find(child);if(n!==null)return n}return null};return find(root)??1}function splitFits(state,orientation,activeSize){let extent=orientation==="row"?activeSize.cols:activeSize.rows,min=orientation==="row"?20:6,n=siblingCount(state.root,state.activeLeafId,orientation);return Math.floor(extent*n/(n+1))-1>=min}function initialSplit(content){return{root:{kind:"leaf",id:"leaf-1",content},activeLeafId:"leaf-1",nextOrdinal:2}}function leaves(node){return node.kind==="leaf"?[node]:node.children.flatMap(leaves)}function splitActive(state,orientation,content,activeSize){if(activeSize&&!splitFits(state,orientation,activeSize))return state;let leaf={kind:"leaf",id:`leaf-${state.nextOrdinal}`,content},insert=(node)=>{if(node.kind==="leaf"){if(node.id!==state.activeLeafId)return node;return{kind:"group",orientation,children:[node,leaf]}}if(node.orientation===orientation){let i=node.children.findIndex((c)=>c.kind==="leaf"&&c.id===state.activeLeafId);if(i>=0){let children=[...node.children.slice(0,i+1),leaf,...node.children.slice(i+1)];return{...node,children}}}return{...node,children:node.children.map(insert)}},root=insert(state.root);if(!activeSize&&depth(root)>4)return state;return{root,activeLeafId:leaf.id,nextOrdinal:state.nextOrdinal+1}}function removeLeaf(state,id){let all=leaves(state.root);if(all.length<=1)return null;let prune=(node)=>{if(node.kind==="leaf")return node.id===id?null:node;let children=node.children.map(prune).filter((c)=>c!==null);if(children.length===0)return null;if(children.length===1)return children[0];return{...node,children}},root=prune(state.root);if(root===null)return null;if(leaves(root).length===all.length)return state;let order=all.map((l)=>l.id),removedIdx=order.indexOf(id),fallback=order[removedIdx>0?removedIdx-1:removedIdx+1],activeLeafId=state.activeLeafId===id?fallback:state.activeLeafId;return{...state,root,activeLeafId}}function renameLeaf(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,walk=(node)=>node.kind==="leaf"?node.id===id?{...node,title:next}:node:{...node,children:node.children.map(walk)};if(!leaves(state.root).some((l)=>l.id===id))return state;return{...state,root:walk(state.root)}}function cycleLeaf(state,delta){let order=leaves(state.root).map((l)=>l.id);if(order.length<=1)return state;let i=order.indexOf(state.activeLeafId),next=order[(i+delta+order.length)%order.length];return{...state,activeLeafId:next}}function hasEngineLeaf(tree){return!tree||leaves(tree.root).some((l)=>l.id==="leaf-1")}function isTabSplit(tree){return tree?leaves(tree.root).length>1:!1}function collapseSplit(next){let ls=leaves(next.root);return ls.length===1&&ls[0]?.id==="leaf-1"?null:next}function splitLeafPtyKey(tabKey,leafId){return leafId==="leaf-1"?tabKey:`${tabKey}::${leafId}`}var SHELL_LEAF_NAME="shell";function splitLeafNames(leafList,tabCommand,engineTitle,liveTitles){let basename=(argv)=>{let head=(argv??tabCommand)[0]??"",name=pathLeaf(head);return name.length>0?name:"?"},seen=new Map,out=new Map;for(let leaf of leafList){if(leaf.title){out.set(leaf.id,leaf.title);continue}let name=leaf.content===null?engineTitle||liveTitles?.get(leaf.id)||basename(leaf.content):liveTitles?.get(leaf.id)||SHELL_LEAF_NAME,n=(seen.get(name)??0)+1;seen.set(name,n),out.set(leaf.id,n===1?name:`${name} ${n}`)}return out}function meaningfulAutoTitle(autoTitle){let trimmed=(autoTitle??"").trim();if(trimmed.length<3)return null;if(/^[\d\s\p{P}\p{S}]+$/u.test(trimmed))return null;return trimmed}function stableRecordedTitle(raw,vendor){let recorded=raw?.trim();if(!recorded)return null;let cleaned=stripEngineStatusPrefix(recorded,vendor);if(cleaned===recorded&&isEngineDecoration(recorded,vendor))return null;if(isEnginePlaceholderTitle(cleaned,vendor))return null;return cleaned||null}function isEngineDecoration(text,vendor){let glyphs=new Set(engineStatusPrefixes(vendor));return[...text].every((ch)=>ch.trim().length===0||glyphs.has(ch))}function tabTitleStable(tab,taskVendor,liveVendor,liveTitle){if(liveVendor===null&&tab.kind==="engine")return tabTitle({...tab,kind:"command",lastTitle:null},taskVendor);let vendor=liveVendor??tab.liveVendor??(tab.kind==="engine"?tab.vendor??taskVendor:void 0)??void 0,source=liveTitle?.trim()||tab.lastTitle,named=vendor?stableRecordedTitle(source,vendor):source??null;if(!vendor||engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return tabTitle({...tab,...vendor?{kind:"engine",vendor}:{},lastTitle:named},taskVendor);return tabTitle({...tab,kind:"engine",vendor,lastTitle:named},taskVendor)}function conversationTitle(tab,taskVendor,liveTitle){let renamed=tab.title?.trim();if(renamed)return renamed;let vendor=titleVendor(tab,taskVendor),named=stableRecordedTitle(liveTitle?.trim()||tab.lastTitle,vendor);if(!named)return null;let entry=engineEntry(vendor),generic=[entry.displayName,entry.defaultCommand[0]].map((name)=>name?.toLowerCase()),lowered=named.toLowerCase();return generic.includes(lowered)||generic.includes(lowered.replace(/ (code|cli)$/,""))?null:named}function titleVendor(tab,taskVendor){return tab.liveVendor??(tab.kind==="engine"?tab.vendor:void 0)??taskVendor}function tabTitle(tab,taskVendor,liveName){if(tab.title)return tab.title;let ls=tab.splitTree?leaves(tab.splitTree.root):[];if(ls.length>1)return t("terminal.tab.groupTitle",{n:tab.ordinal});let sole=ls.length===1?ls[0]:void 0;if(sole&&sole.id!=="leaf-1")return sole.title??`${liveName??SHELL_LEAF_NAME} ${tab.ordinal}`;let vendor=titleVendor(tab,taskVendor);if(liveName&&!isEnginePlaceholderTitle(liveName,vendor))return`${liveName} ${tab.ordinal}`;if(tab.lastTitle&&!isEnginePlaceholderTitle(tab.lastTitle,vendor))return`${tab.lastTitle} ${tab.ordinal}`;let auto=meaningfulAutoTitle(tab.autoTitle);if(auto)return auto;return`${tab.kind==="engine"?engineEntry(tab.vendor??taskVendor).defaultCommand[0]??SHELL_LEAF_NAME:SHELL_LEAF_NAME} ${tab.ordinal}`}function visibleNativeStatus(tab,taskVendor,vendor,liveName){if(!vendor||!liveName)return!1;if(engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return!1;return tabTitle(tab,taskVendor,liveName)===`${liveName} ${tab.ordinal}`}function initialTabs(){return{tabs:[{kind:"engine",id:"tab-1",title:null,ordinal:1}],activeId:"tab-1",nextOrdinal:2}}function reopenHintFor(closed){if(closed?.kind==="command")return{kind:"command"};if(closed?.kind==="engine"&&closed.vendor)return{kind:"engine",vendor:closed.vendor};return{kind:"engine"}}function reopenTabs(state,shell,engine){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,next=state.nextOrdinal+1;if(!engine&&state.reopenAs?.kind==="command")return{tabs:[{kind:"command",id,title:null,ordinal,command:[shell]}],activeId:id,nextOrdinal:next};let vendor=state.reopenAs?.kind==="engine"?state.reopenAs.vendor:void 0;return{tabs:[{kind:"engine",id,title:null,ordinal,...engine??(vendor?{vendor}:{})}],activeId:id,nextOrdinal:next}}function initialShellTabs(shell){return{tabs:[{kind:"command",id:"tab-1",title:null,ordinal:1,command:[shell]}],activeId:"tab-1",nextOrdinal:2}}function rehydrateTabs(persisted,shell,opts={}){let tabs=persisted.tabs.map((t2)=>t2.kind==="command"?{...t2,command:shell,purpose:void 0}:t2);if(tabs.length===0)return opts.allowEmpty?persisted:initialTabs();let activeId=tabs.some((t2)=>t2.id===persisted.activeId)?persisted.activeId:tabs[0].id,maxOrdinal=tabs.reduce((max,t2)=>Math.max(max,t2.ordinal),0);return{tabs,activeId,nextOrdinal:Math.max(persisted.nextOrdinal,maxOrdinal+1)}}function recycleTabs(state,prev){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,pinned=prev.kind==="engine"?prev:void 0;return{tabs:[{kind:"engine",id,ordinal,title:prev.title,autoTitle:prev.autoTitle,...pinned?.vendor?{vendor:pinned.vendor}:{},...pinned?.engineCommand?{engineCommand:pinned.engineCommand}:{}}],activeId:id,nextOrdinal:ordinal+1}}var SHELL_SAFE_ARG=/^[A-Za-z0-9@%+=:,./_-]+$/;function shellCommandLine(argv){return argv.map((a)=>SHELL_SAFE_ARG.test(a)?a:`'${a.replaceAll("'","'\\''")}'`).join(" ")}function shellIdentityInput(taskId,tabId,extraEnv=[]){let task=shellCommandLine([taskId]),tab=shellCommandLine([tabId]),extra=extraEnv.map(([name,value])=>` ${name}=${shellCommandLine([value])}`).join("");return` export ROVE_TASK_ID=${task} ROVE_TAB_ID=${tab}${extra} && clear\r`}function insertAfterActive(state,tab){let i=state.tabs.findIndex((t2)=>t2.id===state.activeId);return{tabs:[...state.tabs.slice(0,i+1),tab,...state.tabs.slice(i+1)],activeId:tab.id,nextOrdinal:state.nextOrdinal+1}}function addTab(state,vendor){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"engine",id:`tab-${ordinal}`,title:null,ordinal,vendor})}function openCommandTab(state,command,label){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command})}function findEditorTab(state){return state.tabs.find((tab)=>tab.kind==="command"&&tab.purpose==="editor")}function openEditorTab(state,command,label){let existing=findEditorTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command,purpose:"editor"})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,command,splitTree:null}:tab);return{...state,tabs,activeId:existing.id}}function findContentTab(state){return state.tabs.find((tab)=>tab.kind==="content")}function openContentTab(state,relPath,label,base){let existing=findContentTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"content",id:`tab-${ordinal}`,title:label,ordinal,relPath,base})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,relPath,base}:tab);return{...state,tabs,activeId:existing.id}}function closeTab(state,id,opts={}){if(state.tabs.length<=1&&!opts.allowEmpty)return{state,closedId:null};let i=state.tabs.findIndex((t2)=>t2.id===id);if(i<0)return{state,closedId:null};let tabs=state.tabs.filter((t2)=>t2.id!==id);if(state.activeId!==id)return{state:{...state,tabs},closedId:id};if(tabs.length===0)return{state:{...state,tabs,activeId:id,reopenAs:reopenHintFor(state.tabs[i])},closedId:id};let next=tabs[Math.max(0,i-1)];return{state:{...state,tabs,activeId:(next??tabs[0]).id},closedId:id}}function closeActiveTab(state){return closeTab(state,state.activeId)}function renameActiveTab(state,title){return setTabTitle(state,state.activeId,title)}function setTabTitle(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.title??null)===next)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,title:next}:t2);return{...state,tabs}}function setTabSessionId(state,id,sessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,sessionId}:t2);return{...state,tabs}}function setTabForkFrom(state,id,sourceSessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,forkFrom:sourceSessionId}:t2);return{...state,tabs}}function setTabEngineCommand(state,id,command){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,engineCommand:command}:t2);return{...state,tabs}}function setTabInitialPrompt(state,id,prompt){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,initialPrompt:prompt}:t2);return{...state,tabs}}function setTabLastTitle(state,id,lastTitle){if(lastTitle.length===0)return state;let current=state.tabs.find((t2)=>t2.id===id);if(!current||current.lastTitle===lastTitle)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,lastTitle}:t2);return{...state,tabs}}function setTabLiveVendor(state,id,liveVendor){let current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.liveVendor??null)===liveVendor)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,liveVendor}:t2);return{...state,tabs}}function setTabAutoTitle(state,id,autoTitle){let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,autoTitle}:t2);return{...state,tabs}}function setTabSpawned(state,id,spawned){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"&&!t2.spawned!==!spawned?{...t2,spawned}:t2);return{...state,tabs}}function cycleTab(state,delta){let n=state.tabs.length;if(n<=1)return state;let i=state.tabs.findIndex((t2)=>t2.id===state.activeId),next=state.tabs[(i+delta+n)%n];return{...state,activeId:next.id}}function gotoTab(state,index){let target=state.tabs[index];return target&&target.id!==state.activeId?{...state,activeId:target.id}:state}function moveTab(state,id,delta){let i=state.tabs.findIndex((t2)=>t2.id===id),j=i+delta;if(i<0||j<0||j>=state.tabs.length)return state;let tabs=[...state.tabs],a=tabs[i];return tabs[i]=tabs[j],tabs[j]=a,{...state,tabs}}function selectTab(state,id){if(state.activeId===id||!state.tabs.some((t2)=>t2.id===id))return state;return{...state,activeId:id}}function setTabSplit(state,id,tree){if(!state.tabs.some((t2)=>t2.id===id))return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,splitTree:tree}:t2);return{...state,tabs}}function tabPtyKey(taskId,tabId){return`${taskId}::${tabId}`}function tabPtyKeyFor(taskId,tab){if(tab.kind==="engine"&&tab.ptyTask)return tabPtyKey(tab.ptyTask.id,tab.ptyTask.tabId??"tab-1");return tabPtyKey(taskId,tab.id)}function tabCwdFor(tab,taskWorktree){if(tab.kind==="engine"&&tab.ptyTask)return tab.ptyTask.worktree;return taskWorktree}
2
+ import{buildEngineSessionLaunch,trustEngineWorktree}from"./chunk-15fp4p9v.js";import{engineForkArgv,engineResumeArgv,withPinnedSessionId}from"./chunk-rzef6szc.js";import{engineEntry,engineStatusPrefixes,isEnginePlaceholderTitle,stripEngineStatusPrefix}from"./chunk-edrxs4cy.js";import{tildify}from"./chunk-2r1fgng9.js";import{t}from"./chunk-bj6sa371.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";function engineTabArgv(tab,base,live,fallbackVendor){if(tab.forkFrom&&!tab.spawned&&!live){let forked=tab.vendor?engineForkArgv(base,tab.vendor,tab.forkFrom,tab.sessionId??null):null;if(forked)return forked}if(!tab.sessionId)return base;let vendor=tab.vendor??fallbackVendor;if(tab.spawned&&!live)return engineResumeArgv(base,vendor,tab.sessionId)??base;return withPinnedSessionId(base,vendor,()=>tab.sessionId).argv}function engineTabSpawnFor(state,tab,base,opts){let{live,shell,prompt}=opts,firstEngine=state.tabs.find((t2)=>t2.kind==="engine"),fresh=!tab.spawned&&!live,tabPrompt=fresh?tab.initialPrompt?.trim():void 0,wantsPrompt=!!prompt&&tab.id===firstEngine?.id&&fresh,isFreshFirstEngine=tab.id===firstEngine?.id&&fresh,promptIntent=tabPrompt?{kind:"explicit",prompt:tabPrompt}:wantsPrompt?{kind:"new-task",prompt}:isFreshFirstEngine?{kind:"repo-init"}:{kind:"none"},ref=tab.ptyTask;trustEngineWorktree(tab.vendor??opts.task.vendor,ref?.worktree??opts.worktreePath);let launch=buildEngineSessionLaunch({task:ref?{...opts.task,id:ref.id,kind:"task"}:opts.task,worktreePath:ref?.worktree??opts.worktreePath,shell,argv:engineTabArgv(tab,base,live,opts.task.vendor),promptIntent,protocolGates:opts.protocolGates,inlineImages:opts.inlineImages,tabId:ref?ref.tabId??"tab-1":tab.id});return{command:launch.command,...launch.firstMessage?{firstMessage:launch.firstMessage,engineBin:base[0]}:{}}}function tabExitAction(tab,deadOnAttach,resumeTried){if(tab.kind==="engine"&&deadOnAttach&&!!tab.sessionId&&tab.spawned&&!resumeTried)return"resume";return"close"}import*as fs from"fs";import*as os from"os";function pathLeaf(p){return p.slice(lastSeparator(p)+1)}function lastSeparator(p){return pathSyntax(p).sep==="\\"||process.platform==="win32"&&!p.startsWith("/")?Math.max(p.lastIndexOf("/"),p.lastIndexOf("\\")):p.lastIndexOf("/")}function expandHome(p){if(p==="~")return os.homedir();if(p.startsWith("~/")||process.platform==="win32"&&p.startsWith("~\\"))return os.homedir()+p.slice(1);return p}function stripTrailingSlash(p){let root=pathSyntax(p).parse(p).root,end=p.length;while(end>root.length&&lastSeparator(p.slice(0,end))===end-1)end--;return p.slice(0,end)}function splitPathForDirSuggest(value){if(!value)return{base:"",filter:""};let expanded=expandHome(value==="~"?"~/":value),lastSlash=lastSeparator(expanded);if(lastSlash===-1)return{base:"",filter:expanded};return{base:expanded.slice(0,lastSlash+1),filter:expanded.slice(lastSlash+1)}}function listSubdirs(base){if(!base)return[];try{let entries=fs.readdirSync(base,{withFileTypes:!0}),out=[];for(let e of entries)if(e.isDirectory())out.push(e.name);return out.sort((a,b)=>a.localeCompare(b))}catch{return[]}}function filterSubdirs(all,filter){let f=filter.toLowerCase(),visible=f.startsWith(".")?all:all.filter((n)=>!n.startsWith("."));if(!f)return visible;return visible.filter((n)=>n.toLowerCase().startsWith(f))}function joinDrill(typedValue,baseExpanded,name){let out=`${baseExpanded+name}/`;if(typedValue.startsWith("~")){let short=tildify(out);if(short.startsWith("~"))return short==="~"?"~/":`${short}/`}return out}function joinPicked(typedValue,baseExpanded,name){let out=baseExpanded+name;if(typedValue.startsWith("~"))return tildify(out);return out}function depth(node){return node.kind==="leaf"?0:1+Math.max(...node.children.map(depth))}function siblingCount(root,id,orientation){let find=(node)=>{if(node.kind==="leaf")return null;if(node.orientation===orientation&&node.children.some((c)=>c.kind==="leaf"&&c.id===id))return node.children.length;for(let child of node.children){let n=find(child);if(n!==null)return n}return null};return find(root)??1}function splitFits(state,orientation,activeSize){let extent=orientation==="row"?activeSize.cols:activeSize.rows,min=orientation==="row"?20:6,n=siblingCount(state.root,state.activeLeafId,orientation);return Math.floor(extent*n/(n+1))-1>=min}function initialSplit(content){return{root:{kind:"leaf",id:"leaf-1",content},activeLeafId:"leaf-1",nextOrdinal:2}}function leaves(node){return node.kind==="leaf"?[node]:node.children.flatMap(leaves)}function splitActive(state,orientation,content,activeSize){if(activeSize&&!splitFits(state,orientation,activeSize))return state;let leaf={kind:"leaf",id:`leaf-${state.nextOrdinal}`,content},insert=(node)=>{if(node.kind==="leaf"){if(node.id!==state.activeLeafId)return node;return{kind:"group",orientation,children:[node,leaf]}}if(node.orientation===orientation){let i=node.children.findIndex((c)=>c.kind==="leaf"&&c.id===state.activeLeafId);if(i>=0){let children=[...node.children.slice(0,i+1),leaf,...node.children.slice(i+1)];return{...node,children}}}return{...node,children:node.children.map(insert)}},root=insert(state.root);if(!activeSize&&depth(root)>4)return state;return{root,activeLeafId:leaf.id,nextOrdinal:state.nextOrdinal+1}}function removeLeaf(state,id){let all=leaves(state.root);if(all.length<=1)return null;let prune=(node)=>{if(node.kind==="leaf")return node.id===id?null:node;let children=node.children.map(prune).filter((c)=>c!==null);if(children.length===0)return null;if(children.length===1)return children[0];return{...node,children}},root=prune(state.root);if(root===null)return null;if(leaves(root).length===all.length)return state;let order=all.map((l)=>l.id),removedIdx=order.indexOf(id),fallback=order[removedIdx>0?removedIdx-1:removedIdx+1],activeLeafId=state.activeLeafId===id?fallback:state.activeLeafId;return{...state,root,activeLeafId}}function renameLeaf(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,walk=(node)=>node.kind==="leaf"?node.id===id?{...node,title:next}:node:{...node,children:node.children.map(walk)};if(!leaves(state.root).some((l)=>l.id===id))return state;return{...state,root:walk(state.root)}}function cycleLeaf(state,delta){let order=leaves(state.root).map((l)=>l.id);if(order.length<=1)return state;let i=order.indexOf(state.activeLeafId),next=order[(i+delta+order.length)%order.length];return{...state,activeLeafId:next}}function hasEngineLeaf(tree){return!tree||leaves(tree.root).some((l)=>l.id==="leaf-1")}function isTabSplit(tree){return tree?leaves(tree.root).length>1:!1}function collapseSplit(next){let ls=leaves(next.root);return ls.length===1&&ls[0]?.id==="leaf-1"?null:next}function splitLeafPtyKey(tabKey,leafId){return leafId==="leaf-1"?tabKey:`${tabKey}::${leafId}`}var SHELL_LEAF_NAME="shell";function splitLeafNames(leafList,tabCommand,engineTitle,liveTitles){let basename=(argv)=>{let head=(argv??tabCommand)[0]??"",name=pathLeaf(head);return name.length>0?name:"?"},seen=new Map,out=new Map;for(let leaf of leafList){if(leaf.title){out.set(leaf.id,leaf.title);continue}let name=leaf.content===null?engineTitle||liveTitles?.get(leaf.id)||basename(leaf.content):liveTitles?.get(leaf.id)||SHELL_LEAF_NAME,n=(seen.get(name)??0)+1;seen.set(name,n),out.set(leaf.id,n===1?name:`${name} ${n}`)}return out}function meaningfulAutoTitle(autoTitle){let trimmed=(autoTitle??"").trim();if(trimmed.length<3)return null;if(/^[\d\s\p{P}\p{S}]+$/u.test(trimmed))return null;return trimmed}function stableRecordedTitle(raw,vendor){let recorded=raw?.trim();if(!recorded)return null;let cleaned=stripEngineStatusPrefix(recorded,vendor);if(cleaned===recorded&&isEngineDecoration(recorded,vendor))return null;if(isEnginePlaceholderTitle(cleaned,vendor))return null;return cleaned||null}function isEngineDecoration(text,vendor){let glyphs=new Set(engineStatusPrefixes(vendor));return[...text].every((ch)=>ch.trim().length===0||glyphs.has(ch))}function tabTitleStable(tab,taskVendor,liveVendor,liveTitle){if(liveVendor===null&&tab.kind==="engine")return tabTitle({...tab,kind:"command",lastTitle:null},taskVendor);let vendor=liveVendor??tab.liveVendor??(tab.kind==="engine"?tab.vendor??taskVendor:void 0)??void 0,source=liveTitle?.trim()||tab.lastTitle,named=vendor?stableRecordedTitle(source,vendor):source??null;if(!vendor||engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return tabTitle({...tab,...vendor?{kind:"engine",vendor}:{},lastTitle:named},taskVendor);return tabTitle({...tab,kind:"engine",vendor,lastTitle:named},taskVendor)}function conversationTitle(tab,taskVendor,liveTitle){let renamed=tab.title?.trim();if(renamed)return renamed;let vendor=titleVendor(tab,taskVendor),named=stableRecordedTitle(liveTitle?.trim()||tab.lastTitle,vendor);if(!named)return null;let entry=engineEntry(vendor),generic=[entry.displayName,entry.defaultCommand[0]].map((name)=>name?.toLowerCase()),lowered=named.toLowerCase();return generic.includes(lowered)||generic.includes(lowered.replace(/ (code|cli)$/,""))?null:named}function titleVendor(tab,taskVendor){return tab.liveVendor??(tab.kind==="engine"?tab.vendor:void 0)??taskVendor}function tabTitle(tab,taskVendor,liveName){if(tab.title)return tab.title;let ls=tab.splitTree?leaves(tab.splitTree.root):[];if(ls.length>1)return t("terminal.tab.groupTitle",{n:tab.ordinal});let sole=ls.length===1?ls[0]:void 0;if(sole&&sole.id!=="leaf-1")return sole.title??`${liveName??SHELL_LEAF_NAME} ${tab.ordinal}`;let vendor=titleVendor(tab,taskVendor);if(liveName&&!isEnginePlaceholderTitle(liveName,vendor))return`${liveName} ${tab.ordinal}`;if(tab.lastTitle&&!isEnginePlaceholderTitle(tab.lastTitle,vendor))return`${tab.lastTitle} ${tab.ordinal}`;let auto=meaningfulAutoTitle(tab.autoTitle);if(auto)return auto;return`${tab.kind==="engine"?engineEntry(tab.vendor??taskVendor).defaultCommand[0]??SHELL_LEAF_NAME:SHELL_LEAF_NAME} ${tab.ordinal}`}function visibleNativeStatus(tab,taskVendor,vendor,liveName){if(!vendor||!liveName)return!1;if(engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return!1;return tabTitle(tab,taskVendor,liveName)===`${liveName} ${tab.ordinal}`}function initialTabs(){return{tabs:[{kind:"engine",id:"tab-1",title:null,ordinal:1}],activeId:"tab-1",nextOrdinal:2}}function reopenHintFor(closed){if(closed?.kind==="command")return{kind:"command"};if(closed?.kind==="engine"&&closed.vendor)return{kind:"engine",vendor:closed.vendor};return{kind:"engine"}}function reopenTabs(state,shell,engine){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,next=state.nextOrdinal+1;if(!engine&&state.reopenAs?.kind==="command")return{tabs:[{kind:"command",id,title:null,ordinal,command:[shell]}],activeId:id,nextOrdinal:next};let vendor=state.reopenAs?.kind==="engine"?state.reopenAs.vendor:void 0;return{tabs:[{kind:"engine",id,title:null,ordinal,...engine??(vendor?{vendor}:{})}],activeId:id,nextOrdinal:next}}function initialShellTabs(shell){return{tabs:[{kind:"command",id:"tab-1",title:null,ordinal:1,command:[shell]}],activeId:"tab-1",nextOrdinal:2}}function rehydrateTabs(persisted,shell,opts={}){let tabs=persisted.tabs.map((t2)=>t2.kind==="command"?{...t2,command:shell,purpose:void 0}:t2);if(tabs.length===0)return opts.allowEmpty?persisted:initialTabs();let activeId=tabs.some((t2)=>t2.id===persisted.activeId)?persisted.activeId:tabs[0].id,maxOrdinal=tabs.reduce((max,t2)=>Math.max(max,t2.ordinal),0);return{tabs,activeId,nextOrdinal:Math.max(persisted.nextOrdinal,maxOrdinal+1)}}function recycleTabs(state,prev){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,pinned=prev.kind==="engine"?prev:void 0;return{tabs:[{kind:"engine",id,ordinal,title:prev.title,autoTitle:prev.autoTitle,...pinned?.vendor?{vendor:pinned.vendor}:{},...pinned?.engineCommand?{engineCommand:pinned.engineCommand}:{}}],activeId:id,nextOrdinal:ordinal+1}}var SHELL_SAFE_ARG=/^[A-Za-z0-9@%+=:,./_-]+$/;function shellCommandLine(argv){return argv.map((a)=>SHELL_SAFE_ARG.test(a)?a:`'${a.replaceAll("'","'\\''")}'`).join(" ")}function shellIdentityInput(taskId,tabId,extraEnv=[]){let task=shellCommandLine([taskId]),tab=shellCommandLine([tabId]),extra=extraEnv.map(([name,value])=>` ${name}=${shellCommandLine([value])}`).join("");return` export ROVE_TASK_ID=${task} ROVE_TAB_ID=${tab}${extra} && clear\r`}function insertAfterActive(state,tab){let i=state.tabs.findIndex((t2)=>t2.id===state.activeId);return{tabs:[...state.tabs.slice(0,i+1),tab,...state.tabs.slice(i+1)],activeId:tab.id,nextOrdinal:state.nextOrdinal+1}}function addTab(state,vendor){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"engine",id:`tab-${ordinal}`,title:null,ordinal,vendor})}function openCommandTab(state,command,label){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command})}function findEditorTab(state){return state.tabs.find((tab)=>tab.kind==="command"&&tab.purpose==="editor")}function openEditorTab(state,command,label){let existing=findEditorTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command,purpose:"editor"})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,command,splitTree:null}:tab);return{...state,tabs,activeId:existing.id}}function findContentTab(state){return state.tabs.find((tab)=>tab.kind==="content")}function openContentTab(state,relPath,label,base){let existing=findContentTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"content",id:`tab-${ordinal}`,title:label,ordinal,relPath,base})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,relPath,base}:tab);return{...state,tabs,activeId:existing.id}}function closeTab(state,id,opts={}){if(state.tabs.length<=1&&!opts.allowEmpty)return{state,closedId:null};let i=state.tabs.findIndex((t2)=>t2.id===id);if(i<0)return{state,closedId:null};let tabs=state.tabs.filter((t2)=>t2.id!==id);if(state.activeId!==id)return{state:{...state,tabs},closedId:id};if(tabs.length===0)return{state:{...state,tabs,activeId:id,reopenAs:reopenHintFor(state.tabs[i])},closedId:id};let next=tabs[Math.max(0,i-1)];return{state:{...state,tabs,activeId:(next??tabs[0]).id},closedId:id}}function closeActiveTab(state){return closeTab(state,state.activeId)}function renameActiveTab(state,title){return setTabTitle(state,state.activeId,title)}function setTabTitle(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.title??null)===next)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,title:next}:t2);return{...state,tabs}}function setTabSessionId(state,id,sessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,sessionId}:t2);return{...state,tabs}}function setTabForkFrom(state,id,sourceSessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,forkFrom:sourceSessionId}:t2);return{...state,tabs}}function setTabEngineCommand(state,id,command){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,engineCommand:command}:t2);return{...state,tabs}}function setTabInitialPrompt(state,id,prompt){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,initialPrompt:prompt}:t2);return{...state,tabs}}function setTabLastTitle(state,id,lastTitle){if(lastTitle.length===0)return state;let current=state.tabs.find((t2)=>t2.id===id);if(!current||current.lastTitle===lastTitle)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,lastTitle}:t2);return{...state,tabs}}function setTabLiveVendor(state,id,liveVendor){let current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.liveVendor??null)===liveVendor)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,liveVendor}:t2);return{...state,tabs}}function setTabAutoTitle(state,id,autoTitle){let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,autoTitle}:t2);return{...state,tabs}}function setTabSpawned(state,id,spawned){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"&&!t2.spawned!==!spawned?{...t2,spawned}:t2);return{...state,tabs}}function cycleTab(state,delta){let n=state.tabs.length;if(n<=1)return state;let i=state.tabs.findIndex((t2)=>t2.id===state.activeId),next=state.tabs[(i+delta+n)%n];return{...state,activeId:next.id}}function gotoTab(state,index){let target=state.tabs[index];return target&&target.id!==state.activeId?{...state,activeId:target.id}:state}function moveTab(state,id,delta){let i=state.tabs.findIndex((t2)=>t2.id===id),j=i+delta;if(i<0||j<0||j>=state.tabs.length)return state;let tabs=[...state.tabs],a=tabs[i];return tabs[i]=tabs[j],tabs[j]=a,{...state,tabs}}function selectTab(state,id){if(state.activeId===id||!state.tabs.some((t2)=>t2.id===id))return state;return{...state,activeId:id}}function setTabSplit(state,id,tree){if(!state.tabs.some((t2)=>t2.id===id))return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,splitTree:tree}:t2);return{...state,tabs}}function tabPtyKey(taskId,tabId){return`${taskId}::${tabId}`}function tabPtyKeyFor(taskId,tab){if(tab.kind==="engine"&&tab.ptyTask)return tabPtyKey(tab.ptyTask.id,tab.ptyTask.tabId??"tab-1");return tabPtyKey(taskId,tab.id)}function tabCwdFor(tab,taskWorktree){if(tab.kind==="engine"&&tab.ptyTask)return tab.ptyTask.worktree;return taskWorktree}
3
3
  export{pathLeaf,expandHome,stripTrailingSlash,splitPathForDirSuggest,listSubdirs,filterSubdirs,joinDrill,joinPicked,initialSplit,leaves,splitActive,removeLeaf,renameLeaf,cycleLeaf,hasEngineLeaf,isTabSplit,collapseSplit,splitLeafPtyKey,splitLeafNames,tabTitleStable,conversationTitle,tabTitle,visibleNativeStatus,initialTabs,reopenTabs,initialShellTabs,rehydrateTabs,recycleTabs,engineTabArgv,engineTabSpawnFor,tabExitAction,shellIdentityInput,addTab,openCommandTab,findEditorTab,openEditorTab,openContentTab,closeTab,closeActiveTab,renameActiveTab,setTabTitle,setTabSessionId,setTabForkFrom,setTabEngineCommand,setTabInitialPrompt,setTabLastTitle,setTabLiveVendor,setTabAutoTitle,setTabSpawned,cycleTab,gotoTab,moveTab,selectTab,setTabSplit,tabPtyKey,tabPtyKeyFor,tabCwdFor};
@@ -0,0 +1,13 @@
1
+ // @bun
2
+ import{PreambleStripper}from"./chunk-sq6zj3fb.js";import{shQuote}from"./chunk-y1xnjx8x.js";import{loadStateFile,updateStateFile}from"./chunk-32j6y5d1.js";import{homeDir}from"./chunk-z1nj22va.js";import{RoveDaemonClient}from"./chunk-sza3tyhs.js";import{logClientError}from"./chunk-0g4wc808.js";import{shortHomeTag}from"./chunk-eqdbfgq5.js";import{PRE_RENAME_VERSION_KEY}from"./chunk-f8gg8cqh.js";import{spawn as spawn3}from"child_process";import{mkdirSync as mkdirSync3,rmSync as rmSync2}from"fs";import{connect as netConnect}from"net";import{tmpdir}from"os";import{join}from"path";var LOCAL_MACHINE_ID="local",ALIAS_RE=/^[A-Za-z0-9][A-Za-z0-9._-]*$/;function isValidMachineAlias(alias){return alias!==LOCAL_MACHINE_ID&&alias.length<=64&&ALIAS_RE.test(alias)}function parseSshTarget(target){let trimmed=target.trim();if(!trimmed)return null;let at=trimmed.lastIndexOf("@"),user=at>=0?trimmed.slice(0,at):void 0,rest=at>=0?trimmed.slice(at+1):trimmed;if(at>=0&&!user)return null;let bracketed=/^\[([^\]]+)\](?::(\d{1,5}))?$/.exec(rest),portMatch=bracketed?null:/^([^:]*):(\d{1,5})$/.exec(rest),host=bracketed?bracketed[1]??"":portMatch?portMatch[1]??"":rest,rawPort=bracketed?bracketed[2]:portMatch?.[2],port=rawPort?Number(rawPort):void 0;if(!host||/\s/.test(host))return null;if(port!==void 0&&(port<1||port>65535))return null;return{host,...user?{user}:{},...port?{port}:{}}}function defaultMachineAlias(args){let source=!args.typedUser&&args.port===void 0&&!isIpLiteral(args.typedHost)?args.typedHost:args.remoteHostname||args.typedHost;return sanitizeAlias(source)}function isIpLiteral(host){return host.includes(":")||/^\d{1,3}(\.\d{1,3}){3}$/.test(host)}function sanitizeAlias(raw){return(raw.split(".")[0]??raw).replace(/[^A-Za-z0-9._-]/g,"-").slice(0,64)}function sshTargetOf(config){return config.user?`${config.user}@${config.host}`:config.host}function readMachines(state){let raw=state.machines;if(!raw||typeof raw!=="object"||Array.isArray(raw))return{};return raw}function listMachines(){let machines=readMachines(loadStateFile());return Object.entries(machines).map(([alias,config])=>({alias,...config}))}function getMachine(alias){return readMachines(loadStateFile())[alias]??null}function addMachine(alias,config){let added=!1;return updateStateFile((state)=>{let machines={...readMachines(state)};added=machines[alias]===void 0,machines[alias]={...config,addedAt:config.addedAt??machines[alias]?.addedAt??new Date().toISOString()},state.machines=machines;return}),{added}}function updateMachine(alias,patch){updateStateFile((state)=>{let machines={...readMachines(state)},existing=machines[alias];if(!existing)return!1;machines[alias]={...existing,...patch},state.machines=machines;return})}function setMachineIdentity(alias,identity){updateMachine(alias,{identity})}function removeMachine(alias){let removed=!1;return updateStateFile((state)=>{let machines={...readMachines(state)};if(removed=machines[alias]!==void 0,!removed)return!1;delete machines[alias],state.machines=machines;return}),removed}function dedupeMachines(entries){let kept=[],seen=[];for(let entry of entries){let identity=entry.identity;if(identity){if(seen.some((other)=>sameMachine(other,identity)))continue;seen.push(identity)}kept.push(entry)}return kept}function sameMachine(a,b){return a.hostname===b.hostname&&a.homeDir===b.homeDir&&a.daemonPid===b.daemonPid}function duplicateAliasOf(machines,self,identity){for(let[alias,config]of Object.entries(machines)){if(alias===self)continue;let other=config.identity;if(!other)continue;if(other.hostname===identity.hostname&&other.homeDir===identity.homeDir&&other.daemonPid===identity.daemonPid)return alias}return null}var CONTROL_PERSIST_SECONDS=300;function machineSocketDir(alias,home=homeDir()){let natural=join(home,".rove","machines",alias);if(Buffer.byteLength(join(natural,"daemon.sock"),"utf8")<=SOCKET_PATH_LIMIT)return natural;return join(tmpdir(),`rove-m-${shortHomeTag(home)}-${shortHomeTag(alias)}`)}var SOCKET_PATH_LIMIT=100;function localDaemonSocketPath(alias,home=homeDir()){return join(machineSocketDir(alias,home),"daemon.sock")}function localPtySocketPath(alias,home=homeDir()){return join(machineSocketDir(alias,home),"pty.sock")}function controlPath(alias,home=homeDir()){return join(machineSocketDir(alias,home),"cm")}function machineSshArgs(alias,config,opts={}){let argv=["ssh"];if(opts.batch!==!1)argv.push("-o","BatchMode=yes");if(opts.mux!==!1&&process.platform!=="win32")argv.push("-o","ControlMaster=auto","-o",`ControlPath=${controlPath(alias,opts.home)}`,"-o",`ControlPersist=${CONTROL_PERSIST_SECONDS}`);if(argv.push("-o","StrictHostKeyChecking=accept-new"),config.port)argv.push("-p",String(config.port));if(config.auth.kind==="key"&&config.auth.keyPath)argv.push("-i",config.auth.keyPath);return argv.push(sshTargetOf(config)),argv}import{spawn as spawn2}from"child_process";import{mkdirSync as mkdirSync2,rmSync}from"fs";import{createServer}from"net";import{spawn}from"child_process";import{mkdirSync}from"fs";import{posix}from"path";function isWindowsPath(path){return/^[A-Za-z]:[\\/]/.test(path)||/^\\\\[.?]\\/.test(path)}function isWindowsMachine(config){if(config.sockets&&(isWindowsPath(config.sockets.daemon)||isWindowsPath(config.sockets.pty)))return!0;return config.roveBin!==void 0&&isWindowsPath(config.roveBin)}function psQuote(value){return`'${value.replaceAll("'","''")}'`}function powershellCommand(script){return`powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${Buffer.from(script,"utf16le").toString("base64")}`}function windowsRoveCommand(roveBin,args){return powershellCommand(`& ${psQuote(roveBin)} ${args.map(psQuote).join(" ")}; exit $LASTEXITCODE`)}var WINDOWS_LOCATE_SCRIPT=['$dirs = @("$env:APPDATA\\npm", "$env:USERPROFILE\\.bun\\bin", "$env:LOCALAPPDATA\\pnpm", $env:NVM_SYMLINK)',"$dirs += [Environment]::GetEnvironmentVariable('Path', 'User') -split ';'","foreach ($d in $dirs) { if (-not $d) { continue }",` foreach ($n in 'rove.cmd', 'rove.exe') { $p = Join-Path $d $n; if (Test-Path -LiteralPath $p) { "ROVE_BIN=$p"; exit 0 } } }`,"exit 1"].join(`
3
+ `),WINDOWS_LOCATE_ROVE=powershellCommand(WINDOWS_LOCATE_SCRIPT),WINDOWS_ROVE_MISSING=/'(?:[^'\r\n]*[\\/])?rove(?:\.cmd|\.exe|\.ps1)?' is not recognized as (?:an internal or external command|(?:the|a) name of a cmdlet)/i,WINDOWS_SCRIPTS_DISABLED=/rove\.ps1 cannot be loaded because running scripts is disabled/i;function windowsNoRoveMessage(alias,output){if(WINDOWS_SCRIPTS_DISABLED.test(output))return[`${alias}'s ssh sessions run Windows PowerShell, whose execution policy refuses npm's rove.ps1, and no rove.cmd was found to use instead.`,`On ${alias}, either allow scripts for your user: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`,'or make ssh use PowerShell 7 (elevated): New-ItemProperty -Path HKLM:\\SOFTWARE\\OpenSSH -Name DefaultShell -Value "$env:ProgramFiles\\PowerShell\\7\\pwsh.exe" -PropertyType String -Force'].join(`
4
+ `);return[`Rove is not on the PATH of ssh sessions on ${alias} (Windows), nor in %APPDATA%\\npm, ~\\.bun\\bin or the user PATH.`,"Install it there in a PowerShell: npm install -g @sma1lboy/rove",`If it is installed elsewhere, add that folder to your user PATH on ${alias}, then restart sshd (elevated: Restart-Service sshd) so new sessions see it.`].join(`
5
+ `)}var WINDOWS_SSHD_BANNER=/remote software version OpenSSH_for_Windows/i,WINDOWS_SSHD_STEPS=["Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0","Set-Service sshd -StartupType Automatic; Start-Service sshd","if (-not (Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue)) { New-NetFirewallRule -Name OpenSSH-Server-In-TCP -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 }"];function windowsAuthorizeKeySteps(publicKey){let key=psQuote(publicKey.trim());return["# Administrator account (elevated PowerShell):",`Add-Content -Path "$env:ProgramData\\ssh\\administrators_authorized_keys" -Value ${key}`,'icacls.exe "$env:ProgramData\\ssh\\administrators_authorized_keys" /inheritance:r /grant "*S-1-5-32-544:F" /grant "SYSTEM:F"',"# Standard account (normal PowerShell):",`New-Item -ItemType Directory -Force "$HOME\\.ssh" | Out-Null; Add-Content -Path "$HOME\\.ssh\\authorized_keys" -Value ${key}`]}var PATTERNS=[["host-key-changed",/REMOTE HOST IDENTIFICATION HAS CHANGED|Host key for .* has changed/i],["host-key-unverified",/Host key verification failed|No .*host key is known for/i],["auth-denied",/Permission denied \(|Too many authentication failures|no matching host key type/i],["unresolved",/Could not resolve hostname|Name or service not known|nodename nor servname/i],["refused",/Connection refused/i],["timeout",/timed out|Operation timed out|No route to host|Network is unreachable/i]];function classifySshFailure(stderr){for(let[kind,pattern]of PATTERNS)if(pattern.test(stderr))return kind;return"unknown"}function sshFailureRemedy(kind,target,windows=!1){if(windows&&(kind==="refused"||kind==="timeout"))return[`${target} (Windows) ${kind==="refused"?"refused the connection: its OpenSSH Server is not running":"did not answer on the ssh port: its OpenSSH Server is off, or the firewall drops it"}.`,"In an elevated PowerShell there:",...WINDOWS_SSHD_STEPS.map((step)=>` ${step}`)].join(`
6
+ `);if(windows&&kind==="auth-denied")return`${target} (Windows) rejected every key ssh offered. For an Administrators account, OpenSSH for Windows reads only %ProgramData%\\ssh\\administrators_authorized_keys; ~\\.ssh\\authorized_keys is ignored.`;switch(kind){case"unresolved":return`${target} does not resolve to an address. Check the name, or the Host block in ~/.ssh/config; on a tailnet, check \`tailscale status\`.`;case"refused":return`${target} refused the connection: nothing listens for ssh there. Enable Remote Login / start sshd on it (Windows: \`Start-Service sshd\`, elevated), or pass the right --port.`;case"timeout":return`${target} did not answer. Is it awake and on this network (or VPN / tailnet)?`;case"host-key-changed":return`${target}'s host key CHANGED since you last connected. That is either a reinstall or someone in the middle. If you reinstalled it, remove the old key with \`ssh-keygen -R <host>\` and retry; otherwise stop here.`;case"host-key-unverified":return`${target}'s host key is not trusted yet. Run \`rove machine add\` in a terminal to confirm its fingerprint, or connect once with \`ssh ${target}\`.`;case"auth-denied":return`${target} rejected every key ssh offered. Load your key into ssh-agent, pass --identity <key>, or install a key there with \`ssh-copy-id ${target}\`.`;case"unknown":return`ssh to ${target} failed. Run \`ssh ${target} true\` to see why.`}}async function runOnMachine(alias,config,command,opts={}){try{mkdirSync(machineSocketDir(alias,opts.home),{recursive:!0,mode:448})}catch{}let base=machineSshArgs(alias,config,{home:opts.home,mux:opts.sshFlags===void 0}),argv=[base[0]??"ssh",...opts.sshFlags??[],...base.slice(1),command];return await new Promise((resolve)=>{let stdout="",stderr="",settled=!1,done=(exitCode)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({stdout,stderr,exitCode})},child=spawn(argv[0]??"ssh",argv.slice(1),{stdio:["ignore","pipe","pipe"]}),timer=setTimeout(()=>{child.kill("SIGKILL"),stderr+=`
7
+ timed out`,done(-1)},opts.timeoutMs??30000);child.stdout?.on("data",(c)=>{stdout+=c.toString()}),child.stderr?.on("data",(c)=>{stderr+=c.toString()}),child.on("error",()=>done(-1)),child.on("close",(code)=>done(code??-1))})}async function discoverMachine(alias,config,opts={}){let found=await probe(alias,config,config.roveBin,opts);if(found.ok||found.code!=="NO_ROVE")return found;let locate=found.windows?WINDOWS_LOCATE_ROVE:LOCATE_ROVE_SCRIPT,located=parseLocatedRove((await runOnMachine(alias,config,locate,opts)).stdout);if(!located||located===config.roveBin)return found;let retry=await probe(alias,config,located,opts);return retry.ok?{...retry,roveBin:located}:retry}function learnedFrom(found){return{sockets:{daemon:found.status.socketPath,pty:found.status.ptySocketPath},...found.roveBin?{roveBin:found.roveBin}:{},...found.status.relayPreamble?{relayPreamble:!0}:{}}}function remoteRove(roveBin,args){if(roveBin&&isWindowsPath(roveBin))return windowsRoveCommand(roveBin,args);return[roveCommand(roveBin),...args].join(" ")}function roveCommand(roveBin){if(!roveBin)return"rove";return`PATH=${shQuote(posix.dirname(roveBin))}:"$PATH" ${shQuote(roveBin)}`}var LOCATE_ROVE_SCRIPT=[`p=$("\${SHELL:-sh}" -lc 'command -v rove' 2>/dev/null)`,'if [ -n "$p" ] && [ -x "$p" ]; then echo "ROVE_BIN=$p"; exit 0; fi','for p in "$HOME/.bun/bin/rove" "$HOME/.npm-global/bin/rove" "$HOME/.local/bin/rove" /opt/homebrew/bin/rove /usr/local/bin/rove "$HOME"/.nvm/versions/node/*/bin/rove; do',' if [ -x "$p" ]; then echo "ROVE_BIN=$p"; exit 0; fi',"done","exit 1"].join(`
8
+ `);function parseLocatedRove(stdout){return/^ROVE_BIN=((?:\/|[A-Za-z]:\\)\S.*)$/m.exec(stdout)?.[1]?.trim()??null}var ROVE_MISSING=/(?:^|\W)rove:? (?:command not found|not found)|command not found:? rove|\/rove:? No such file or directory|no such file or directory: \S*\/rove\b/i;async function probe(alias,config,roveBin,opts){let status=remoteRove(roveBin,["daemon","status","--json"]),first=await runOnMachine(alias,config,status,opts),parsed=parseStatusJson(first.stdout);if(parsed)return withNoise(windowsReady(alias,parsed),first.stdout);let combined=`${first.stdout}
9
+ ${first.stderr}`.trim();if(WINDOWS_ROVE_MISSING.test(combined)||WINDOWS_SCRIPTS_DISABLED.test(combined))return{ok:!1,code:"NO_ROVE",windows:!0,message:windowsNoRoveMessage(alias,combined)};if(ROVE_MISSING.test(combined))return{ok:!1,code:"NO_ROVE",message:`Rove is not installed on ${alias}: not on its PATH, its login shell's PATH, or the usual bun/npm/nvm install dirs. Install it there with \`npm i -g @sma1lboy/rove\`, then re-run.`};if(first.exitCode===255||first.exitCode===-1){let ssh=classifySshFailure(first.stderr),windows=isWindowsMachine(config)||ssh==="auth-denied"&&await sshServerIsWindows(alias,config,opts),remedy=sshFailureRemedy(ssh,sshTargetOf(config),windows);return{ok:!1,code:"SSH_FAILED",ssh,...windows?{windows}:{},message:ssh==="unknown"&&combined?`${remedy}
10
+ ${combined}`:remedy}}let started=await runOnMachine(alias,config,remoteRove(roveBin,["daemon","restart"]),{...opts,timeoutMs:60000}),second=await runOnMachine(alias,config,status,opts),retry=parseStatusJson(second.stdout);if(retry)return withNoise(windowsReady(alias,retry),second.stdout);if(looksLikeOldStatus(second.stdout))return{ok:!1,code:"BAD_STATUS",message:`${alias} runs a Rove too old for machines (its \`daemon status\` reports no pty socket). Upgrade it: \`ssh ${alias} npm i -g @sma1lboy/rove\`.`};return{ok:!1,code:started.exitCode===0?"BAD_STATUS":"NO_DAEMON",message:`could not read a daemon status from ${alias}: ${`${second.stdout}
11
+ ${second.stderr}
12
+ ${started.stderr}`.trim()||"no output"}`}}function withNoise(found,stdout){let noise=Buffer.byteLength(stdout.slice(0,Math.max(0,stdout.indexOf("{"))),"utf8");return found.ok&&noise>0?{...found,shellNoise:noise}:found}async function sshServerIsWindows(alias,config,opts={}){let run=await runOnMachine(alias,config,"exit",{...opts,timeoutMs:15000,sshFlags:["-v"]});return WINDOWS_SSHD_BANNER.test(run.stderr)}function windowsReady(alias,status){if(status.relayPreamble||!isWindowsPath(status.socketPath))return{ok:!0,status};return{ok:!1,code:"BAD_STATUS",message:`${alias} runs a Rove too old to serve another machine from Windows (rove ${status.roveVersion||"?"}; it needs \`rove daemon relay --preamble\`). Update it there: \`npm install -g @sma1lboy/rove\`.`}}function firstJsonObject(text){let start=text.indexOf("{");if(start<0)return null;let depth=0,inString=!1,escaped=!1;for(let i=start;i<text.length;i++){let ch=text[i];if(inString){if(escaped)escaped=!1;else if(ch==="\\")escaped=!0;else if(ch==='"')inString=!1;continue}if(ch==='"')inString=!0;else if(ch==="{")depth++;else if(ch==="}"&&--depth===0)return text.slice(start,i+1)}return null}function looksLikeOldStatus(stdout){let json=firstJsonObject(stdout);if(json===null)return!1;try{let raw=JSON.parse(json);return typeof raw.socketPath==="string"&&typeof raw.ptySocketPath!=="string"}catch{return!1}}function parseStatusJson(stdout){let json=firstJsonObject(stdout);if(json===null)return null;let raw;try{raw=JSON.parse(json)}catch{return null}if(!raw||typeof raw!=="object")return null;let o=raw,socketPath=typeof o.socketPath==="string"?o.socketPath:"",ptySocketPath=typeof o.ptySocketPath==="string"?o.ptySocketPath:"";if(!socketPath||!ptySocketPath)return null;return{socketPath,ptySocketPath,homeDir:typeof o.homeDir==="string"?o.homeDir:"",hostname:typeof o.hostname==="string"?o.hostname:"",roveVersion:statusVersion(o),daemonPid:typeof o.daemonPid==="number"?o.daemonPid:0,relayPreamble:o.relayPreamble===!0}}function statusVersion(o){if(typeof o.roveVersion==="string")return o.roveVersion;let old=o[PRE_RENAME_VERSION_KEY];return typeof old==="string"?old:""}function useStdioTransport(platform=process.platform,machine={}){return platform==="win32"||process.env.ROVE_MACHINE_TRANSPORT==="stdio"||isWindowsMachine(machine)}function relayCommand(machine,pty){let flags=[...pty?["--pty"]:[],...machine.relayPreamble?["--preamble"]:[]];return remoteRove(machine.roveBin,["daemon","relay",...flags])}var servers=new Map;function serve(path,argv,preamble){if(servers.has(path))return Promise.resolve();let server=createServer((conn)=>{let[cmd,...args]=argv,child=spawn2(cmd??"ssh",args,{stdio:["pipe","pipe","ignore"],windowsHide:!0});conn.pipe(child.stdin);let end=()=>{conn.destroy(),child.kill()};if(preamble){let stripper=new PreambleStripper;child.stdout?.on("data",(chunk)=>{let out=stripper.push(chunk);if(out===null)end();else if(out.length>0)conn.write(out)})}else child.stdout?.pipe(conn);conn.on("close",()=>child.kill()),conn.on("error",end),child.on("exit",()=>conn.end()),child.on("error",(err)=>{logClientError("machines",err),end()})});return servers.set(path,server),new Promise((resolve,reject)=>{server.once("error",(err)=>{servers.delete(path),reject(err)}),rmSync(path,{force:!0}),server.unref(),server.listen(path,()=>resolve())})}async function relayAnswers(alias,home){let client=new RoveDaemonClient(localDaemonSocketPath(alias,home));try{return await client.request("hello",{}),!0}catch{return!1}finally{client.close()}}async function ensureStdioForwards(args){mkdirSync2(machineSocketDir(args.alias,args.home),{recursive:!0});let ssh=machineSshArgs(args.alias,args.config,{home:args.home}),preamble=args.config.relayPreamble===!0;await serve(localDaemonSocketPath(args.alias,args.home),[...ssh,relayCommand(args.config,!1)],preamble),await serve(localPtySocketPath(args.alias,args.home),[...ssh,relayCommand(args.config,!0)],preamble)}function stopStdioForwards(alias,home){for(let path of[localDaemonSocketPath(alias,home),localPtySocketPath(alias,home)])servers.get(path)?.close(),servers.delete(path)}var DIR_MODE=448,BACKOFF_START_MS=500,BACKOFF_MAX_MS=5000,HEALTH_INTERVAL_MS=1e4;function startTunnel(opts){let home=opts.home??homeDir(),daemonSocketPath=localDaemonSocketPath(opts.alias,home),ptySocketPath=localPtySocketPath(opts.alias,home),listeners=new Set,state="connecting",stopped=!1,backoff=BACKOFF_START_MS,setState=(next)=>{if(state===next)return;state=next;for(let listener of listeners)listener(next)},handle={alias:opts.alias,daemonSocketPath,ptySocketPath,state:()=>state,onState:(listener)=>{return listeners.add(listener),()=>listeners.delete(listener)},stop:()=>{if(stopped=!0,useStdioTransport(void 0,opts.config))stopStdioForwards(opts.alias,home)}},schedule=opts.setTimeoutFn??((fn,ms)=>setTimeout(fn,ms)),stdio=()=>ensureStdioForwards({alias:opts.alias,config:opts.config,home}).then(()=>relayAnswers(opts.alias,home)).catch(()=>!1),ensure=opts.ensureFn??(useStdioTransport(void 0,opts.config)?stdio:()=>ensureForwards({alias:opts.alias,config:opts.config,remoteDaemonSocket:opts.remoteDaemonSocket,remotePtySocket:opts.remotePtySocket,home})),check=opts.checkFn??(()=>socketAnswers(daemonSocketPath)),attempt=async()=>{if(stopped)return;let up=await ensure();if(stopped)return;if(up){backoff=BACKOFF_START_MS,setState("online"),schedule(()=>void health(),HEALTH_INTERVAL_MS);return}setState("offline");let wait=backoff;backoff=Math.min(backoff*2,BACKOFF_MAX_MS),schedule(()=>void attempt(),wait)},health=async()=>{if(stopped)return;if(await check()){if(!stopped)schedule(()=>void health(),HEALTH_INTERVAL_MS);return}setState("offline"),attempt()};return attempt(),handle}async function ensureForwards(args){let home=args.home??homeDir();if(useStdioTransport(void 0,args.config))return ensureStdioForwards({alias:args.alias,config:args.config,home}).then(()=>relayAnswers(args.alias,home)).catch(()=>!1);let daemonLocal=localDaemonSocketPath(args.alias,home),ptyLocal=localPtySocketPath(args.alias,home);try{mkdirSync3(machineSocketDir(args.alias,home),{recursive:!0,mode:DIR_MODE})}catch{}if(await socketAnswers(daemonLocal))return!0;for(let path of[daemonLocal,ptyLocal])try{rmSync2(path,{force:!0})}catch{}let base=machineSshArgs(args.alias,args.config,{home});if(await runSsh([...base,"true"])!==0)return!1;let target=base.at(-1)??args.alias,flags=base.slice(0,-1);return await runSsh([...flags,"-O","forward","-L",`${daemonLocal}:${args.remoteDaemonSocket}`,"-L",`${ptyLocal}:${args.remotePtySocket}`,target])===0&&await socketAnswers(daemonLocal)}function runSsh(argv){return new Promise((resolve)=>{let child=spawn3(argv[0]??"ssh",argv.slice(1),{stdio:"ignore"});child.on("error",()=>resolve(-1)),child.on("close",(code)=>resolve(code??-1))})}function socketAnswers(path){return new Promise((resolve)=>{let socket=netConnect(path),done=(answer)=>{socket.destroy(),resolve(answer)};socket.once("connect",()=>done(!0)),socket.once("error",()=>done(!1)),socket.setTimeout(1500,()=>done(!1))})}
13
+ export{isValidMachineAlias,parseSshTarget,defaultMachineAlias,sanitizeAlias,sshTargetOf,readMachines,listMachines,getMachine,addMachine,updateMachine,setMachineIdentity,removeMachine,dedupeMachines,duplicateAliasOf,machineSocketDir,localDaemonSocketPath,localPtySocketPath,machineSshArgs,isWindowsMachine,windowsAuthorizeKeySteps,discoverMachine,learnedFrom,remoteRove,sshServerIsWindows,startTunnel,ensureForwards};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,PACKAGE_NAME,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,ZH_NOTES_ASSET,breakingVersionsCrossed,channelOf,checkLatestVersion,compareSemver,fetchReleaseNotes,fetchReleaseNotesRange,fetchReleaseSummaries,isNewerSemver,owningNpmPrefix,recommendedGlobalInstallCommand,releasePageUrl,repoSlug,updateCommandFor}from"./chunk-4vad6e9t.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{updateCommandFor,repoSlug,releasePageUrl,recommendedGlobalInstallCommand,owningNpmPrefix,isNewerSemver,fetchReleaseSummaries,fetchReleaseNotesRange,fetchReleaseNotes,compareSemver,checkLatestVersion,channelOf,breakingVersionsCrossed,ZH_NOTES_ASSET,UPDATE_SCRIPT_URL,UPDATE_COMMAND,RELEASE_CHANNELS,PACKAGE_NAME,DEFAULT_RELEASE_CHANNEL,CURRENT_VERSION,BREAKING_VERSIONS};
2
+ import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,PACKAGE_NAME,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,ZH_NOTES_ASSET,breakingVersionsCrossed,channelOf,checkLatestVersion,compareSemver,fetchReleaseNotes,fetchReleaseNotesRange,fetchReleaseSummaries,isNewerSemver,owningNpmPrefix,recommendedGlobalInstallCommand,releasePageUrl,repoSlug,updateCommandFor}from"./chunk-qsqde2kj.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{updateCommandFor,repoSlug,releasePageUrl,recommendedGlobalInstallCommand,owningNpmPrefix,isNewerSemver,fetchReleaseSummaries,fetchReleaseNotesRange,fetchReleaseNotes,compareSemver,checkLatestVersion,channelOf,breakingVersionsCrossed,ZH_NOTES_ASSET,UPDATE_SCRIPT_URL,UPDATE_COMMAND,RELEASE_CHANNELS,PACKAGE_NAME,DEFAULT_RELEASE_CHANNEL,CURRENT_VERSION,BREAKING_VERSIONS};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{machineTasks}from"./chunk-9bae7f3m.js";import{dedupeMachines,listMachines}from"./chunk-zaw71cf4.js";import"./chunk-sq6zj3fb.js";import"./chunk-r5j8fycq.js";import"./chunk-xfgqae9t.js";import"./chunk-j1z9kgyd.js";import"./chunk-y1xnjx8x.js";import"./chunk-2brn8e1y.js";import"./chunk-7kysxgbq.js";import"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.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-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";async function mergeTaskList(local){let machines=dedupeMachines(listMachines());if(machines.length===0)return local;let tasks=(local.tasks??[]).map((task)=>({...task,origin:{machineId:"local",hostLabel:"local"}}));for(let entry of machines){let remote=await machineTasks(entry);if(!remote)continue;let hostLabel=entry.alias;for(let task of remote)tasks.push({...task,origin:{machineId:entry.alias,hostLabel}})}return{tasks}}export{mergeTaskList};
package/dist/cli/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{matchPathGlob}from"./chunk-kg11c1py.js";import{failureHint}from"./chunk-6jk2khme.js";import{switchFlag}from"./chunk-zsfyj36g.js";import{expandTilde}from"./chunk-2r1fgng9.js";import{connectIfRunning}from"./chunk-3249q53j.js";import"./chunk-67nb2sdd.js";import"./chunk-7vkvfawc.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{BUILTIN_VENDORS,coerceVendorId,isBuiltinVendor}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-4vad6e9t.js";import{rejectionReason}from"./chunk-7mwvvj46.js";import"./chunk-z1nj22va.js";import"./chunk-br8r72bf.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-bj6sa371.js";import"./chunk-1ffqacz9.js";import"./chunk-cmp64q4j.js";import"./chunk-cynqvjk8.js";import"./chunk-96kvwzj8.js";import"./chunk-0g4wc808.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{resolve as resolve2}from"path";function debugEnabled(env){return env.ROVE_DEBUG==="1"||env.ROVE_DEBUG==="1"}function failureSubject(cliName,argv){let verb=argv[2];return verb&&!verb.startsWith("-")&&!verb.includes("/")?`${cliName} ${verb}`:cliName}function summarizeCliGitError(message,cwd){if(!/not a git repository/i.test(message))return null;return`${cwd} is not a git repository \u2014 run this inside one, or pass a repo path.`}function formatCliFailure(err,opts){let subject=failureSubject(opts.cliName,opts.argv);if(debugEnabled(opts.env))return`${subject}: ${String(err instanceof Error?err.stack??err:err)}
3
+ import{matchPathGlob}from"./chunk-kg11c1py.js";import{failureHint}from"./chunk-6jk2khme.js";import{switchFlag}from"./chunk-zsfyj36g.js";import{expandTilde}from"./chunk-2r1fgng9.js";import{connectIfRunning}from"./chunk-387ecqb8.js";import"./chunk-kgs56yns.js";import"./chunk-4hnz77xf.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{BUILTIN_VENDORS,coerceVendorId,isBuiltinVendor}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-qsqde2kj.js";import{rejectionReason}from"./chunk-7mwvvj46.js";import"./chunk-z1nj22va.js";import"./chunk-br8r72bf.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-bj6sa371.js";import"./chunk-1ffqacz9.js";import"./chunk-cmp64q4j.js";import"./chunk-sza3tyhs.js";import"./chunk-96kvwzj8.js";import"./chunk-0g4wc808.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{resolve as resolve2}from"path";function debugEnabled(env){return env.ROVE_DEBUG==="1"||env.ROVE_DEBUG==="1"}function failureSubject(cliName,argv){let verb=argv[2];return verb&&!verb.startsWith("-")&&!verb.includes("/")?`${cliName} ${verb}`:cliName}function summarizeCliGitError(message,cwd){if(!/not a git repository/i.test(message))return null;return`${cwd} is not a git repository \u2014 run this inside one, or pass a repo path.`}function formatCliFailure(err,opts){let subject=failureSubject(opts.cliName,opts.argv);if(debugEnabled(opts.env))return`${subject}: ${String(err instanceof Error?err.stack??err:err)}
4
4
  ${failureHint(err,opts.cliName)}`;let raw=errorMessage(err),git=summarizeCliGitError(raw,opts.cwd);return`${subject}: ${git??`${raw}
5
- ${failureHint(err,opts.cliName)}`}`}var DYNAMIC_COMMANDS=new Map([["completions",async(args)=>{let{runCompletionsSubcommand}=await import("./chunk-jeve2yf8.js");await runCompletionsSubcommand(args)}],["export",async(args)=>{let{runExportSubcommand}=await import("./chunk-1sg8c2ke.js");await runExportSubcommand(args)}],["repo",async(args)=>{let{runRepoSubcommand}=await import("./chunk-gqh6atvp.js");await runRepoSubcommand(args)}],["api",async(args)=>{let{runApiSubcommand}=await import("./chunk-0ay6bvfg.js");await runApiSubcommand(args)}],["update",async(args)=>{let{runUpdateSubcommand}=await import("./chunk-t6wwh4jk.js");await runUpdateSubcommand(args)}],["theme",async(args)=>{let{runThemeSubcommand}=await import("./chunk-ycd28edp.js");await runThemeSubcommand(args)}],["feedback",async(args)=>{let{runFeedbackSubcommand}=await import("./chunk-k0hwkm75.js");await runFeedbackSubcommand(args)}],["daemon",async(args)=>{let{runDaemonSubcommand}=await import("./chunk-s70mf80s.js");await runDaemonSubcommand(args)}],["machine",async(args)=>{let{runMachineSubcommand}=await import("./chunk-ma74dbah.js");await runMachineSubcommand(args)}],["weixin",async(args)=>{let{runChannelCli}=await import("./chunk-mkb88wzr.js");await runChannelCli("weixin",args)}],["doctor",async(args)=>{let{runDoctorSubcommand}=await import("./chunk-32yhdeak.js");await runDoctorSubcommand(args)}],["config",async(args)=>{let{runConfigSubcommand}=await import("./chunk-bexnp6g4.js");await runConfigSubcommand(args)}],["reset",async(args)=>{let{runResetSubcommand}=await import("./chunk-0hmwkm43.js");await runResetSubcommand(args)}],["pty-host",async(args)=>{let{runPtyHostSubcommand}=await import("./chunk-9kje0m11.js");await runPtyHostSubcommand(args)}],["skill",async(args)=>{let{runSkillSubcommand}=await import("./chunk-682xhgvd.js");await runSkillSubcommand(args)}],["plugin",async(args)=>{let{runPluginSubcommand}=await import("./chunk-x5j66mvs.js");await runPluginSubcommand(args)}],["hook",async(args)=>{let{runHookSubcommand}=await import("./chunk-e31be3k4.js");await runHookSubcommand(args)}]]);import{statSync}from"fs";import{resolve}from"path";async function openLocalOrchestrator(){let{TaskIndexStore}=await import("./chunk-35jbdbkm.js"),{GitWorktreeManager}=await import("./chunk-07daqn0z.js"),{Orchestrator}=await import("./chunk-r5h3dc1y.js"),store=new TaskIndexStore;return await store.load(),new Orchestrator({store,worktrees:new GitWorktreeManager})}async function withDaemonOrLocal(bridge){let client=await connectIfRunning();if(client)try{return await bridge.daemon(client)}finally{client.close()}let orch=await openLocalOrchestrator();return bridge.local(orch)}function isPathLikeArg(arg){return arg==="."||arg===".."||arg.startsWith("./")||arg.startsWith("../")||arg.startsWith(".\\")||arg.startsWith("..\\")||pathSyntax(arg).isAbsolute(arg)||arg.startsWith("/")||arg==="~"||arg.startsWith("~/")||arg.startsWith("~\\")}async function projectRootFor(dir){let{isGitRepo,resolveRepoRoot}=await import("./chunk-hv79ys32.js"),{projectRejection}=await import("./chunk-qnpkhvrn.js"),top=resolveRepoRoot(dir);if(top!==dir)return null;return projectRejection(top,isGitRepo,"explicit")?null:top}async function runOpenDirectory(arg){let dir=resolve(process.cwd(),expandTilde(arg)),isDir=!1;try{isDir=statSync(dir).isDirectory()}catch{isDir=!1}if(!isDir)process.stderr.write(`${ROVE_PRODUCT_NAME}: "${arg}" is not a directory (resolved to ${dir}).
6
- `),process.exit(1);let projectRoot=await projectRootFor(dir);await withDaemonOrLocal({daemon:async(client)=>{let taskId;if(projectRoot)taskId=(await client.request("task.ensureMain",{repo:projectRoot})).task.id;else taskId=(await client.request("task.openDir",{dir})).taskId;await client.request("task.setActive",{taskId})},local:async(orch)=>{let task=projectRoot?await orch.ensureMainTask(projectRoot):await orch.openDirectoryTask({dir}),{writeLastActiveTaskId}=await import("./chunk-y8b2p44q.js");writeLastActiveTaskId(String(task.id))}});let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-nrwyxhtz.js");await launchTui()}function topLevelUsage(cliName=ROVE_PRODUCT_NAME){return[`${cliName} ${CURRENT_VERSION}`,"",`Usage: ${cliName} [command] [options]`,"","Run with no command to launch PureTUI.",`Run \`${cliName} .\` (or \`${cliName} <path>\`) to open a directory as a standalone task.`,"","Commands:"," completions <shell> Generate shell completion script (bash/zsh/fish)"," add [path] Save a repo path for the new-task picker"," remove [path] Forget a saved project (inverse of add; non-destructive)"," adopt [glob] Import existing git worktrees as tasks"," export [--csv|--json] Print the task list (json/csv/table; daemon-free)"," repo <verb> Per-repo init script + first prompt (show|set|unset)",` api <verb> Scriptable RPC surface for agents (see \`${cliName} api --help\`)`," daemon <verb> Manage the daemon (start|stop|status|restart)"," machine <verb> Other computers running Rove (add|doctor|remove|list)"," weixin <verb> Talk to Rove from WeChat (login|logout|status|allow|deny|send|agent)"," doctor [--report|--fix] Diagnose daemon/PTY/engines/git; --fix walks the remedies",` config [--path] Open ${cliName}'s config file (state.json) in your editor`," reset [--hard] Stop runtimes; optionally wipe task/UI state"," theme <verb> Manage user themes (list|add|remove)",` skill <verb> Install the ${cliName} agent skill (install|status|command|print)`," plugin <verb> Install and run plugins (install|link|list|action|\u2026)"," feedback Send feedback to GitHub Discussions",` update [version|channel|list] Self-update ${cliName}, switch channel, or list versions`,"","Options:"," -v, --version Print version"," -h, --help Print this help"," --skill Print the agent skill file and exit"," --web [bridge flags] Serve the browser client on 127.0.0.1 and print its link"].join(`
5
+ ${failureHint(err,opts.cliName)}`}`}var DYNAMIC_COMMANDS=new Map([["completions",async(args)=>{let{runCompletionsSubcommand}=await import("./chunk-x7yxf5b5.js");await runCompletionsSubcommand(args)}],["export",async(args)=>{let{runExportSubcommand}=await import("./chunk-ecrw211n.js");await runExportSubcommand(args)}],["repo",async(args)=>{let{runRepoSubcommand}=await import("./chunk-gqh6atvp.js");await runRepoSubcommand(args)}],["api",async(args)=>{let{runApiSubcommand}=await import("./chunk-ajad94vy.js");await runApiSubcommand(args)}],["update",async(args)=>{let{runUpdateSubcommand}=await import("./chunk-bm0qe5gy.js");await runUpdateSubcommand(args)}],["theme",async(args)=>{let{runThemeSubcommand}=await import("./chunk-ycd28edp.js");await runThemeSubcommand(args)}],["feedback",async(args)=>{let{runFeedbackSubcommand}=await import("./chunk-wqvmvgaf.js");await runFeedbackSubcommand(args)}],["daemon",async(args)=>{let{runDaemonSubcommand}=await import("./chunk-adj65prq.js");await runDaemonSubcommand(args)}],["machine",async(args)=>{let{runMachineSubcommand}=await import("./chunk-98s0fhmv.js");await runMachineSubcommand(args)}],["weixin",async(args)=>{let{runChannelCli}=await import("./chunk-qnaxnx5w.js");await runChannelCli("weixin",args)}],["doctor",async(args)=>{let{runDoctorSubcommand}=await import("./chunk-sap4tggv.js");await runDoctorSubcommand(args)}],["config",async(args)=>{let{runConfigSubcommand}=await import("./chunk-bexnp6g4.js");await runConfigSubcommand(args)}],["reset",async(args)=>{let{runResetSubcommand}=await import("./chunk-f6hgkc7g.js");await runResetSubcommand(args)}],["pty-host",async(args)=>{let{runPtyHostSubcommand}=await import("./chunk-5c4vr45n.js");await runPtyHostSubcommand(args)}],["skill",async(args)=>{let{runSkillSubcommand}=await import("./chunk-nf3kg9rz.js");await runSkillSubcommand(args)}],["plugin",async(args)=>{let{runPluginSubcommand}=await import("./chunk-k5k2jpxk.js");await runPluginSubcommand(args)}],["hook",async(args)=>{let{runHookSubcommand}=await import("./chunk-vjp7drne.js");await runHookSubcommand(args)}]]);import{statSync}from"fs";import{resolve}from"path";async function openLocalOrchestrator(){let{TaskIndexStore}=await import("./chunk-22d4fjjb.js"),{GitWorktreeManager}=await import("./chunk-qfe1wf5c.js"),{Orchestrator}=await import("./chunk-6jj82bej.js"),store=new TaskIndexStore;return await store.load(),new Orchestrator({store,worktrees:new GitWorktreeManager})}async function withDaemonOrLocal(bridge){let client=await connectIfRunning();if(client)try{return await bridge.daemon(client)}finally{client.close()}let orch=await openLocalOrchestrator();return bridge.local(orch)}function isPathLikeArg(arg){return arg==="."||arg===".."||arg.startsWith("./")||arg.startsWith("../")||arg.startsWith(".\\")||arg.startsWith("..\\")||pathSyntax(arg).isAbsolute(arg)||arg.startsWith("/")||arg==="~"||arg.startsWith("~/")||arg.startsWith("~\\")}async function projectRootFor(dir){let{isGitRepo,resolveRepoRoot}=await import("./chunk-hv79ys32.js"),{projectRejection}=await import("./chunk-qnpkhvrn.js"),top=resolveRepoRoot(dir);if(top!==dir)return null;return projectRejection(top,isGitRepo,"explicit")?null:top}async function runOpenDirectory(arg){let dir=resolve(process.cwd(),expandTilde(arg)),isDir=!1;try{isDir=statSync(dir).isDirectory()}catch{isDir=!1}if(!isDir)process.stderr.write(`${ROVE_PRODUCT_NAME}: "${arg}" is not a directory (resolved to ${dir}).
6
+ `),process.exit(1);let projectRoot=await projectRootFor(dir);await withDaemonOrLocal({daemon:async(client)=>{let taskId;if(projectRoot)taskId=(await client.request("task.ensureMain",{repo:projectRoot})).task.id;else taskId=(await client.request("task.openDir",{dir})).taskId;await client.request("task.setActive",{taskId})},local:async(orch)=>{let task=projectRoot?await orch.ensureMainTask(projectRoot):await orch.openDirectoryTask({dir}),{writeLastActiveTaskId}=await import("./chunk-y8b2p44q.js");writeLastActiveTaskId(String(task.id))}});let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-80t9z2b2.js");await launchTui()}function topLevelUsage(cliName=ROVE_PRODUCT_NAME){return[`${cliName} ${CURRENT_VERSION}`,"",`Usage: ${cliName} [command] [options]`,"","Run with no command to launch PureTUI.",`Run \`${cliName} .\` (or \`${cliName} <path>\`) to open a directory as a standalone task.`,"","Commands:"," completions <shell> Generate shell completion script (bash/zsh/fish)"," add [path] Save a repo path for the new-task picker"," remove [path] Forget a saved project (inverse of add; non-destructive)"," adopt [glob] Import existing git worktrees as tasks"," export [--csv|--json] Print the task list (json/csv/table; daemon-free)"," repo <verb> Per-repo init script + first prompt (show|set|unset)",` api <verb> Scriptable RPC surface for agents (see \`${cliName} api --help\`)`," daemon <verb> Manage the daemon (start|stop|status|restart)"," machine <verb> Other computers running Rove (add|doctor|remove|list)"," weixin <verb> Talk to Rove from WeChat (login|logout|status|allow|deny|send|agent)"," doctor [--report|--fix] Diagnose daemon/PTY/engines/git; --fix walks the remedies",` config [--path] Open ${cliName}'s config file (state.json) in your editor`," reset [--hard] Stop runtimes; optionally wipe task/UI state"," theme <verb> Manage user themes (list|add|remove)",` skill <verb> Install the ${cliName} agent skill (install|status|command|print)`," plugin <verb> Install and run plugins (install|link|list|action|\u2026)"," feedback Send feedback to GitHub Discussions",` update [version|channel|list] Self-update ${cliName}, switch channel, or list versions`,"","Options:"," -v, --version Print version"," -h, --help Print this help"," --skill Print the agent skill file and exit"," --web [bridge flags] Serve the browser client on 127.0.0.1 and print its link"].join(`
7
7
  `)}var CLI_NAME=ROVE_PRODUCT_NAME,ADD_USAGE=[`Usage: ${CLI_NAME} add [path]`,` ${CLI_NAME} add --remote --host <host> --user <user> --path <basePath> [--port N] [--key [path] | --password]`,"","Save a repo for the new-task picker. With --remote, register an SSH-backed","project whose worktrees live on <host>; engine launch over SSH is pending.",""].join(`
8
8
  `);async function runAddSubcommand(rest){let arg=rest[0];if(arg==="--help"||arg==="-h"||arg==="help"){process.stdout.write(ADD_USAGE);return}if(arg==="--remote"){let{runAddRemote}=await import("./chunk-gcg5h667.js");await runAddRemote(rest.slice(1));return}if(arg?.startsWith("-"))process.stderr.write(`${CLI_NAME} add: unknown flag "${arg}"
9
9
 
@@ -26,4 +26,4 @@ ${ADOPT_USAGE}
26
26
  pass a path glob to adopt, e.g. ${CLI_NAME} adopt '${repo}/*' --yes`);return}let matched=worktrees.filter(isMatch);if(matched.length===0){console.log(`
27
27
  no worktrees match glob ${JSON.stringify(glob)}`);return}if(!yes){console.log(`
28
28
  ${matched.length} worktree(s) match \u2014 re-run with --yes to adopt them`);return}await adoptWorktreesInto(orch,repo,matched,vendor),console.log(`done \u2014 adopted ${matched.length} worktree(s)`)}function printTopLevelUsage(out){out.write(`${topLevelUsage()}
29
- `)}var COMMANDS=new Map([["add",runAddSubcommand],["remove",runRemoveSubcommand],["adopt",runAdoptSubcommand],...DYNAMIC_COMMANDS]);async function main(){let[,,...rawArgs]=process.argv,[subcommand,...rest]=rawArgs;if(subcommand==="--version"||subcommand==="-v"||subcommand==="version"){let{CURRENT_VERSION:CURRENT_VERSION2}=await import("./chunk-pn2f24wx.js");console.log(`${CLI_NAME} ${CURRENT_VERSION2}`);return}if(subcommand==="--help"||subcommand==="-h"||subcommand==="help"){printTopLevelUsage(process.stdout);return}if(subcommand==="--skill"){let{runSkillSubcommand}=await import("./chunk-682xhgvd.js");await runSkillSubcommand(["print"]);return}if(subcommand==="--web"){let{runWebSubcommand}=await import("./chunk-sk19nvdj.js");await runWebSubcommand(rest);return}let handler=subcommand!==void 0?COMMANDS.get(subcommand):void 0;if(handler){await handler(rest);return}if(subcommand!==void 0&&isPathLikeArg(subcommand)){await runOpenDirectory(subcommand);return}if(subcommand!==void 0)console.error(`${CLI_NAME}: unknown command '${subcommand}'`),printTopLevelUsage(process.stderr),process.exit(2);let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-nrwyxhtz.js");await launchTui()}main().catch((err)=>{console.error(formatCliFailure(err,{cliName:CLI_NAME,argv:process.argv,cwd:process.cwd(),env:process.env})),process.exit(1)});
29
+ `)}var COMMANDS=new Map([["add",runAddSubcommand],["remove",runRemoveSubcommand],["adopt",runAdoptSubcommand],...DYNAMIC_COMMANDS]);async function main(){let[,,...rawArgs]=process.argv,[subcommand,...rest]=rawArgs;if(subcommand==="--version"||subcommand==="-v"||subcommand==="version"){let{CURRENT_VERSION:CURRENT_VERSION2}=await import("./chunk-zxkjpfp5.js");console.log(`${CLI_NAME} ${CURRENT_VERSION2}`);return}if(subcommand==="--help"||subcommand==="-h"||subcommand==="help"){printTopLevelUsage(process.stdout);return}if(subcommand==="--skill"){let{runSkillSubcommand}=await import("./chunk-nf3kg9rz.js");await runSkillSubcommand(["print"]);return}if(subcommand==="--web"){let{runWebSubcommand}=await import("./chunk-63t3sh8c.js");await runWebSubcommand(rest);return}let handler=subcommand!==void 0?COMMANDS.get(subcommand):void 0;if(handler){await handler(rest);return}if(subcommand!==void 0&&isPathLikeArg(subcommand)){await runOpenDirectory(subcommand);return}if(subcommand!==void 0)console.error(`${CLI_NAME}: unknown command '${subcommand}'`),printTopLevelUsage(process.stderr),process.exit(2);let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-80t9z2b2.js");await launchTui()}main().catch((err)=>{console.error(formatCliFailure(err,{cliName:CLI_NAME,argv:process.argv,cwd:process.cwd(),env:process.env})),process.exit(1)});
package/dist/cli/rove.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{join as l}from"node:path";import{createInterface as a}from"node:readline/promises";import{pathToFileURL as ss}from"node:url";import{spawnSync as Q}from"node:child_process";import{constants as e,accessSync as V}from"node:fs";import{homedir as R}from"node:os";import{basename as o,delimiter as I,dirname as U,join as y}from"node:path";import{fileURLToPath as T}from"node:url";var H={$schema:"https://json.schemastore.org/package.json",name:"@sma1lboy/rove",version:"0.9.272",description:"Rove — the agent multiplexer for your terminal. Run coding agents on parallel tasks with isolated worktrees and persistent sessions.",keywords:["terminal","tui","cli","multiplexer","ai-agents","coding-agent","ai-coding-assistant","agentic-ai","parallel-agents","git-worktree","claude-code","codex","llm","developer-tools"],type:"module",packageManager:"bun@1.3.13",bin:{rove:"dist/cli/rove.js"},files:["dist/cli","dist/completions","dist/web","dist/web-ui","dist/skills","dist/*.wav","README.md","LICENSE"],publishConfig:{access:"public"},repository:{type:"git",url:"git+https://github.com/Sma1lboy/rove.git"},homepage:"https://rove.run",bugs:{url:"https://github.com/Sma1lboy/rove/issues"},engines:{bun:">=1.3.11"},scripts:{dev:"ROVE_DEV=1 bun --conditions=browser ./src/cli/rove.ts","dev:mock":"ROVE_DEV=1 bun ./src/tui-react/mock/host.tsx","dev:sandbox":"bun run scripts/dev-sandbox.ts run","dev:sandbox:reset":"bun run scripts/dev-sandbox.ts reset",build:"bun run scripts/build.ts","build:rubric":"bun run scripts/build-tier-rubric.ts",compile:"bun run scripts/compile.ts",typecheck:"tsc --noEmit","check-i18n":"bun run scripts/check-i18n.ts",test:"bun run test:fast && bun run test:socket","perf:golden":"bun scripts/perf-golden.ts","pty:soak":"bun scripts/pty-soak.ts","test:fast":"vitest run --passWithNoTests --minWorkers=1 --maxWorkers=8","test:socket":"ROVE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:socket:coverage":"ROVE_INCLUDE_SOCKET=1 ROVE_COVERAGE_DAEMON=1 vitest run test/daemon --coverage --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:behavior":"ROVE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --retry=2 --passWithNoTests","test:render":"bun scripts/render-track.mjs",coverage:"vitest run --coverage --passWithNoTests",bench:"vitest bench --run",lint:"biome check .",knip:"knip-bun",postinstall:"bun run scripts/check-preview-deps.ts || true",prepublishOnly:"bun run typecheck && bun run build","plugin-sandbox":"bun run scripts/plugin-sandbox.ts"},"//":"biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",dependencies:{"@ansi-tools/parser":"^1.0.15","@opentui/core":"0.4.3","@opentui/react":"0.4.3","@xterm/addon-serialize":"^0.14.0","@xterm/addon-unicode11":"^0.9.0","@xterm/headless":"^6.0.0",react:"^19.2.8","smol-toml":"^1.7.1",ws:"^8.21.3"},optionalDependencies:{"node-pty":"^1.1.0"},devDependencies:{"@biomejs/biome":"1.9.4","@sma1lboy/rove-daemon":"0.7.23","@tsconfig/bun":"1.0.11","@types/bun":"1.3.14","@types/node":"25.6.2","@types/react":"^19.2.0","@vitest/coverage-v8":"2.1.9",knip:"^6.14.2","react-devtools-core":"^7.0.1","rove-bridge":"0.0.33","rove-weixin":"0.0.33",typescript:"5.8.2",vitest:"2.1.9"},trustedDependencies:[]};var q="rove";var k=q;var z="ROVE_BUN",_="ROVE_NO_BUN_BOOTSTRAP",F="ROVE_SKIP_BUN_CHECK",$=H.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0",L=(s)=>{try{return V(s,e.X_OK),!0}catch{return!1}},B=(s)=>s==="win32"?"bun.exe":"bun",f=(s)=>{let t=Q(s,["--version"],{encoding:"utf8",timeout:5000});if(t.error||t.status!==0)return null;return t.stdout?.split(`
2
+ import{join as l}from"node:path";import{createInterface as a}from"node:readline/promises";import{pathToFileURL as ss}from"node:url";import{spawnSync as Q}from"node:child_process";import{constants as e,accessSync as V}from"node:fs";import{homedir as R}from"node:os";import{basename as o,delimiter as I,dirname as U,join as y}from"node:path";import{fileURLToPath as T}from"node:url";var H={$schema:"https://json.schemastore.org/package.json",name:"@sma1lboy/rove",version:"0.9.273",description:"Rove — the agent multiplexer for your terminal. Run coding agents on parallel tasks with isolated worktrees and persistent sessions.",keywords:["terminal","tui","cli","multiplexer","ai-agents","coding-agent","ai-coding-assistant","agentic-ai","parallel-agents","git-worktree","claude-code","codex","llm","developer-tools"],type:"module",packageManager:"bun@1.3.13",bin:{rove:"dist/cli/rove.js"},files:["dist/cli","dist/completions","dist/web","dist/web-ui","dist/skills","dist/*.wav","README.md","LICENSE"],publishConfig:{access:"public"},repository:{type:"git",url:"git+https://github.com/Sma1lboy/rove.git"},homepage:"https://rove.run",bugs:{url:"https://github.com/Sma1lboy/rove/issues"},engines:{bun:">=1.3.11"},scripts:{dev:"ROVE_DEV=1 bun --conditions=browser ./src/cli/rove.ts","dev:mock":"ROVE_DEV=1 bun ./src/tui-react/mock/host.tsx","dev:sandbox":"bun run scripts/dev-sandbox.ts run","dev:sandbox:reset":"bun run scripts/dev-sandbox.ts reset",build:"bun run scripts/build.ts","build:rubric":"bun run scripts/build-tier-rubric.ts",compile:"bun run scripts/compile.ts",typecheck:"tsc --noEmit","check-i18n":"bun run scripts/check-i18n.ts",test:"bun run test:fast && bun run test:socket","perf:golden":"bun scripts/perf-golden.ts","pty:soak":"bun scripts/pty-soak.ts","test:fast":"vitest run --passWithNoTests --minWorkers=1 --maxWorkers=8","test:socket":"ROVE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:socket:coverage":"ROVE_INCLUDE_SOCKET=1 ROVE_COVERAGE_DAEMON=1 vitest run test/daemon --coverage --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:behavior":"ROVE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --retry=2 --passWithNoTests","test:render":"bun scripts/render-track.mjs",coverage:"vitest run --coverage --passWithNoTests",bench:"vitest bench --run",lint:"biome check .",knip:"knip-bun",postinstall:"bun run scripts/check-preview-deps.ts || true",prepublishOnly:"bun run typecheck && bun run build","plugin-sandbox":"bun run scripts/plugin-sandbox.ts"},"//":"biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",dependencies:{"@ansi-tools/parser":"^1.0.15","@opentui/core":"0.4.3","@opentui/react":"0.4.3","@xterm/addon-serialize":"^0.14.0","@xterm/addon-unicode11":"^0.9.0","@xterm/headless":"^6.0.0",react:"^19.2.8","smol-toml":"^1.7.1",ws:"^8.21.3"},optionalDependencies:{"node-pty":"^1.1.0"},devDependencies:{"@biomejs/biome":"1.9.4","@sma1lboy/rove-daemon":"0.7.23","@tsconfig/bun":"1.0.11","@types/bun":"1.3.14","@types/node":"25.6.2","@types/react":"^19.2.0","@vitest/coverage-v8":"2.1.9",knip:"^6.14.2","react-devtools-core":"^7.0.1","rove-bridge":"0.0.34","rove-weixin":"0.0.34",typescript:"5.8.2",vitest:"2.1.9"},trustedDependencies:[]};var q="rove";var k=q;var z="ROVE_BUN",_="ROVE_NO_BUN_BOOTSTRAP",F="ROVE_SKIP_BUN_CHECK",$=H.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0",L=(s)=>{try{return V(s,e.X_OK),!0}catch{return!1}},B=(s)=>s==="win32"?"bun.exe":"bun",f=(s)=>{let t=Q(s,["--version"],{encoding:"utf8",timeout:5000});if(t.error||t.status!==0)return null;return t.stdout?.split(`
3
3
  `)[0]?.trim()??""};function J(s){return s.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function X(s,t=$){let c=J(s),W=J(t);if(!c||!W)return!0;let b=c.split(".").map(Number),g=W.split(".").map(Number);for(let p=0;p<3;p++){let r=b[p]??0,i=g[p]??0;if(r!==i)return r>i}return!0}function Y(s=process.env){return s[F]==="1"}function A(s={}){let t=s.env??process.env,c=s.platform??process.platform,W=s.home??t.HOME??t.USERPROFILE??R(),b=B(c),g=[],p=t[z]?.trim();if(p)g.push(p);for(let G of(t.PATH??t.Path??"").split(I))if(G)g.push(y(G,b));let r=t.BUN_INSTALL?.trim();if(r)g.push(y(r,"bin",b));if(W)g.push(y(W,".bun","bin",b));let i=s.launcherDir;if(i)g.push(y(i,"..","..","node_modules","bun","bin",b)),g.push(y(i,"..","..","..","bun","bin",b));return g}function N(s={}){let t=s.isExecutable??L;return A(s).find((c)=>t(c))??null}function Z(s={}){let t=s.isExecutable??L,c=s.bunVersionOf??f;if(Y(s.env??process.env))return{bun:N(s),stale:null,unusable:null};let W=null,b=null;for(let g of A(s)){if(!t(g))continue;let p=c(g);if(p===null){b??=g;continue}if(X(p))return{bun:g,stale:W,unusable:b};W??={path:g,version:J(p)??p}}return{bun:null,stale:W,unusable:b}}function h(s){return U(T(s))}function C(s){return o(T(s)).replace(/\.[cm]?[jt]s$/,"")}function n(s=process.platform){return s==="win32"?["powershell","-NoProfile","-Command","irm bun.sh/install.ps1 | iex"]:["bash","-c","curl -fsSL https://bun.sh/install | bash"]}function D(s=k,t=process.platform){let c=t==="win32"?'powershell -c "irm bun.sh/install.ps1 | iex"':"curl -fsSL https://bun.sh/install | bash";return[`${s}: Rove runs on the Bun runtime, and no Bun was found on this machine.`,"","Install Bun, then run this command again:",` ${c}`," npm install -g bun # any platform, if you already have npm","","Or install Bun and Rove together in one step:"," curl -fsSL https://rove.run/install.sh | sh","",`Already have Bun somewhere unusual? Point Rove at it: ${z}=/path/to/bun`,""].join(`
4
4
  `)}function j(s,t,c=k){return[`${c}: this machine's Bun is too old for Rove — found ${t} at ${s}, need ${$} or newer.`,"","Rove's terminals are built on Bun's PTY API. An older Bun ignores it silently,","so every terminal and engine tab would open empty and stay empty.","","Upgrade Bun, then run this command again:"," bun upgrade # Bun installed itself (~/.bun)"," brew upgrade bun # Homebrew"," npm install -g bun@latest # npm-managed Bun","",`Have a newer Bun elsewhere? Point Rove at it: ${z}=/path/to/bun`,`Run on this one anyway (unsupported, terminals will be blank): ${F}=1`,""].join(`
5
5
  `)}function v(s,t=k){return[`${t}: the Bun at ${s} could not be run — \`${s} --version\` failed or never returned.`,"","Rove skipped it rather than starting through it, which would hang or fail","with a message from that binary instead of this one.","","Reinstall Bun, then run this command again:"," curl -fsSL https://bun.sh/install | bash","",`Have a working Bun elsewhere? Point Rove at it: ${z}=/path/to/bun`,""].join(`
@@ -12,7 +12,7 @@ _rove() {
12
12
  fi
13
13
  if [[ ${COMP_CWORD} -eq 2 ]]; then
14
14
  case "${prev}" in
15
- api) COMPREPLY=( $(compgen -W "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch ports viewers review-comments worktree-files worktree-diff worktree-diff-file repo-match repo-list publish deliveries should-run add spawn send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split tab-revive panel-show notify prompt engine-report row-token set-active report attention-dismiss gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land sync delete archive unarchive ensure-worktree remove-worktree worktree-usage discover-adoptable adopt" -- "${cur}") ) ;;
15
+ api) COMPREPLY=( $(compgen -W "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch ports viewers review-comments worktree-files worktree-diff worktree-diff-file repo-match repo-list publish pr-attach deliveries should-run add spawn send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split tab-revive panel-show notify prompt engine-report row-token set-active report attention-dismiss gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land sync delete archive unarchive ensure-worktree remove-worktree worktree-usage discover-adoptable adopt github-status github-login github-logout github-sync github-apply" -- "${cur}") ) ;;
16
16
  daemon) COMPREPLY=( $(compgen -W "status start stop restart relay" -- "${cur}") ) ;;
17
17
  machine) COMPREPLY=( $(compgen -W "add doctor remove list" -- "${cur}") ) ;;
18
18
  plugin) COMPREPLY=( $(compgen -W "install link list search outdated update enable disable unlink uninstall config-dir log action pane" -- "${cur}") ) ;;
@@ -19,7 +19,7 @@ complete -c rove -f -n __fish_use_subcommand -a skill
19
19
  complete -c rove -f -n __fish_use_subcommand -a plugin
20
20
  complete -c rove -f -n __fish_use_subcommand -a feedback
21
21
  complete -c rove -f -n __fish_use_subcommand -a update
22
- complete -c rove -f -n "__fish_seen_subcommand_from api" -a "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch ports viewers review-comments worktree-files worktree-diff worktree-diff-file repo-match repo-list publish deliveries should-run add spawn send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split tab-revive panel-show notify prompt engine-report row-token set-active report attention-dismiss gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land sync delete archive unarchive ensure-worktree remove-worktree worktree-usage discover-adoptable adopt"
22
+ complete -c rove -f -n "__fish_seen_subcommand_from api" -a "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch ports viewers review-comments worktree-files worktree-diff worktree-diff-file repo-match repo-list publish pr-attach deliveries should-run add spawn send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split tab-revive panel-show notify prompt engine-report row-token set-active report attention-dismiss gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land sync delete archive unarchive ensure-worktree remove-worktree worktree-usage discover-adoptable adopt github-status github-login github-logout github-sync github-apply"
23
23
  complete -c rove -f -n "__fish_seen_subcommand_from daemon" -a "status start stop restart relay"
24
24
  complete -c rove -f -n "__fish_seen_subcommand_from machine" -a "add doctor remove list"
25
25
  complete -c rove -f -n "__fish_seen_subcommand_from plugin" -a "install link list search outdated update enable disable unlink uninstall config-dir log action pane"
@@ -13,7 +13,7 @@ _rove() {
13
13
 
14
14
  verbs=()
15
15
  case "${words[2]}" in
16
- api) verbs=("schema" "engine-list" "list" "get-task" "pty-list" "collect" "context" "digest" "agent-turns" "inspect" "read-output" "watch" "ports" "viewers" "review-comments" "worktree-files" "worktree-diff" "worktree-diff-file" "repo-match" "repo-list" "publish" "deliveries" "should-run" "add" "spawn" "send" "dispatch" "interrupt" "note" "note-list" "note-delete" "pane-open" "pane-close" "pane-graphics" "tab-close" "tab-new" "tab-split" "tab-revive" "panel-show" "notify" "prompt" "engine-report" "row-token" "set-active" "report" "attention-dismiss" "gate-raise" "gate-answer" "gate-withdraw" "gates" "feedback" "issue-list" "issue-create" "issue-update" "issue-delete" "routine-list" "routine-create" "routine-update" "routine-delete" "routine-run-now" "routine-respond" "routine-runs" "workitem-list" "workitem-start" "update" "land" "sync" "delete" "archive" "unarchive" "ensure-worktree" "remove-worktree" "worktree-usage" "discover-adoptable" "adopt") ;;
16
+ api) verbs=("schema" "engine-list" "list" "get-task" "pty-list" "collect" "context" "digest" "agent-turns" "inspect" "read-output" "watch" "ports" "viewers" "review-comments" "worktree-files" "worktree-diff" "worktree-diff-file" "repo-match" "repo-list" "publish" "pr-attach" "deliveries" "should-run" "add" "spawn" "send" "dispatch" "interrupt" "note" "note-list" "note-delete" "pane-open" "pane-close" "pane-graphics" "tab-close" "tab-new" "tab-split" "tab-revive" "panel-show" "notify" "prompt" "engine-report" "row-token" "set-active" "report" "attention-dismiss" "gate-raise" "gate-answer" "gate-withdraw" "gates" "feedback" "issue-list" "issue-create" "issue-update" "issue-delete" "routine-list" "routine-create" "routine-update" "routine-delete" "routine-run-now" "routine-respond" "routine-runs" "workitem-list" "workitem-start" "update" "land" "sync" "delete" "archive" "unarchive" "ensure-worktree" "remove-worktree" "worktree-usage" "discover-adoptable" "adopt" "github-status" "github-login" "github-logout" "github-sync" "github-apply") ;;
17
17
  daemon) verbs=("status" "start" "stop" "restart" "relay") ;;
18
18
  machine) verbs=("add" "doctor" "remove" "list") ;;
19
19
  plugin) verbs=("install" "link" "list" "search" "outdated" "update" "enable" "disable" "unlink" "uninstall" "config-dir" "log" "action" "pane") ;;
@@ -3,7 +3,7 @@ name: rove
3
3
  description: Use when controlling Rove tasks, parallel coding attempts, hosted agent sessions, task lifecycle, or the daemon-owned issue tracker from a shell. Also the ONLY channel for messaging another agent session on this machine — `rove api send`, never a peer/MCP side channel.
4
4
  ---
5
5
 
6
- <!-- rove-skill-version: 86 — bump in lockstep with ROVE_SKILL_VERSION (src/lib/skill-install.ts). -->
6
+ <!-- rove-skill-version: 89 — bump in lockstep with ROVE_SKILL_VERSION (src/lib/skill-install.ts). -->
7
7
 
8
8
  # Rove shell control
9
9
 
@@ -491,7 +491,8 @@ A question only the owner can answer goes through a gate, not a MEMO line or the
491
491
 
492
492
  | Verb | Purpose |
493
493
  |---|---|
494
- | `update [--task-id ID] [--title T] [--branch B] [--command CMD] [--model M] [--effort L] [--pinned true\|false] [--status S] [--report-branch B --report-pr N --report-summary TEXT]` | Change any combination of fields in one call: validated first, applied branch → command → model/effort → title → pinned → status; not atomic (a mid-way failure reports `applied`). Without `--task-id` it changes your own task. `--command` takes effect on the next launch; `--tab tab-N` with `--title` renames that tab (needs `--task-id`). `--status` sets the lifecycle LABEL (`backlog`/`in_progress`/`in_review`/`done`/`canceled`/`error`) — cosmetic: the row, its Worktree, its branch and its engine all stay, and `canceled` does NOT close or clean up anything. `--report-*` (need `--status`) record what you delivered: `update --task-id "$ROVE_TASK_ID" --status in_review --report-pr N` |
494
+ | `update [--task-id ID] [--title T] [--branch B] [--parent ID\|none] [--command CMD] [--model M] [--effort L] [--pinned true\|false] [--status S] [--report-branch B --report-pr N --report-summary TEXT]` | Change any combination of fields in one call: validated first, applied parent → branch → command → model/effort → title → pinned → status; not atomic (a mid-way failure reports `applied`). Without `--task-id` it changes your own task. `--parent ID` moves the task under another without touching its engine, tabs, worktree or branch, and points its reply address at ID (its bare `send` reaches ID); `--parent none` makes it top level; `--group G --parent ID` moves a whole `add --count` round. Archiving the new parent (or `delete --cascade`) then takes the moved task along. Refusals: `PARENT_NOT_FOUND` (parent must be on the task's machine), `PARENT_IS_SELF`, `PARENT_CYCLE`, `TASK_NOT_MOVABLE`. `--command` takes effect on the next launch; `--tab tab-N` with `--title` renames that tab (needs `--task-id`). `--status` sets the lifecycle LABEL (`backlog`/`in_progress`/`in_review`/`done`/`canceled`/`error`) — cosmetic: the row, its Worktree, its branch and its engine all stay, and `canceled` does NOT close or clean up anything. `--report-*` (need `--status`) record what you delivered: `update --task-id "$ROVE_TASK_ID" --status in_review --report-pr N` |
495
+ | `pr-attach --url URL\|N [--task-id ID]` | Show a PR on your task's row (live state). Run once after opening a PR from any directory; idempotent; `publish` does it for you |
495
496
  | `set-active --task-id ID` / `--none` | Change shared active task |
496
497
  | `ensure-worktree --task-id ID` | Materialize without starting an engine |
497
498
  | `worktree-usage [--task-id ID]` | Disk each task's own worktrees take, largest first (archived too); `ignoredBytes` is the gitignored part. Pick what to free with `remove-worktree` |
@@ -678,6 +679,10 @@ rove api get-task --task-id "$ROVE_TASK_ID" # .task.prStatus.checkState
678
679
  `unknown`. Anything but `passing` is not green. Report what the field says,
679
680
  not what your local run implied.
680
681
 
682
+ Opened a PR from any directory (`gh pr create`, a worktree Rove does not know)?
683
+ Run `rove api pr-attach --url <url>` once so it shows on your task's row with
684
+ live state. Idempotent; `rove api publish` already does it.
685
+
681
686
  **Coordinator side** — do NOT block or poll. Keep working (or end your
682
687
  turn); each worker's outcome arrives in your chat as a `[ROVE PEER]` message
683
688
  with its task id. What arrives is the worker's claim, not Rove-verified —
@@ -89,6 +89,7 @@ worktree-diff-file --task-id(REQ) --path(REQ) --scope{branch|working}(REQ)
89
89
  deliveries --task-id(REQ) --limit
90
90
  should-run --task-id(REQ) --tab
91
91
  gates --repo --task-id --status{open|answered|withdrawn|all}(open)
92
+ github-status (none)
92
93
  ```
93
94
  <!-- generated:end -->
94
95
 
@@ -238,24 +239,29 @@ clear the shared active task.
238
239
 
239
240
  <!-- generated:begin create,edit,lifecycle -->
240
241
  ```text
241
- publish --task-id(REQ) --remote
242
- add --repo(REQ) --clone --allow-fork --title --branch --base-branch --base-for
243
- --worktree-name --command --effort --tier{swift|standard|deep|auto} --model
244
- --count --agents <claude:2,codex:1>
245
- --status{backlog|in_progress|in_review|done|canceled|error}(backlog) --pin
246
- --require-report --expect-report --activate(false) --prompt|--prompt-file
247
- spawn --parent --worktree(true) --no-worktree --base-branch --isolate-in --title
248
- --command --prompt|--prompt-file
249
- update --task-id --title --tab --branch --command --model --effort --pinned
250
- --require-report{on|off}
251
- --status{backlog|in_progress|in_review|done|canceled|error} --report-branch
252
- --report-pr --report-summary
253
- land --task-id(REQ) --dry-run --strategy{merge|squash}(merge) --delete-branch
254
- --remove-worktree(true) --repo
255
- sync --task-id(REQ) --repo
256
- delete --task-id --group --cascade --force --delete-branch --delete-remote --wait
257
- archive --task-id(REQ) --force
258
- unarchive --task-id(REQ)
242
+ publish --task-id(REQ) --remote
243
+ pr-attach --url(REQ) --task-id
244
+ add --repo(REQ) --clone --allow-fork --title --branch --base-branch --base-for
245
+ --worktree-name --command --effort --tier{swift|standard|deep|auto} --model
246
+ --count --agents <claude:2,codex:1>
247
+ --status{backlog|in_progress|in_review|done|canceled|error}(backlog) --pin
248
+ --require-report --expect-report --activate(false) --prompt|--prompt-file
249
+ spawn --parent --worktree(true) --no-worktree --base-branch --isolate-in --title
250
+ --command --prompt|--prompt-file
251
+ update --task-id --title --tab --branch --parent --group --command --model
252
+ --effort --pinned --require-report{on|off}
253
+ --status{backlog|in_progress|in_review|done|canceled|error} --report-branch
254
+ --report-pr --report-summary
255
+ land --task-id(REQ) --dry-run --strategy{merge|squash}(merge) --delete-branch
256
+ --remove-worktree(true) --repo
257
+ sync --task-id(REQ) --repo
258
+ delete --task-id --group --cascade --force --delete-branch --delete-remote --wait
259
+ archive --task-id(REQ) --force
260
+ unarchive --task-id(REQ)
261
+ github-login --cancel
262
+ github-logout (none)
263
+ github-sync --to(REQ)
264
+ github-apply --token(REQ)
259
265
  ```
260
266
  <!-- generated:end -->
261
267