@sma1lboy/rove 0.9.110 → 0.9.148

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 (194) hide show
  1. package/README.md +0 -1
  2. package/dist/cli/chunk-003tvywc.js +3 -0
  3. package/dist/cli/chunk-0b48bpkd.js +2 -0
  4. package/dist/cli/chunk-0dv2xssg.js +19 -0
  5. package/dist/cli/chunk-0sm2k04y.js +3 -0
  6. package/dist/cli/chunk-0vmrteyg.js +3 -0
  7. package/dist/cli/chunk-13s8rnk5.js +3 -0
  8. package/dist/cli/chunk-1kktfmtg.js +4 -0
  9. package/dist/cli/chunk-1rkb0j1j.js +3 -0
  10. package/dist/cli/chunk-1tx1s5v6.js +2 -0
  11. package/dist/cli/chunk-1x0vahnn.js +2 -0
  12. package/dist/cli/chunk-20pt9yqh.js +4 -0
  13. package/dist/cli/chunk-2brn8e1y.js +3 -0
  14. package/dist/cli/chunk-2fypr41w.js +3 -0
  15. package/dist/cli/chunk-2jxdzw4r.js +3 -0
  16. package/dist/cli/chunk-2pqqvse3.js +2 -0
  17. package/dist/cli/chunk-39s1ndtm.js +106 -0
  18. package/dist/cli/chunk-3bfm4hrs.js +3 -0
  19. package/dist/cli/chunk-3rqa4pyt.js +28 -0
  20. package/dist/cli/chunk-3ry6prwn.js +3 -0
  21. package/dist/cli/chunk-3ztek08w.js +4 -0
  22. package/dist/cli/chunk-49s2zc0h.js +8 -0
  23. package/dist/cli/chunk-4wwx51m5.js +3 -0
  24. package/dist/cli/chunk-5ar3vspk.js +9 -0
  25. package/dist/cli/chunk-5bn91n19.js +3 -0
  26. package/dist/cli/chunk-5c3d1y0r.js +2 -0
  27. package/dist/cli/chunk-5hhvrvgm.js +8 -0
  28. package/dist/cli/chunk-6dn6h640.js +3 -0
  29. package/dist/cli/chunk-6frfschk.js +2 -0
  30. package/dist/cli/chunk-6xppe9bj.js +3 -0
  31. package/dist/cli/chunk-6zwg6me2.js +4 -0
  32. package/dist/cli/chunk-764bsyb0.js +1 -0
  33. package/dist/cli/chunk-79gqh5p2.js +5 -0
  34. package/dist/cli/chunk-7ngv7v9g.js +9 -0
  35. package/dist/cli/chunk-7nhdvr5v.js +4 -0
  36. package/dist/cli/chunk-7pfnda5z.js +12 -0
  37. package/dist/cli/chunk-7q67dz4c.js +3 -0
  38. package/dist/cli/chunk-8ddjfc2v.js +4 -0
  39. package/dist/cli/chunk-8g456mmt.js +3 -0
  40. package/dist/cli/chunk-8sbevss4.js +4 -0
  41. package/dist/cli/chunk-91j0an0j.js +2 -0
  42. package/dist/cli/chunk-99devyxr.js +2 -0
  43. package/dist/cli/chunk-9c1e5z6a.js +3 -0
  44. package/dist/cli/chunk-a4a4qaa2.js +3 -0
  45. package/dist/cli/chunk-adez6hq4.js +69 -0
  46. package/dist/cli/chunk-aq912vvw.js +3 -0
  47. package/dist/cli/chunk-arf294te.js +2 -0
  48. package/dist/cli/chunk-awsawfrm.js +2 -0
  49. package/dist/cli/chunk-aynx3d2a.js +2 -0
  50. package/dist/cli/chunk-b8d162tf.js +3 -0
  51. package/dist/cli/chunk-b99tf0jr.js +3 -0
  52. package/dist/cli/chunk-bj59a9mg.js +3 -0
  53. package/dist/cli/chunk-c6mthex1.js +5 -0
  54. package/dist/cli/chunk-cb2fpajx.js +3 -0
  55. package/dist/cli/chunk-cbkje3aw.js +2 -0
  56. package/dist/cli/chunk-df7qe9y7.js +4 -0
  57. package/dist/cli/chunk-dkxhmj9k.js +25 -0
  58. package/dist/cli/chunk-dqndd97s.js +7 -0
  59. package/dist/cli/chunk-dvabncrk.js +7 -0
  60. package/dist/cli/chunk-dvcb6mk4.js +3 -0
  61. package/dist/cli/chunk-dw853n4w.js +3 -0
  62. package/dist/cli/chunk-dx6b3dv8.js +7 -0
  63. package/dist/cli/chunk-e1g9mvww.js +11 -0
  64. package/dist/cli/chunk-ef11h2am.js +3 -0
  65. package/dist/cli/chunk-ef39ym8f.js +3 -0
  66. package/dist/cli/chunk-eq5f3z2f.js +3 -0
  67. package/dist/cli/chunk-ewdhz3dv.js +2 -0
  68. package/dist/cli/chunk-f6zsej8e.js +3 -0
  69. package/dist/cli/chunk-g1ybdgad.js +25 -0
  70. package/dist/cli/chunk-gbasmvsc.js +4 -0
  71. package/dist/cli/chunk-gjf604bb.js +3 -0
  72. package/dist/cli/chunk-gm3vj0fv.js +5 -0
  73. package/dist/cli/chunk-grh6dvfv.js +5 -0
  74. package/dist/cli/chunk-gwcneg6c.js +2 -0
  75. package/dist/cli/chunk-h5j8b87f.js +2 -0
  76. package/dist/cli/chunk-h79zfebh.js +7 -0
  77. package/dist/cli/chunk-h82kzdsn.js +3 -0
  78. package/dist/cli/chunk-hape5vwj.js +3 -0
  79. package/dist/cli/chunk-hg5x2wr0.js +3 -0
  80. package/dist/cli/chunk-j4xfsn9g.js +5 -0
  81. package/dist/cli/chunk-jfqbq12b.js +18 -0
  82. package/dist/cli/chunk-jsss3ze7.js +11 -0
  83. package/dist/cli/chunk-jvpzy378.js +3 -0
  84. package/dist/cli/chunk-jvxh93nz.js +3 -0
  85. package/dist/cli/chunk-jyv82gf0.js +258 -0
  86. package/dist/cli/chunk-k3ksts6g.js +3 -0
  87. package/dist/cli/chunk-kdrrw9tm.js +2 -0
  88. package/dist/cli/chunk-kg11c1py.js +3 -0
  89. package/dist/cli/chunk-kwe61yx7.js +3 -0
  90. package/dist/cli/chunk-m0fz6g32.js +16 -0
  91. package/dist/cli/chunk-mb5m7ex7.js +2 -0
  92. package/dist/cli/chunk-me77sx3d.js +3 -0
  93. package/dist/cli/chunk-mfqjdpd8.js +2 -0
  94. package/dist/cli/chunk-mggca3k4.js +4 -0
  95. package/dist/cli/chunk-mjvq39gf.js +4 -0
  96. package/dist/cli/chunk-mrwee8sj.js +3 -0
  97. package/dist/cli/chunk-my20q579.js +3 -0
  98. package/dist/cli/chunk-nceh1q3d.js +12 -0
  99. package/dist/cli/chunk-ncsgawg7.js +2 -0
  100. package/dist/cli/chunk-nm6adny4.js +4 -0
  101. package/dist/cli/chunk-np07kccr.js +4 -0
  102. package/dist/cli/chunk-nrnjt5b6.js +2 -0
  103. package/dist/cli/chunk-p7kztgsd.js +4 -0
  104. package/dist/cli/chunk-pbqwegjy.js +2 -0
  105. package/dist/cli/chunk-pcvk98rr.js +3 -0
  106. package/dist/cli/chunk-pdh5w4ht.js +3 -0
  107. package/dist/cli/chunk-pdj8kqa1.js +74 -0
  108. package/dist/cli/chunk-pw6yfj10.js +3 -0
  109. package/dist/cli/chunk-q6579rbz.js +3 -0
  110. package/dist/cli/chunk-q7dr9eh6.js +3 -0
  111. package/dist/cli/chunk-qkehg2c1.js +3 -0
  112. package/dist/cli/chunk-qtke4x42.js +3 -0
  113. package/dist/cli/chunk-rxvbm4vt.js +7 -0
  114. package/dist/cli/chunk-s66eh0pw.js +8 -0
  115. package/dist/cli/chunk-s76r56rj.js +3 -0
  116. package/dist/cli/chunk-sdyzxayr.js +3 -0
  117. package/dist/cli/chunk-svpn3av1.js +3 -0
  118. package/dist/cli/chunk-t0kta7rb.js +5 -0
  119. package/dist/cli/chunk-v5893dx3.js +2 -0
  120. package/dist/cli/chunk-vapjtyv9.js +7 -0
  121. package/dist/cli/chunk-vpzvcxc1.js +3 -0
  122. package/dist/cli/chunk-vqpa27cf.js +15 -0
  123. package/dist/cli/chunk-vws0j65e.js +4 -0
  124. package/dist/cli/chunk-vyb5a2rh.js +4 -0
  125. package/dist/cli/chunk-vzxbe80f.js +7 -0
  126. package/dist/cli/chunk-w5xy83ze.js +2 -0
  127. package/dist/cli/chunk-wqfp8d9w.js +3 -0
  128. package/dist/cli/chunk-wsvf9eka.js +33 -0
  129. package/dist/cli/chunk-x760j10f.js +3 -0
  130. package/dist/cli/chunk-x930m5fs.js +2 -0
  131. package/dist/cli/chunk-xbc3gcry.js +2 -0
  132. package/dist/cli/chunk-xy8be792.js +3 -0
  133. package/dist/cli/chunk-xzrqnpmh.js +4 -0
  134. package/dist/cli/chunk-y2yq6zca.js +3 -0
  135. package/dist/cli/chunk-yhcz2d2r.js +34 -0
  136. package/dist/cli/chunk-yk2ha5yj.js +3 -0
  137. package/dist/cli/chunk-ykvk2546.js +3 -0
  138. package/dist/cli/chunk-ympakh8w.js +6 -0
  139. package/dist/cli/chunk-yn5a7xcv.js +2 -0
  140. package/dist/cli/chunk-zb56ck5f.js +3 -0
  141. package/dist/cli/index.js +15 -850
  142. package/dist/cli/kobe-run.js +1 -858
  143. package/dist/cli/kobe.js +6 -6
  144. package/dist/cli/pty-host-node.mjs +167 -50
  145. package/dist/cli/rove-run.js +1 -858
  146. package/dist/cli/rove.js +6 -6
  147. package/dist/skills/rove/SKILL.md +29 -10
  148. package/dist/skills/rove/references/api-flags.md +35 -13
  149. package/dist/web-ui/assets/{ChatTerminal-BshL3M8G.js → harness-CYGJ4uoR.js} +19 -20
  150. package/dist/web-ui/assets/index-BTJ-X_4s.js +10 -0
  151. package/dist/web-ui/assets/index-Cs2vPLNB.css +1 -0
  152. package/dist/web-ui/index.html +2 -9
  153. package/dist/web-ui/pty-server.mjs +4 -5
  154. package/package.json +1 -1
  155. package/dist/web-ui/assets/AppShell-BGRIs5hW.js +0 -5
  156. package/dist/web-ui/assets/AppShell-HiyATJse.css +0 -1
  157. package/dist/web-ui/assets/Board-Bt3cwp25.js +0 -1
  158. package/dist/web-ui/assets/BoardPeek-BKR-tpS2.js +0 -2
  159. package/dist/web-ui/assets/ChatTranscript-B67_nnd6.js +0 -1
  160. package/dist/web-ui/assets/ConfirmDialog-kyA624OF.js +0 -1
  161. package/dist/web-ui/assets/IssueIntakePanel-BMP8Vc3E.js +0 -1
  162. package/dist/web-ui/assets/IssuePeek-C7afOYax.js +0 -1
  163. package/dist/web-ui/assets/IssuesPage-DwRaBB-m.js +0 -1
  164. package/dist/web-ui/assets/SettingsPage-CC1sDI8j.js +0 -2
  165. package/dist/web-ui/assets/SlideOver-B4sEOPyN.css +0 -1
  166. package/dist/web-ui/assets/SlideOver-Dq0ifiEm.js +0 -173
  167. package/dist/web-ui/assets/ViewToggle-mNsDLJp8.js +0 -1
  168. package/dist/web-ui/assets/WorkspaceTabs-DLzL-Tad.css +0 -1
  169. package/dist/web-ui/assets/WorkspaceTabs-ifAIZ5Fj.js +0 -3
  170. package/dist/web-ui/assets/active-task-gVYiAkLv.js +0 -1
  171. package/dist/web-ui/assets/arrow-left-CavMh90a.js +0 -1
  172. package/dist/web-ui/assets/board-DoArj55z.js +0 -1
  173. package/dist/web-ui/assets/chips-CS9PLtWy.js +0 -1
  174. package/dist/web-ui/assets/createLucideIcon-CuIQtyYV.js +0 -1
  175. package/dist/web-ui/assets/external-link-Du2dMe8S.js +0 -1
  176. package/dist/web-ui/assets/external-store-BCBJewGi.js +0 -1
  177. package/dist/web-ui/assets/harness-ClZMF1wx.js +0 -1
  178. package/dist/web-ui/assets/index-B4xJZEqf.js +0 -10
  179. package/dist/web-ui/assets/index-BnjD9Q-3.css +0 -1
  180. package/dist/web-ui/assets/issues-DwrKXp9c.js +0 -4
  181. package/dist/web-ui/assets/issues-ISAZBras.js +0 -1
  182. package/dist/web-ui/assets/lazy-issue-panels-DvXQJhKy.js +0 -6
  183. package/dist/web-ui/assets/rotate-cw-BXdpzm3v.js +0 -1
  184. package/dist/web-ui/assets/routes-DEEkL5gG.js +0 -1
  185. package/dist/web-ui/assets/store-BLM6nh6E.js +0 -2
  186. package/dist/web-ui/assets/tabs-CMZJhcSB.js +0 -1
  187. package/dist/web-ui/assets/task._taskId-BCIhg4IQ.js +0 -1
  188. package/dist/web-ui/assets/terminal-DOp1a9g6.js +0 -1
  189. package/dist/web-ui/assets/text-match-DW3sD-Rt.js +0 -1
  190. package/dist/web-ui/assets/time-kviT26Tm.js +0 -1
  191. package/dist/web-ui/assets/toast-C4yXq6hM.js +0 -1
  192. package/dist/web-ui/assets/web-transport-Beqz2mQ8.js +0 -1
  193. package/dist/web-ui/assets/worktrees-BGvJOG2b.js +0 -1
  194. /package/dist/web-ui/assets/{ChatTerminal-BrP-ENHg.css → harness-BrP-ENHg.css} +0 -0
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{existsSync,renameSync,statSync}from"fs";var DEFAULT_LOG_ROTATE_CAP_BYTES=10485760;function shouldRotateLog(sizeBytes,capBytes=DEFAULT_LOG_ROTATE_CAP_BYTES){return sizeBytes>capBytes}function rotateLogIfNeeded(path,capBytes=DEFAULT_LOG_ROTATE_CAP_BYTES){try{if(!existsSync(path))return;let{size}=statSync(path);if(!shouldRotateLog(size,capBytes))return;renameSync(path,`${path}.old`)}catch{}}
3
+ export{DEFAULT_LOG_ROTATE_CAP_BYTES,shouldRotateLog,rotateLogIfNeeded};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{pluginConfigDir,pluginStateDir}from"./chunk-jyv82gf0.js";function buildPluginEnv(opts){let configDir=pluginConfigDir(opts.pluginId,opts.homeDir),stateDir=pluginStateDir(opts.pluginId,opts.homeDir),extra=withPluginEnvAliases(opts.extra??{});return{...process.env,...opts.homeDir?{ROVE_HOME_DIR:opts.homeDir,KOBE_HOME_DIR:opts.homeDir}:{},ROVE_SOCKET_PATH:opts.socketPath,KOBE_SOCKET_PATH:opts.socketPath,ROVE_BIN_PATH:opts.binPath,KOBE_BIN_PATH:opts.binPath,ROVE_PLUGIN_ID:opts.pluginId,KOBE_PLUGIN_ID:opts.pluginId,ROVE_PLUGIN_ROOT:opts.pluginRoot,KOBE_PLUGIN_ROOT:opts.pluginRoot,ROVE_PLUGIN_CONFIG_DIR:configDir,KOBE_PLUGIN_CONFIG_DIR:configDir,ROVE_PLUGIN_STATE_DIR:stateDir,KOBE_PLUGIN_STATE_DIR:stateDir,...extra}}function withPluginEnvAliases(values){let aliases={};for(let[key,value]of Object.entries(values))if(key.startsWith("ROVE_"))aliases[`KOBE_${key.slice(5)}`]=value;else if(key.startsWith("KOBE_"))aliases[`ROVE_${key.slice(5)}`]=value;return{...aliases,...values}}
3
+ export{buildPluginEnv};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{vendorFromArgv}from"./chunk-gbasmvsc.js";import{engineEntry}from"./chunk-yhcz2d2r.js";import{isContribEngine}from"./chunk-df7qe9y7.js";import{roveCliInvocation}from"./chunk-7nhdvr5v.js";import{getCustomEngineIds,getPersistedString}from"./chunk-k3ksts6g.js";import{BUILTIN_VENDORS,coerceVendorId,isBuiltinVendor}from"./chunk-h82kzdsn.js";var VENDOR_LABEL=Object.fromEntries(BUILTIN_VENDORS.map((v)=>[v,engineEntry(v).displayName]));function engineCommandKey(vendor){return`engineCommand.${vendor}`}function engineNameKey(vendor){return`engineName.${vendor}`}function engineDisplayName(vendor){return getPersistedString(engineNameKey(vendor))?.trim()||engineEntry(vendor).displayName}function humanizeSlug(id){return id.split(/[-_]+/).filter((word)=>word.length>0).map((word)=>word.charAt(0).toUpperCase()+word.slice(1)).join(" ")}function defaultEngineCommand(vendor){return engineEntry(coerceVendorId(vendor)).defaultCommand}function parseEngineCommand(command){let out=[],token="",hasToken=!1,quote=null;for(let ch of command){if(quote){if(ch===quote)quote=null;else token+=ch;continue}if(ch==='"'||ch==="'"){quote=ch,hasToken=!0;continue}if(/\s/.test(ch)){if(hasToken)out.push(token),token="",hasToken=!1;continue}token+=ch,hasToken=!0}if(hasToken)out.push(token);return out}function interactiveEngineCommand(vendor,effort){let v=coerceVendorId(vendor),override=getPersistedString(engineCommandKey(v))?.trim(),base=(()=>{if(override){let argv=parseEngineCommand(override);if(argv.length>0)return argv}return defaultEngineCommand(v)})();return withEngineTerminalTitle(withEngineEffort(base,v,effort),v)}function withEngineTerminalTitle(argv,vendor){let args=engineEntry(coerceVendorId(vendor)).terminalTitle?.launchArgs;return args&&args.length>0?[...argv,...args]:argv}function withEngineEffort(argv,vendor,effort){let trimmed=effort?.trim();if(!trimmed)return argv;let entry=engineEntry(coerceVendorId(vendor));if(!entry.effortLevels?.includes(trimmed))return argv;return entry.effortArgv?.(argv,trimmed)??argv}function kobeApiInvocation(){let quote=(a)=>/^[A-Za-z0-9_/.:=-]+$/.test(a)?a:`'${a.replace(/'/g,"'\\''")}'`;try{return[...roveCliInvocation(),"api"].map(quote).join(" ")}catch{return"rove api"}}import{randomUUID}from"crypto";function hasFlag(argv,flag){return argv.some((a)=>a===flag||a.startsWith(`${flag}=`))}function controlsOwnSession(identity,argv){return(identity?.sessionControlFlags??[]).some((flag)=>hasFlag(argv,flag))}function pinSessionArgv(identity,argv,sessionId){if(!identity?.pinFlag)return argv;if(controlsOwnSession(identity,argv))return argv;return[...argv,identity.pinFlag,sessionId]}function acceptsPinnedSession(identity,argv){return!!identity?.pinFlag&&!controlsOwnSession(identity,argv)}function resumeSessionArgv(identity,base,sessionId){if(!sessionId||!identity?.resumeArgv)return null;if(controlsOwnSession(identity,base))return null;return identity.resumeArgv(base,sessionId)}function pickUnclaimedSessionId(ids,claimed){for(let i=ids.length-1;i>=0;i--){let id=ids[i];if(id&&!claimed.has(id))return id}return null}function forkSessionArgvFor(identity,base,sourceId,newId){if(!sourceId||!identity?.forkArgv)return null;if(controlsOwnSession(identity,base))return null;return identity.forkArgv(base,sourceId,newId)}function acceptsSessionFork(identity){return!!identity?.forkArgv}var GENERIC_PROTOCOL="generic",ENGINE_PROTOCOLS=BUILTIN_VENDORS;function engineProtocolKey(id){return`engineProtocol.${id}`}function getEngineProtocol(id){if(isBuiltinVendor(id)||isContribEngine(id))return id;let raw=getPersistedString(engineProtocolKey(id))?.trim();return raw&&ENGINE_PROTOCOLS.includes(raw)?raw:void 0}function isPresetId(id){return isBuiltinVendor(id)||isContribEngine(id)||getCustomEngineIds().includes(id)}function listPresetIds(){return[...BUILTIN_VENDORS,...getCustomEngineIds()]}function describePreset(id){let command=getPersistedString(engineCommandKey(id))?.trim()||defaultEngineCommand(id).join(" ");return{id,name:engineDisplayName(id),command,protocol:getEngineProtocol(id)??GENERIC_PROTOCOL,builtin:isBuiltinVendor(id)}}function listEnginePresets(){return listPresetIds().map(describePreset)}function resolveCommandProtocol(command){let trimmed=command?.trim();if(!trimmed)return GENERIC_PROTOCOL;if(isPresetId(trimmed))return getEngineProtocol(trimmed)??GENERIC_PROTOCOL;let fromArgv=vendorFromArgv(trimmed);if(fromArgv)return fromArgv;let argv0=parseEngineCommand(trimmed)[0];if(argv0)for(let id of getCustomEngineIds()){let preset=getPersistedString(engineCommandKey(id))?.trim();if(preset&&parseEngineCommand(preset)[0]===argv0)return getEngineProtocol(id)??GENERIC_PROTOCOL}return GENERIC_PROTOCOL}function engineLaunchArgv(spec){let command=spec.command?.trim();if(!command)return interactiveEngineCommand(spec.vendor,spec.effort);let vendor=(isPresetId(command)?getEngineProtocol(command):void 0)??resolveCommandProtocol(command),base=isPresetId(command)?presetBaseArgv(command):(()=>{let argv=parseEngineCommand(command);return argv.length>0?argv:null})();if(!base)return interactiveEngineCommand(spec.vendor,spec.effort);return withEngineTerminalTitle(withEngineEffort(base,vendor,spec.effort),vendor)}function presetBaseArgv(id){let override=getPersistedString(engineCommandKey(id))?.trim();if(override){let argv=parseEngineCommand(override);if(argv.length>0)return argv}return defaultEngineCommand(id)}function sessionProtocol(vendor){let id=vendor?.trim();if(!id)return"claude";return getEngineProtocol(id)??id}function protocolEntry(vendor){return engineEntry(sessionProtocol(vendor))}function withPinnedSessionId(argv,vendor,newId=randomUUID){let identity=engineEntry(sessionProtocol(vendor)).sessionIdentity;if(!acceptsPinnedSession(identity,argv))return{argv,sessionId:null};let sessionId=newId();return{argv:pinSessionArgv(identity,argv,sessionId),sessionId}}function engineResumeArgv(base,vendor,sessionId){return resumeSessionArgv(engineEntry(sessionProtocol(vendor)).sessionIdentity,base,sessionId)}function engineCanFork(vendor){return acceptsSessionFork(engineEntry(sessionProtocol(vendor)).sessionIdentity)}function engineForkArgv(base,vendor,sourceId,newId){return forkSessionArgvFor(engineEntry(sessionProtocol(vendor)).sessionIdentity,base,sourceId,newId)}
3
+ export{engineCommandKey,engineNameKey,engineDisplayName,humanizeSlug,defaultEngineCommand,interactiveEngineCommand,kobeApiInvocation,pickUnclaimedSessionId,GENERIC_PROTOCOL,ENGINE_PROTOCOLS,engineProtocolKey,getEngineProtocol,listPresetIds,describePreset,listEnginePresets,resolveCommandProtocol,engineLaunchArgv,sessionProtocol,protocolEntry,withPinnedSessionId,engineResumeArgv,engineCanFork,engineForkArgv};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ var lastTime=-1,lastRand=Array(16).fill(0);function encodeTime(now,len){let out="",n=now;for(let i=len-1;i>=0;i--){let mod=n%32;out="0123456789ABCDEFGHJKMNPQRSTVWXYZ"[mod]+out,n=(n-mod)/32}return out}function randomIndices(len){let buf=new Uint8Array(len);crypto.getRandomValues(buf);let out=Array(len);for(let i=0;i<len;i++)out[i]=(buf[i]??0)&31;return out}function incrementIndices(indices){for(let i=indices.length-1;i>=0;i--){let v=indices[i]??0;if(v<31)return indices[i]=v+1,!0;indices[i]=0}return!1}function indicesToString(indices){let out="";for(let idx of indices)out+="0123456789ABCDEFGHJKMNPQRSTVWXYZ"[idx]??"0";return out}function ulid(now=Date.now()){let randIndices,time;if(now>lastTime)time=now,randIndices=randomIndices(16);else{time=lastTime;let next=lastRand.slice();if(!incrementIndices(next))randIndices=randomIndices(16);else randIndices=next}return lastTime=time,lastRand=randIndices,encodeTime(time,10)+indicesToString(randIndices)}
3
+ export{ulid};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{toTaskId}from"./chunk-xy8be792.js";import{coerceVendorId}from"./chunk-h82kzdsn.js";import{DAEMON_MIGRATION_MARKER}from"./chunk-8ddjfc2v.js";import{logClient}from"./chunk-grh6dvfv.js";import{defaultClientLogPath}from"./chunk-x760j10f.js";import{randomBytes}from"crypto";import{linkSync,mkdirSync,readFileSync,unlinkSync,writeFileSync}from"fs";import{link,mkdir,readFile,unlink,writeFile}from"fs/promises";import{dirname}from"path";function isProcessAlive(pid){if(!Number.isInteger(pid)||pid<=0)return!1;try{return process.kill(pid,0),!0}catch(err){if(err.code==="ESRCH")return!1;return!0}}class LockfileError extends Error{heldByPid;constructor(message,heldByPid){super(message);this.name="LockfileError",this.heldByPid=heldByPid}}async function acquire(lockPath,opts={}){await mkdir(dirname(lockPath),{recursive:!0});let token=`${process.pid}:${randomBytes(8).toString("hex")}`,sidecar=`${lockPath}.${token.replace(":","-")}`;for(;;){await writeFile(sidecar,token);try{return await link(sidecar,lockPath),token}catch(err){if(err.code!=="EEXIST")throw err}finally{await unlink(sidecar).catch(()=>{})}let holder;try{holder=(await readFile(lockPath,"utf8")).trim()}catch(err){if(err.code!=="ENOENT")throw err;continue}let holderPid=Number.parseInt(holder,10),alive=Number.isFinite(holderPid)&&holderPid>0&&isProcessAlive(holderPid);if(alive&&!opts.forceTakeover)throw new LockfileError(`task index is locked by another Rove instance (pid ${holderPid})`,holderPid);console.warn(`[rove] removing stale lockfile at ${lockPath} (was held by pid ${holderPid}${alive?", forced":", process gone"})`),releaseSync(lockPath,holder)}}async function release(lockPath,token){let current;try{current=(await readFile(lockPath,"utf8")).trim()}catch(err){if(err.code==="ENOENT")return;throw err}if(current!==token)return;try{await unlink(lockPath)}catch(err){if(err.code==="ENOENT")return;throw err}}function sleepSync(ms){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,ms)}function acquireSync(lockPath,timeoutMs=5000){mkdirSync(dirname(lockPath),{recursive:!0});let token=`${process.pid}:${randomBytes(8).toString("hex")}`,sidecar=`${lockPath}.${token.replace(":","-")}`,deadline=Date.now()+timeoutMs;for(;;){writeFileSync(sidecar,token);try{return linkSync(sidecar,lockPath),token}catch(err){if(err.code!=="EEXIST")throw err}finally{try{unlinkSync(sidecar)}catch{}}let holder;try{holder=readFileSync(lockPath,"utf8").trim()}catch(err){if(err.code!=="ENOENT")throw err;continue}let holderPid=Number.parseInt(holder,10);if(Number.isFinite(holderPid)&&holderPid>0&&isProcessAlive(holderPid)){if(Date.now()>=deadline)throw new LockfileError(`${lockPath} is locked by another Rove instance (pid ${holderPid})`,holderPid);sleepSync(25);continue}console.warn(`[rove] removing stale lockfile at ${lockPath} (was held by pid ${holderPid}, process gone)`),releaseSync(lockPath,holder)}}function releaseSync(lockPath,token){try{if(readFileSync(lockPath,"utf8").trim()!==token)return;unlinkSync(lockPath)}catch(err){if(err.code!=="ENOENT")throw err}}import{existsSync}from"fs";import{copyFile,readFile as readFile2}from"fs/promises";import{dirname as dirname2,join}from"path";function coerceTask(value){if(!value||typeof value!=="object")return null;let v=value;if(typeof v.id!=="string"||typeof v.title!=="string"||typeof v.repo!=="string"||typeof v.branch!=="string"||typeof v.worktreePath!=="string"||typeof v.status!=="string"||typeof v.createdAt!=="string"||typeof v.updatedAt!=="string")return null;if(!isTaskStatus(v.status))return null;let kind=v.kind==="main"?"main":v.kind==="dir"?"dir":"task",scratch=kind==="dir"&&v.scratch===!0,healedStatus=kind==="main"&&(v.status==="in_progress"||v.status==="done")?"backlog":v.status,deletion=coerceDeletion(v.deletion),quotaResume=coerceQuotaResume(v.quotaResume),linkedWorkItem=coerceLinkedWorkItem(v.linkedWorkItem),dispatcher=coerceDispatcher(v.dispatcher),routine=coerceRoutine(v.routine);return{id:toTaskId(v.id),title:v.title,repo:v.repo,branch:v.branch,worktreePath:v.worktreePath,status:healedStatus,pinned:typeof v.pinned==="boolean"?v.pinned:!1,kind,...scratch?{scratch:!0}:{},...routine?{routine}:{},vendor:coerceVendorId(typeof v.vendor==="string"?v.vendor:void 0),...typeof v.command==="string"&&v.command.trim().length>0?{command:v.command}:{},prStatus:coercePRStatus(v.prStatus),...typeof v.modelEffort==="string"&&v.modelEffort.length>0?{modelEffort:v.modelEffort}:{},...typeof v.groupId==="string"&&v.groupId.length>0?{groupId:v.groupId}:{},...v.observedLanguage==="zh"||v.observedLanguage==="en"?{observedLanguage:v.observedLanguage}:{},...deletion?{deletion}:{},...quotaResume?{quotaResume}:{},...linkedWorkItem?{linkedWorkItem}:{},...dispatcher?{dispatcher}:{},...typeof v.prompt==="string"&&v.prompt.length>0?{prompt:v.prompt}:{},...typeof v.baseRef==="string"&&v.baseRef.trim().length>0?{baseRef:v.baseRef}:{},createdAt:v.createdAt,updatedAt:v.updatedAt}}function coerceDispatcher(value){if(!value||typeof value!=="object"||Array.isArray(value))return;let v=value;if(typeof v.taskId!=="string"||v.taskId.length===0)return;if(typeof v.tabId!=="string"||v.tabId.length===0)return;return{taskId:v.taskId,tabId:v.tabId}}function coerceRoutine(value){if(!value||typeof value!=="object"||Array.isArray(value))return;let v=value;if(typeof v.automationId!=="string"||v.automationId.length===0)return;return{automationId:v.automationId}}function coerceQuotaResume(value){if(!value||typeof value!=="object"||Array.isArray(value))return;let v=value;if(typeof v.resumeAt!=="string"||v.resumeAt.length===0)return;if(typeof v.requestedAt!=="string"||v.requestedAt.length===0)return;return{resumeAt:v.resumeAt,requestedAt:v.requestedAt}}function coerceLinkedWorkItem(value){if(!value||typeof value!=="object"||Array.isArray(value))return;let v=value;if(v.provider!=="github")return;if(v.type!=="issue"&&v.type!=="pr")return;if(typeof v.number!=="number"||!Number.isFinite(v.number))return;if(typeof v.title!=="string"||typeof v.url!=="string"||v.url.length===0)return;return{provider:v.provider,type:v.type,number:v.number,title:v.title,url:v.url}}function coerceDeletion(value){if(!value||typeof value!=="object"||Array.isArray(value))return;let v=value;if(v.phase!=="queued"&&v.phase!=="running"&&v.phase!=="error"||typeof v.force!=="boolean"||typeof v.requestedAt!=="string"||v.requestedAt.length===0||v.error!==void 0&&typeof v.error!=="string")return;return{phase:v.phase,force:v.force,...typeof v.deleteBranch==="boolean"?{deleteBranch:v.deleteBranch}:{},requestedAt:v.requestedAt,...typeof v.error==="string"?{error:v.error}:{}}}function coercePRStatus(value){if(!value||typeof value!=="object"||Array.isArray(value))return;let v=value;if(!isPRProviderId(v.provider)||!isPRLifecycleState(v.lifecycle)||!isPRCheckState(v.checkState))return;return{provider:v.provider,lifecycle:v.lifecycle,checkState:v.checkState,...typeof v.number==="number"&&Number.isFinite(v.number)?{number:v.number}:{},...typeof v.url==="string"?{url:v.url}:{},...typeof v.title==="string"?{title:v.title}:{},...typeof v.baseRef==="string"?{baseRef:v.baseRef}:{},...typeof v.headRef==="string"?{headRef:v.headRef}:{},...typeof v.reviewDecision==="string"?{reviewDecision:v.reviewDecision}:{},...typeof v.mergeable==="string"?{mergeable:v.mergeable}:{},...typeof v.lastCheckedAt==="string"?{lastCheckedAt:v.lastCheckedAt}:{},...typeof v.lastError==="string"?{lastError:v.lastError}:{}}}function isPRProviderId(v){return v==="github"||v==="gitlab"||v==="bitbucket"||v==="unknown"}function isPRLifecycleState(v){return v==="creating"||v==="open"||v==="ready_to_merge"||v==="merged"||v==="closed"||v==="unknown"}function isPRCheckState(v){return v==="none"||v==="pending"||v==="passing"||v==="failing"||v==="unknown"}function isTaskStatus(s){return s==="backlog"||s==="in_progress"||s==="in_review"||s==="done"||s==="canceled"||s==="error"}var sleep=(ms)=>new Promise((resolve)=>setTimeout(resolve,ms)),LOCK_RETRY_DELAY_MS=25,LOCK_MAX_WAIT_MS=5000;async function acquireWithRetry(lockPath){let deadline=Date.now()+LOCK_MAX_WAIT_MS;for(;;)try{return await acquire(lockPath)}catch(err){if(!(err instanceof LockfileError)||Date.now()>=deadline)throw err;await sleep(LOCK_RETRY_DELAY_MS)}}var CURRENT_VERSION=3;async function backupCorruptManifest(path,now=()=>new Date){let backupPath=`${path}.corrupt-${now().toISOString().replaceAll(":","-")}`;try{return await copyFile(path,backupPath),backupPath}catch{return null}}var SUPPORTED_VERSIONS=new Set([1,2,3]);function warnManifestRecovery(message,manifestPath){console.warn(message),logClient("tasks-index",message,manifestPath?defaultClientLogPath(dirname2(dirname2(manifestPath))):void 0)}async function recoverUnsupportedVersion(parsed,sourcePath){if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))return!1;let version=parsed.version;if(version===void 0||SUPPORTED_VERSIONS.has(version))return!1;let backup=await backupCorruptManifest(sourcePath);return warnManifestRecovery(`[rove] tasks.json at ${sourcePath} has unsupported version=${String(version)}; recovering with empty index.${backup?` Original bytes backed up to ${backup}.`:" Backup copy failed; the stale file is left in place."}`,sourcePath),!0}function normalizeIndex(parsed,source){if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))return warnManifestRecovery(`[rove] tasks.json at ${source} is not an object; recovering with empty index.`,source),{version:CURRENT_VERSION,tasks:[]};let obj=parsed,version=obj.version;if(version!==void 0&&!SUPPORTED_VERSIONS.has(version))return warnManifestRecovery(`[rove] tasks.json at ${source} has unsupported version=${String(version)}; recovering with empty index.`,source),{version:CURRENT_VERSION,tasks:[]};let rawTasks=Array.isArray(obj.tasks)?obj.tasks:[],tasks=[];for(let entry of rawTasks){let task=coerceTask(entry);if(task)tasks.push(task);else console.warn(`[rove] dropping malformed task entry from ${source}: ${JSON.stringify(entry)}`)}return{version:CURRENT_VERSION,tasks}}var TOMBSTONE_TTL_MS=2592000000;function readableLegacyIndexPath(canonicalPath,legacyPath){return existsSync(join(dirname2(canonicalPath),DAEMON_MIGRATION_MARKER))?void 0:legacyPath}async function readDiskIndex(path,legacyPath){let raw,sourcePath=path;try{raw=await readFile2(sourcePath,"utf8")}catch(err){if(err.code!=="ENOENT")throw err;let legacy=readableLegacyIndexPath(path,legacyPath);if(!legacy)return{tasks:[],removed:[]};sourcePath=legacy;try{raw=await readFile2(sourcePath,"utf8")}catch(legacyErr){if(legacyErr.code==="ENOENT")return{tasks:[],removed:[]};throw legacyErr}}let parsed;try{parsed=JSON.parse(raw)}catch{return await backupCorruptManifest(sourcePath),{tasks:[],removed:[]}}if(await recoverUnsupportedVersion(parsed,sourcePath))return{tasks:[],removed:[]};return{tasks:normalizeIndex(parsed,sourcePath).tasks,removed:coerceTombstones(parsed)}}function coerceTombstones(parsed){if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))return[];let raw=parsed.removed;if(!Array.isArray(raw))return[];let out=[];for(let entry of raw){if(!entry||typeof entry!=="object")continue;let v=entry;if(typeof v.id==="string"&&v.id.length>0&&typeof v.at==="string")out.push({id:v.id,at:v.at})}return out}function mergeTasksWithDisk(cacheTasks,diskTasks,dirty,removed,diskRemoved=[],now=Date.now()){let tombstones=new Map;for(let t of diskRemoved){let at=Date.parse(t.at);if(!Number.isFinite(at)||now-at>TOMBSTONE_TTL_MS)continue;tombstones.set(t.id,t.at)}for(let[id,at]of removed)tombstones.set(id,at);let diskById=new Map(diskTasks.map((t)=>[t.id,t])),result=[],included=new Set;for(let task of cacheTasks){if(tombstones.has(task.id))continue;if(dirty.has(task.id))result.push(task);else{let onDisk=diskById.get(task.id);if(onDisk===void 0)continue;result.push(onDisk)}included.add(task.id)}for(let task of diskTasks){if(included.has(task.id)||tombstones.has(task.id))continue;result.push(task),included.add(task.id)}return{tasks:result,removed:[...tombstones].map(([id,at])=>({id,at}))}}async function recoverIndexFromDisk(path,legacyPath){let raw,sourcePath=path;try{raw=await readFile2(path,"utf8")}catch(err){if(err.code!=="ENOENT")throw err;let legacy=readableLegacyIndexPath(path,legacyPath),legacyRaw;if(legacy)try{legacyRaw=await readFile2(legacy,"utf8"),sourcePath=legacy}catch(legacyErr){if(legacyErr.code!=="ENOENT")throw legacyErr}if(legacyRaw===void 0)return{version:CURRENT_VERSION,tasks:[]};raw=legacyRaw}let parsed;try{parsed=JSON.parse(raw)}catch(err){let backup=await backupCorruptManifest(sourcePath);return warnManifestRecovery(`[rove] tasks.json at ${sourcePath} is corrupted (${err.message}); recovering with empty index.${backup?` Original bytes backed up to ${backup}.`:" Backup copy failed; the stale file is left in place."}`,sourcePath),{version:CURRENT_VERSION,tasks:[]}}if(await recoverUnsupportedVersion(parsed,sourcePath))return{version:CURRENT_VERSION,tasks:[]};return normalizeIndex(parsed,sourcePath)}
3
+ export{isProcessAlive,release,acquireSync,releaseSync,acquireWithRetry,readableLegacyIndexPath,readDiskIndex,mergeTasksWithDisk,recoverIndexFromDisk};
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ var CURSOR_DOWN_RE=/\x1b\[(\d*)[BE]/g,CURSOR_POSITION_RE=/\x1b\[[\d;]*[Hf]/g;var ANSI_RE=/\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?|\x1b[@-_]/g;function breakRowsOnCursorMotion(raw){return raw.replace(CURSOR_DOWN_RE,(_m,count)=>`
3
+ `.repeat(Math.min(200,Math.max(1,Number.parseInt(count,10)||1)))).replace(CURSOR_POSITION_RE,`
4
+ `)}function terminalRows(raw,maxLineChars){return breakRowsOnCursorMotion(raw).replace(ANSI_RE,"").replace(/\r\n/g,`
5
+ `).split(`
6
+ `).map((line)=>{let overwritten=line.split("\r").pop()??"";return maxLineChars===void 0?overwritten:overwritten.slice(0,maxLineChars)})}
7
+ export{terminalRows};
@@ -0,0 +1,8 @@
1
+ // @bun
2
+ import{checkOnboardingEnv,noEngineAction}from"./chunk-bj59a9mg.js";import{LAST_RUN_VERSION_KEY}from"./chunk-mggca3k4.js";import{isNpxMissing,markSkillHintSeen,npxSkillsArgv,npxSkillsCommand}from"./chunk-g1ybdgad.js";import{t}from"./chunk-b8d162tf.js";import{getPersistedBool,loadStateFile,setPersistedBool}from"./chunk-gjf604bb.js";import{activeCliName}from"./chunk-ef11h2am.js";import{__require}from"./chunk-wqfp8d9w.js";import{spawnSync}from"child_process";import{appendFileSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"fs";import{homedir}from"os";import{basename,join}from"path";var ONBOARDED_KEY="onboarded",PRIMER_KEY="onboardedPrimer";function detectShell(env=process.env){let shell=basename(env.SHELL??"");return shell==="zsh"||shell==="bash"||shell==="fish"?shell:null}function installCompletions(shell,home=homedir(),cli=activeCliName()){let rcMarker=`${cli} completions`;if(shell==="fish"){let dir=join(home,".config","fish","completions"),path=join(dir,`${cli}.fish`);return mkdirSync(dir,{recursive:!0}),writeFileSync(path,`${cli} completions fish | source
3
+ `),path}let rc=join(home,shell==="zsh"?".zshrc":".bashrc");if(!(existsSync(rc)?readFileSync(rc,"utf8"):"").includes(rcMarker)){let line=`
4
+ # ${cli} completions
5
+ command -v ${cli} >/dev/null && source <(${cli} completions ${shell})
6
+ `;appendFileSync(rc,line)}return rc}function isOnboarded(){return getPersistedBool(ONBOARDED_KEY,!1)}function markOnboarded(){setPersistedBool(ONBOARDED_KEY,!0)}function isPrimerDone(){return getPersistedBool(PRIMER_KEY,!1)}function markPrimerDone(){setPersistedBool(PRIMER_KEY,!0)}function backfillPrimerForExistingUsers(){if(typeof loadStateFile()[LAST_RUN_VERSION_KEY]!=="string")return!1;return markPrimerDone(),!0}function envReadyForTasks(env){return env.engines.anyUsable&&env.git.found}function applyOnboardingChoices(choices,shell,env){let cli=activeCliName(),completionsHelp=`${cli} completions --help`,skillInstall=`${cli} skill install`,out=(line)=>process.stdout.write(`${line}
7
+ `);if(shell!==null)if(choices.completions)out(t("onboarding.appliedCompletions",{path:installCompletions(shell)}));else out(t("onboarding.skippedCompletions",{command:completionsHelp}));if(choices.skill){if(isNpxMissing())out(t("onboarding.skillNeedsNode",{command:skillInstall}));else if(out(t("onboarding.installingSkill",{command:npxSkillsCommand()})),spawnSync("npx",npxSkillsArgv(),{stdio:"inherit"}).status!==0)out(t("onboarding.skillFailed",{command:skillInstall}))}else out(t("onboarding.skippedSkill",{command:skillInstall})),markSkillHintSeen();out(""),out(env.git.line);for(let line of env.engines.lines)out(line);if(out(""),envReadyForTasks(env))out(t("onboarding.ready")),out(t("onboarding.readyHint",{command:cli}));else{if(out(t("onboarding.notReadyHeader")),!env.engines.anyUsable)out(` \u2192 ${noEngineAction(env.engines.signedOut)}`);if(!env.git.found)out(` \u2192 ${t("doctor.fix.gitAction")}`)}}async function maybeRunOnboarding(){if(!process.stdout.isTTY||!process.stdin.isTTY)return!1;let seen=isOnboarded();if(seen&&(isPrimerDone()||backfillPrimerForExistingUsers()))return!1;markOnboarded();let shell=detectShell(),env=await checkOnboardingEnv(),{runOnboardingWizard}=await import("./chunk-cbkje3aw.js"),choices=await runOnboardingWizard(shell,env,seen?"primer":"full");return markPrimerDone(),applyOnboardingChoices(choices,shell,env),!0}
8
+ export{detectShell,installCompletions,envReadyForTasks,applyOnboardingChoices,maybeRunOnboarding};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ var TOP_LEVEL_SUBCOMMANDS=["completions","add","remove","adopt","export","repo","api","daemon","doctor","config","reset","theme","skill","plugin","feedback","update"],SUBCOMMAND_VERBS={daemon:["status","start","stop","restart"],plugin:["install","link","list","search","outdated","update","enable","disable","unlink","uninstall","config-dir","log","action","pane"],repo:["show","set","unset"],skill:["install","status","command","print"],theme:["list","add","remove"]};
3
+ export{TOP_LEVEL_SUBCOMMANDS,SUBCOMMAND_VERBS};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ var recentStateChanges=[];function snapshotShape(value){if(value===null)return"null";if(Array.isArray(value))return`Array(${value.length})`;if(value instanceof Map)return`Map(${value.size})`;if(value instanceof Set)return`Set(${value.size})`;if(typeof value==="string")return`string(${value.length})`;if(typeof value==="number"||typeof value==="boolean"||typeof value>"u")return String(value);if(typeof value==="object")return`Object(${Object.keys(value).length})`;return typeof value}function recordStateChange(label,before,after){if(recentStateChanges.push(`${new Date().toISOString()} ${label}: ${snapshotShape(before)} -> ${snapshotShape(after)}`),recentStateChanges.length>64)recentStateChanges.splice(0,recentStateChanges.length-64)}function recentStateChangesForDiagnostics(){return recentStateChanges}function createStateCell(initial,debugLabel){let snapshot=initial,listeners=new Set,state=()=>snapshot;return state.get=state,state.set=(next)=>{if(Object.is(next,snapshot))return;if(debugLabel)recordStateChange(debugLabel,snapshot,next);snapshot=next;for(let listener of[...listeners])listener()},state.update=(fn)=>state.set(fn(snapshot)),state.subscribe=(listener)=>{return listeners.add(listener),()=>{listeners.delete(listener)}},state}function mapReadableState(source,map){let get=()=>map(source.get()),derived=get;return derived.get=get,derived.subscribe=source.subscribe,derived}
3
+ export{recentStateChangesForDiagnostics,createStateCell,mapReadableState};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{activeCliName}from"./chunk-ef11h2am.js";import{constants,accessSync,statSync}from"fs";import{isAbsolute}from"path";function isRunnableFile(path){try{if(!statSync(path).isFile())return!1;return accessSync(path,constants.X_OK),!0}catch{return!1}}function resolvePluginBinPath(argv=process.argv,moduleUrl=import.meta.url){if(moduleUrl.includes("/$bunfs/")||moduleUrl.includes("B:\\~BUN"))return process.execPath;let entry=argv[1];if(entry&&isAbsolute(entry)&&isRunnableFile(entry))return entry;return activeCliName()}
3
+ export{resolvePluginBinPath};
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import{embeddedTerminalEnv}from"./chunk-ef39ym8f.js";import{ClientWriter,installDaemonCrashHandlers,linkLegacyRuntimePath,logDaemonError,objectPayload,requireString}from"./chunk-ympakh8w.js";import{DEFAULT_TERMINAL_COLORS,foldDefaultColorQueries,formatDefaultColorReply,parseTerminalDefaultColors}from"./chunk-7q67dz4c.js";import{resolveLoginShell}from"./chunk-b99tf0jr.js";import{ensureOwnerOnlyStateDir,listenOnUnixSocket}from"./chunk-4wwx51m5.js";import{DAEMON_PROTOCOL_VERSION,frameToLine}from"./chunk-3ztek08w.js";import"./chunk-kwe61yx7.js";import{rotateLogIfNeeded}from"./chunk-pw6yfj10.js";import{recordPtyExit}from"./chunk-yk2ha5yj.js";import"./chunk-rxvbm4vt.js";import{defaultPtyFreezeDir,defaultPtyHostLogPath,defaultPtyHostPidPath,defaultPtyHostSocketPath,isWindowsPipePath,legacyPtyHostPidPath,legacyPtyHostSocketPath,resolveDaemonHomeDir}from"./chunk-x760j10f.js";import{COMPAT_STATE_DIR_BASENAME,ROVE_STATE_DIR_BASENAME,readRoveHomeDirEnv}from"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";import{readFileSync as readFileSync2}from"fs";import{mkdir,unlink,writeFile}from"fs/promises";import{createServer}from"net";import{dirname}from"path";import{StringDecoder as StringDecoder3}from"string_decoder";import{existsSync,lstatSync,mkdirSync,renameSync,symlinkSync}from"fs";import{homedir}from"os";import{join}from"path";var PTY_HOST_DATA_ENTRIES=["pty-exits.json","pty-sessions"];function lstatIfExists(path){try{return lstatSync(path)}catch{return}}function migrateLegacyPtyHostData(homeDir=readRoveHomeDirEnv()??homedir()){let canonicalDir=join(homeDir,ROVE_STATE_DIR_BASENAME),legacyDir=join(homeDir,COMPAT_STATE_DIR_BASENAME);if(canonicalDir===legacyDir)return[];let moved=[];for(let name of PTY_HOST_DATA_ENTRIES){let canonical=join(canonicalDir,name),legacy=join(legacyDir,name);try{if(existsSync(canonical))continue;let stat=lstatIfExists(legacy);if(!stat||stat.isSymbolicLink())continue;mkdirSync(canonicalDir,{recursive:!0}),renameSync(legacy,canonical),moved.push(name);try{symlinkSync(canonical,legacy)}catch{}}catch{}}return moved}import{chmodSync,mkdirSync as mkdirSync2,readFileSync,readdirSync,renameSync as renameSync2,rmSync,statSync,writeFileSync}from"fs";import{join as join2}from"path";import{StringDecoder}from"string_decoder";var FREEZE_VERSION=1;function freezeSession(session,now=new Date){return{v:FREEZE_VERSION,key:session.key,cwd:session.cwd,command:[...session.command],cols:session.cols,rows:session.rows,title:session.title,totalBytes:session.totalBytes,exit:session.exit,...session.lastHumanWriteMs?{lastHumanWriteMs:session.lastHumanWriteMs}:{},ringB64:Buffer.concat(session.chunks).toString("base64"),updatedAt:now.toISOString()}}function thawRing(record,cap){let ring;try{ring=Buffer.from(record.ringB64,"base64")}catch{return null}let trimmed=ring.byteLength>cap?ring.subarray(ring.byteLength-cap):ring;return{chunks:trimmed.byteLength>0?[trimmed]:[],bytes:trimmed.byteLength}}function thawSession(record,cap){let ring=thawRing(record,cap);if(!ring)return null;return{key:record.key,cwd:record.cwd,proc:null,alive:!1,chunks:[...ring.chunks],bytes:ring.bytes,totalBytes:Math.max(record.totalBytes,ring.bytes),cols:record.cols,rows:record.rows,command:record.command,title:record.title,titleCarry:"",titleDecoder:new StringDecoder("utf8"),colorQueryCarry:"",defaultColors:DEFAULT_TERMINAL_COLORS,sinks:new Map,parked:!1,parkedScreenBytes:0,exit:record.exit,restored:!0,lastFreezeAtMs:0,lastHumanWriteMs:record.lastHumanWriteMs??0}}function recordFile(dir,key){return join2(dir,`${encodeURIComponent(key)}.json`)}function parseRecord(raw){try{let parsed=JSON.parse(raw);if(parsed?.v!==FREEZE_VERSION)return null;if(typeof parsed.key!=="string"||parsed.key.length===0)return null;if(parsed.key.startsWith("::"))return null;if(typeof parsed.cwd!=="string"||!Array.isArray(parsed.command))return null;if(typeof parsed.ringB64!=="string"||typeof parsed.totalBytes!=="number")return null;return parsed}catch{return null}}var FREEZE_TTL_MS=1209600000,FREEZE_RESTORE_MAX_BYTES=67108864;function updatedAtMs(record){let t=Date.parse(record.updatedAt);return Number.isFinite(t)?t:0}function loadFrozenSessions(dir=defaultPtyFreezeDir(),now=Date.now(),report){let names;try{names=readdirSync(dir)}catch{return[]}let entries=[];for(let name of names){if(!name.endsWith(".json"))continue;try{let st=statSync(join2(dir,name));entries.push({name,size:st.size,mtimeMs:st.mtimeMs})}catch{}}entries.sort((a,b)=>b.mtimeMs-a.mtimeMs);let kept=[],stale=[],bytesRead=0,deferred=0,unreadable=0;for(let entry of entries){if(now-entry.mtimeMs>FREEZE_TTL_MS){stale.push(entry.name);continue}if(bytesRead+entry.size>FREEZE_RESTORE_MAX_BYTES&&kept.length>0){deferred++;continue}let record=null;try{record=parseRecord(readFileSync(join2(dir,entry.name),"utf8"))}catch{}if(bytesRead+=entry.size,!record){unreadable++;continue}if(now-updatedAtMs(record)>FREEZE_TTL_MS)stale.push(entry.name);else kept.push(record)}for(let name of stale)try{rmSync(join2(dir,name),{force:!0})}catch{}return kept.sort((a,b)=>updatedAtMs(b)-updatedAtMs(a)),report?.({restored:kept.length,bytesRead,deferred,expired:stale.length,unreadable}),kept}var DIR_MODE=448,FILE_MODE=384;function tightenExistingPermissions(dir){try{chmodSync(dir,DIR_MODE)}catch{}let names;try{names=readdirSync(dir)}catch{return}for(let name of names){if(!name.endsWith(".json"))continue;try{chmodSync(join2(dir,name),FILE_MODE)}catch{}}}function fileFreezeSink(dir=defaultPtyFreezeDir()){return tightenExistingPermissions(dir),{save(record){try{mkdirSync2(dir,{recursive:!0,mode:DIR_MODE});let target=recordFile(dir,record.key),staging=`${target}.${process.pid}.tmp`;writeFileSync(staging,JSON.stringify(record),{encoding:"utf8",mode:FILE_MODE}),renameSync2(staging,target)}catch{}},drop(key){try{rmSync(recordFile(dir,key),{force:!0})}catch{}}}}function clearFrozenSessions(dir=defaultPtyFreezeDir()){try{rmSync(dir,{recursive:!0,force:!0})}catch{}}function bunTerminalDriver(spawn){let spawnTerminal=spawn??((argv,options)=>Bun.spawn(argv,options));return(request)=>{let proc=spawnTerminal([...request.argv],{cwd:request.cwd,env:request.env,terminal:{cols:request.cols,rows:request.rows,name:"xterm-256color",data:(_terminal,data)=>request.onData(data)}});return{pid:proc.pid,exited:proc.exited.then(()=>({code:proc.exitCode??null,signal:proc.signalCode??null}),()=>({code:null,signal:null})),write:(data)=>proc.terminal?.write(data),resize:(cols,rows)=>proc.terminal?.resize(cols,rows),close:()=>proc.terminal?.close(),kill:(signal)=>proc.kill(signal)}}}import{StringDecoder as StringDecoder2}from"string_decoder";function freshSessionState(key,spec,argv){return{key,cwd:spec.cwd,proc:null,alive:!0,chunks:[],bytes:0,totalBytes:0,cols:spec.cols??80,rows:spec.rows??24,command:argv,title:"",titleCarry:"",titleDecoder:new StringDecoder2("utf8"),colorQueryCarry:"",defaultColors:parseTerminalDefaultColors(spec.defaultColors)??DEFAULT_TERMINAL_COLORS,sinks:new Map,parked:!1,parkedScreenBytes:0,exit:null,restored:!1,lastFreezeAtMs:0,frozenTotalBytes:0,lastHumanWriteMs:0}}function sessionInfo(s){return{key:s.key,alive:s.alive,pid:s.proc?.pid??null,command:s.command,title:s.title,totalBytes:s.totalBytes,parked:s.parked,parkedScreenBytes:s.parkedScreenBytes,exit:s.exit,restored:s.restored||void 0}}function describeExit(exit){if(exit?.signal)return` (signal ${exit.signal})`;if(exit&&exit.code!==null)return` (code ${exit.code})`;return" (cause unknown)"}function ringTail(chunks,bytes,maxBytes){let skip=Math.max(0,bytes-maxBytes),seen=0,parts=[];for(let chunk of chunks){if(seen+chunk.byteLength>skip)parts.push(seen>=skip?chunk:chunk.subarray(skip-seen));seen+=chunk.byteLength}return Buffer.concat(parts).toString("utf8")}function hostStats(sessions,perSessionCap,parkRestoreDeltas,parkRestoreFallbacks){let ringBytes=0,count=0,parkedSessions=0,parkedScreenBytes=0;for(let session of sessions)if(count++,ringBytes+=session.bytes,session.parked)parkedSessions++,parkedScreenBytes+=session.parkedScreenBytes;return{ringBytes,ringCapacityBytes:count*perSessionCap,parkedSessions,parkedScreenBytes,parkRestoreDeltas,parkRestoreFallbacks}}var OSC_TITLE_RE=/\x1b\][02];([^\x07\x1b]*)(?:\x07|\x1b\\)/g,TITLE_CARRY_CAP=1024;function titleCarryFrom(rest){let osc=rest.lastIndexOf("\x1B]");if(osc!==-1)return rest.slice(osc,osc+TITLE_CARRY_CAP);return rest.endsWith("\x1B")?"\x1B":""}function foldOscTitle(prevCarry,chunkText){let text=prevCarry+chunkText,title=null,end=0;OSC_TITLE_RE.lastIndex=0;for(let match=OSC_TITLE_RE.exec(text);match;match=OSC_TITLE_RE.exec(text))title=match[1]??"",end=match.index+match[0].length;return{title,carry:titleCarryFrom(text.slice(end))}}function scanOscTitle(session,buf){let{title,carry}=foldOscTitle(session.titleCarry,session.titleDecoder.write(buf));if(title!==null)session.title=title;session.titleCarry=carry}function peekRing(session,sinceOffset,humanWriteQuietMs){if(!session)return{exists:!1,alive:!1,pid:null,offset:0,data:"",sinceValid:!1,exit:null};let windowStart=session.totalBytes-session.bytes,buf=Buffer.concat(session.chunks),sinceValid=!1;if(sinceOffset!==void 0&&sinceOffset>=windowStart&&sinceOffset<=session.totalBytes)buf=buf.subarray(sinceOffset-windowStart),sinceValid=!0;return{exists:!0,alive:session.alive,pid:session.proc?.pid??null,offset:session.totalBytes,data:buf.toString("base64"),sinceValid,exit:session.exit??null,...session.lastHumanWriteMs&&session.lastHumanWriteMs>0?{lastHumanWriteMs:session.lastHumanWriteMs,humanWriteQuietMs}:{}}}async function settledWithin(exited,ms){let timer,settled=await Promise.race([exited.then(()=>!0,()=>!0),new Promise((resolve)=>{timer=setTimeout(()=>resolve(!1),ms)})]);if(timer)clearTimeout(timer);return settled}function signalProcessGroup(pid,signal,fallback,platform=process.platform,onSignal){if(platform!=="win32"&&pid>1)try{process.kill(-pid,signal),onSignal?.(`sent ${signal} to process group -${pid}`);return}catch{}try{fallback(),onSignal?.(`sent ${signal} to pid ${pid} (per-process fallback)`)}catch{}}var TERMINATION_GRACE_MS=500;async function terminatePtyChild(proc,onSettled,onSignal){if(signalProcessGroup(proc.pid,"SIGTERM",()=>proc.kill("SIGTERM"),process.platform,onSignal),!await settledWithin(proc.exited,TERMINATION_GRACE_MS))signalProcessGroup(proc.pid,"SIGKILL",()=>proc.kill("SIGKILL"),process.platform,onSignal),await settledWithin(proc.exited,TERMINATION_GRACE_MS);onSettled()}class PtyChildController{deps;constructor(deps){this.deps=deps}spawn(key,spec,spare=!1){let argv=spec.command&&spec.command.length>0?[...spec.command]:[spec.shell??resolveLoginShell()],session=freshSessionState(key,spec,argv);if(this.startChild(session),session.alive)this.deps.log?.("pty",`spawned ${argv[0]} for ${key} (pid ${session.proc?.pid})`),this.deps.onSessionStart?.(spare);return session}startChild(session){try{session.proc=(this.deps.driver??bunTerminalDriver())({argv:[...session.command],cwd:session.cwd,env:embeddedTerminalEnv(process.env,{TERM:"xterm-256color",COLUMNS:String(session.cols),LINES:String(session.rows),BASH_SILENCE_DEPRECATION_WARNING:"1",KOBE_TERMINAL_PTY:"1"}),cols:session.cols,rows:session.rows,onData:(data)=>this.onData(session,data)}),session.alive=!0,session.proc.exited.then((exit)=>this.markExited(session,exit),()=>this.markExited(session))}catch(err){session.alive=!1,this.deps.log?.("pty",`spawn failed for ${session.key}: ${err instanceof Error?err.message:String(err)}`)}}async endChild(session){if(!session.alive)return;let proc=session.proc;if(!proc){this.markExited(session);return}await terminatePtyChild(proc,()=>this.markExited(session),(line)=>this.deps.log?.("pty-signal",`${session.key}: ${line}`))}markExited(session,exit){if(!session.alive)return;session.alive=!1;let sessionExit={code:exit?.code??null,signal:exit?.signal??null,at:new Date().toISOString()};session.exit=sessionExit;try{session.proc?.close()}catch{}this.deps.onExit?.(session,sessionExit)}onData(session,data){let buf=typeof data==="string"?Buffer.from(data,"utf8"):Buffer.from(data),colorQueries=foldDefaultColorQueries(session.colorQueryCarry,buf.toString("latin1"));session.colorQueryCarry=colorQueries.carry;for(let slot of colorQueries.slots)try{session.proc?.write(formatDefaultColorReply(slot,session.defaultColors))}catch{}scanOscTitle(session,buf),session.chunks.push(buf),session.bytes+=buf.byteLength,session.totalBytes+=buf.byteLength;while(session.bytes>this.deps.scrollbackCap&&session.chunks.length>1){let dropped=session.chunks.shift();if(dropped)session.bytes-=dropped.byteLength}this.deps.onOutput?.(session,buf)}}function shouldFreeze(session,now){let appended=session.totalBytes-(session.frozenTotalBytes??0);if(appended<=0)return!1;let since=now-session.lastFreezeAtMs;if(since<5000)return!1;return appended>=65536||since>=60000}class WarmSpare{deps;spare=null;constructor(deps){this.deps=deps}warm(cwd,shell,cols=80,rows=24){let argv0=shell??resolveLoginShell();if(this.spare?.alive&&this.spare.cwd===cwd&&this.spare.command[0]===argv0)return;let old=this.spare;if(this.spare=null,old)this.deps.endChild(old);let session=this.deps.spawn("::spare",{cwd,command:[argv0],cols,rows},!0);this.spare=session.alive?session:null}adopt(key,spec){let spare=this.spare;if(!spare?.alive||spare.cwd!==spec.cwd)return null;let want=spec.command&&spec.command.length>0?spec.command:[spec.shell??resolveLoginShell()];if(want.length!==1||want[0]!==spare.command[0])return null;this.spare=null,spare.key=key,spare.defaultColors=parseTerminalDefaultColors(spec.defaultColors)??spare.defaultColors;let cols=spec.cols??spare.cols,rows=spec.rows??spare.rows;if(spare.cols!==cols||spare.rows!==rows){spare.cols=cols,spare.rows=rows;try{spare.proc?.resize(cols,rows)}catch{return this.deps.markExited(spare),null}}return this.deps.log?.("pty",`adopted warm shell for ${key} (pid ${spare.proc?.pid})`),this.deps.onSessionStart?.(),this.warm(spec.cwd,spare.command[0],cols,rows),spare}async end(){let spare=this.spare;if(this.spare=null,spare)await this.deps.endChild(spare)}}var LOGGED_WRITE_BYTES=1024,DEFAULT_SCROLLBACK_CAP=524288,EXIT_TAIL_BYTES=16384,DEFAULT_HUMAN_WRITE_QUIET_MS=1e4;function resolveHumanWriteQuietMs(){let raw=process.env.KOBE_PTY_HUMAN_WRITE_QUIET_MS;if(raw===void 0)return DEFAULT_HUMAN_WRITE_QUIET_MS;let n=Number(raw);return Number.isFinite(n)&&n>=0?n:DEFAULT_HUMAN_WRITE_QUIET_MS}class PtyHost{sessions=new Map;opts;scrollbackCap;humanWriteQuietMs;parkRestoreDeltas=0;parkRestoreFallbacks=0;childController;warmSpare;constructor(opts={}){this.opts=opts,this.scrollbackCap=opts.scrollbackCap??DEFAULT_SCROLLBACK_CAP,this.humanWriteQuietMs=opts.humanWriteQuietMs??resolveHumanWriteQuietMs(),this.childController=new PtyChildController({driver:opts.driver,scrollbackCap:this.scrollbackCap,onSessionStart:(spare)=>{if(!spare)this.opts.onSessionStart?.()},onOutput:(session,data)=>{if(session.sinks.size===0){this.maybeFreeze(session);return}let frame={type:"event",name:"pty.data",payload:{key:session.key,data:data.toString("base64")}};for(let sink of session.sinks.values())sink(frame);this.maybeFreeze(session)},onExit:(session,exit)=>{let frame={type:"event",name:"pty.exit",payload:{key:session.key,pid:session.proc?.pid??null,...exit}};for(let sink of session.sinks.values())sink(frame);if(this.opts.log?.("pty",`session ${session.key} exited${describeExit(exit)}`),this.maybeFreeze(session,!0),!session.closedByRequest)try{this.opts.onSessionExit?.({key:session.key,pid:session.proc?.pid??null,exit,tail:ringTail(session.chunks,session.bytes,EXIT_TAIL_BYTES)})}catch{}this.opts.onSessionEnd?.()},log:(event,message)=>this.opts.log?.(event,message)}),this.warmSpare=new WarmSpare({spawn:(key,spec,spare)=>this.childController.spawn(key,spec,spare),endChild:(session)=>this.childController.endChild(session),markExited:(session)=>this.childController.markExited(session),log:(event,message)=>this.opts.log?.(event,message),onSessionStart:()=>this.opts.onSessionStart?.()})}open(key,spec,token,sink,sinceOffset,sincePid){let session=this.sessions.get(key),created=!1,respawned=!1;if(!session)created=!0,session=this.warmSpare.adopt(key,spec)??this.childController.spawn(key,spec),this.sessions.set(key,session);else if(!session.alive&&session.restored)this.respawn(session,spec),respawned=session.alive;else if(session.alive&&spec.cols!==void 0&&spec.rows!==void 0&&(session.cols!==spec.cols||session.rows!==spec.rows))this.resize(key,spec.cols,spec.rows);let defaultColors=parseTerminalDefaultColors(spec.defaultColors);if(defaultColors)session.defaultColors=defaultColors;session.sinks.set(token,sink),session.parked=!1,session.parkedScreenBytes=0;let windowStart=session.totalBytes-session.bytes,replay=Buffer.concat(session.chunks),sinceValid=!1;if(!created&&sinceOffset!==void 0&&sinceOffset>=windowStart&&sinceOffset<=session.totalBytes&&sincePid!==void 0&&sincePid===session.proc?.pid)replay=replay.subarray(sinceOffset-windowStart),sinceValid=!0;if(sinceOffset!==void 0&&sincePid!==void 0)if(sinceValid)this.parkRestoreDeltas++;else this.parkRestoreFallbacks++;return{replay:replay.toString("base64"),alive:session.alive,pid:session.proc?.pid??null,created,respawned,offset:session.totalBytes,sinceValid}}warm(cwd,shell,cols=80,rows=24){this.warmSpare.warm(cwd,shell,cols,rows)}write(key,data,client){let session=this.sessions.get(key);if(!session?.alive||data.length===0)return;if(client!==void 0&&session.sinks.has(client))session.lastHumanWriteMs=Date.now();if(data.length>=LOGGED_WRITE_BYTES)this.opts.log?.("pty",`wrote ${data.length} bytes to ${key}`);try{session.proc?.write(data)}catch(error){this.opts.log?.("pty",`write failed on ${key} (${data.length} bytes): ${String(error)}`)}}resize(key,cols,rows){let session=this.sessions.get(key);if(!session?.alive)return;session.cols=cols,session.rows=rows;try{session.proc?.resize(cols,rows)}catch{}}kill(key){let session=this.sessions.get(key);if(!session)return Promise.resolve();return this.sessions.delete(key),session.closedByRequest=!0,this.opts.freeze?.drop(key),this.childController.endChild(session)}rename(from,to){let session=this.sessions.get(from);if(!session||from===to||this.sessions.has(to))return!1;return this.sessions.delete(from),session.key=to,this.sessions.set(to,session),this.opts.freeze?.drop(from),this.maybeFreeze(session,!0),this.opts.log?.("pty",`renamed session ${from} \u2192 ${to}`),!0}detach(key,token,parked=!1,parkedScreenBytes=0){let session=this.sessions.get(key);if(!session)return;session.sinks.delete(token),this.applyParkedOnDetach(session,parked,parkedScreenBytes)}detachClient(token){for(let session of this.sessions.values()){if(!session.sinks.delete(token))continue;this.applyParkedOnDetach(session)}}applyParkedOnDetach(session,parked=!1,parkedScreenBytes=0){if(session.sinks.size!==0)return;session.parked=parked,session.parkedScreenBytes=parked?Math.max(0,parkedScreenBytes):0}list(){return Array.from(this.sessions.values(),(s)=>sessionInfo(s))}peek(key,sinceOffset){return peekRing(this.sessions.get(key),sinceOffset,this.humanWriteQuietMs)}stats(){return hostStats(this.sessions.values(),this.scrollbackCap,this.parkRestoreDeltas,this.parkRestoreFallbacks)}sweepTasks(liveTaskIds){for(let key of Array.from(this.sessions.keys())){let taskId=key.split("::")[0]??key;if(!liveTaskIds.has(taskId))this.kill(key)}}async killAll(){let sessions=Array.from(this.sessions.keys(),(key)=>this.kill(key));sessions.push(this.warmSpare.end()),await Promise.all(sessions)}async shutdown(){this.flushFrozen();let endings=Array.from(this.sessions.values(),(session)=>this.childController.endChild(session));endings.push(this.warmSpare.end()),await Promise.all(endings)}flushFrozen(){for(let session of this.sessions.values())this.maybeFreeze(session,!0)}restoreFrozen(records){let restoredCount=0;for(let record of records){if(this.sessions.has(record.key))continue;let session=thawSession(record,this.scrollbackCap);if(!session)continue;this.sessions.set(record.key,session),restoredCount++}if(restoredCount>0)this.opts.log?.("pty",`restored ${restoredCount} frozen session(s) from disk`);return restoredCount}respawn(session,spec){if(session.restored=!1,session.exit=null,spec.command&&spec.command.length>0)session.command=[...spec.command];if(session.cols=spec.cols??session.cols,session.rows=spec.rows??session.rows,session.defaultColors=parseTerminalDefaultColors(spec.defaultColors)??session.defaultColors,this.childController.startChild(session),!session.alive)return;this.opts.log?.("pty",`respawned restored session ${session.key} (pid ${session.proc?.pid})`),this.opts.onSessionStart?.()}maybeFreeze(session,force=!1){let freeze=this.opts.freeze;if(!freeze||session.key.startsWith("::"))return;if(session.closedByRequest)return;let now=Date.now();if(!force&&!shouldFreeze(session,now))return;session.lastFreezeAtMs=now,session.frozenTotalBytes=session.totalBytes,freeze.save(freezeSession(session))}liveCount(){let n=0;for(let session of this.sessions.values())if(session.alive)n++;return n}}var DEFAULT_IDLE_EXIT_MS=60000,DEFAULT_ORPHAN_CHECK_MS=30000;function resolveMaxLifetimeMs(){let raw=process.env.KOBE_PTY_MAX_LIFETIME_MS;if(raw===void 0)return null;let n=Number(raw);return Number.isFinite(n)&&n>0?n:null}function resolveIdleExitMs(){let raw=process.env.KOBE_PTY_IDLE_EXIT_MS;if(raw===void 0)return DEFAULT_IDLE_EXIT_MS;let n=Number(raw);return Number.isFinite(n)&&n>=0?n:DEFAULT_IDLE_EXIT_MS}async function startPtyHostServer(options={}){migrateLegacyPtyHostData();let socketPath=options.socketPath??defaultPtyHostSocketPath(),pidPath=options.pidPath??defaultPtyHostPidPath(),freezeDir=options.freezeDir??defaultPtyFreezeDir(),idleExitMs=options.idleExitMs||resolveIdleExitMs(),orphanCheckMs=options.orphanCheckMs||DEFAULT_ORPHAN_CHECK_MS,maxLifetimeMs=options.maxLifetimeMs??resolveMaxLifetimeMs(),bootedAtMs=Date.now(),log=options.log??(()=>{}),clients=new Set,stopping=!1,wipeFreezeOnStop=!1,idleTimer=null,cancelIdle=()=>{if(idleTimer)clearTimeout(idleTimer);idleTimer=null},armIdle=()=>{if(stopping)return;cancelIdle(),idleTimer=setTimeout(()=>{if(stopping||ptys.liveCount()>0)return;log("idle",`no live sessions for ${idleExitMs}ms \u2014 exiting`),stop()},idleExitMs)},orphaned=()=>{if(maxLifetimeMs!==null&&Date.now()-bootedAtMs>=maxLifetimeMs)return`exceeded its ${maxLifetimeMs}ms lifetime ceiling`;let owner;try{owner=readFileSync2(pidPath,"utf8").trim()}catch{return`pidfile ${pidPath} is gone`}let pid=Number.parseInt(owner,10);if(pid===process.pid)return null;return Number.isInteger(pid)?`pidfile ${pidPath} now names pid ${pid}`:`pidfile ${pidPath} is unreadable`},orphanTimer=setInterval(()=>{if(stopping)return;let reason=orphaned();if(!reason)return;log("orphan",`${reason} \u2014 exiting rather than outliving my owner`),stop()},orphanCheckMs);orphanTimer.unref?.();let ptys=new PtyHost({onSessionStart:cancelIdle,onSessionEnd:()=>{if(ptys.liveCount()===0)armIdle()},onSessionExit:(info)=>{try{recordPtyExit(info)}catch(err){log("pty",`exit record write failed for ${info.key}: ${err instanceof Error?err.message:String(err)}`)}},freeze:fileFreezeSink(freezeDir),driver:options.driver,log});ptys.restoreFrozen(loadFrozenSessions(freezeDir,Date.now(),(s)=>{let mib=(bytes)=>`${Math.round(bytes/1048576)}MB`,notes=[`restored ${s.restored} (${mib(s.bytesRead)})`];if(s.deferred>0)notes.push(`${s.deferred} left unread past the ${mib(FREEZE_RESTORE_MAX_BYTES)} budget`);if(s.expired>0)notes.push(`${s.expired} deleted past the ${FREEZE_TTL_MS/86400000}-day TTL`);if(s.unreadable>0)notes.push(`${s.unreadable} unreadable`);log("freeze",notes.join(", "))}));let pipeSocket=isWindowsPipePath(socketPath);if(await ensureOwnerOnlyStateDir(resolveDaemonHomeDir()),!pipeSocket)await mkdir(dirname(socketPath),{recursive:!0});await mkdir(dirname(pidPath),{recursive:!0});let server=createServer((socket)=>{let client={socket,writer:new ClientWriter(socket,{onOverflow:()=>{log("backpressure","disconnecting PTY client whose critical queue exceeded 8MiB"),socket.destroy()}}),buffer:""};clients.add(client);let decoder=new StringDecoder3("utf8");socket.on("data",(chunk)=>{client.buffer+=decoder.write(chunk),drain(client)}),socket.on("error",()=>{}),socket.on("close",()=>{clients.delete(client),ptys.detachClient(client)})}),api={socketPath,pidPath,async close(){if(stopping)return;if(stopping=!0,cancelIdle(),clearInterval(orphanTimer),await ptys.shutdown(),wipeFreezeOnStop)clearFrozenSessions(freezeDir);for(let client of Array.from(clients))client.socket.destroy();if(await new Promise((resolve)=>server.close(()=>resolve())),!pipeSocket)await unlink(socketPath).catch(()=>{});await unlink(pidPath).catch(()=>{})}};async function stop(){await api.close().catch((err)=>logDaemonError("pty-host-shutdown",err)),options.onStop?.()}function dispatch(req,client){switch(req.name){case"hello":return{protocolVersion:DAEMON_PROTOCOL_VERSION,ptyHost:!0,pid:process.pid};case"pty.open":{let payload=objectPayload(req.payload);return ptys.open(requireString(payload,"key"),{cwd:requireString(payload,"cwd"),command:Array.isArray(payload.command)?payload.command.filter((c)=>typeof c==="string"):void 0,shell:typeof payload.shell==="string"?payload.shell:void 0,cols:typeof payload.cols==="number"?payload.cols:void 0,rows:typeof payload.rows==="number"?payload.rows:void 0,defaultColors:parseTerminalDefaultColors(payload.defaultColors)??void 0},client,(frame)=>writeFrame(client,frame),typeof payload.sinceOffset==="number"?payload.sinceOffset:void 0,typeof payload.sincePid==="number"?payload.sincePid:void 0)}case"pty.write":{let payload=objectPayload(req.payload);return ptys.write(requireString(payload,"key"),typeof payload.data==="string"?payload.data:"",client),{}}case"pty.resize":{let payload=objectPayload(req.payload);return ptys.resize(requireString(payload,"key"),typeof payload.cols==="number"?payload.cols:80,typeof payload.rows==="number"?payload.rows:24),{}}case"pty.kill":return ptys.kill(requireString(objectPayload(req.payload),"key")),{};case"pty.rename":{let payload=objectPayload(req.payload);return{renamed:ptys.rename(requireString(payload,"from"),requireString(payload,"to"))}}case"pty.detach":{let payload=objectPayload(req.payload);ptys.detach(requireString(payload,"key"),client,payload.parked===!0,typeof payload.parkedScreenBytes==="number"?payload.parkedScreenBytes:0)}return{};case"pty.list":return{pid:process.pid,rssBytes:process.memoryUsage().rss,sessions:ptys.list(),stats:ptys.stats()};case"pty.peek":{let payload=objectPayload(req.payload);return ptys.peek(requireString(payload,"key"),typeof payload.sinceOffset==="number"?payload.sinceOffset:void 0)}case"pty.warm":{let payload=objectPayload(req.payload);return ptys.warm(requireString(payload,"cwd"),typeof payload.shell==="string"?payload.shell:void 0,typeof payload.cols==="number"?payload.cols:void 0,typeof payload.rows==="number"?payload.rows:void 0),{}}case"pty.sweep":{let payload=objectPayload(req.payload),ids=Array.isArray(payload.liveTaskIds)?payload.liveTaskIds.filter((id)=>typeof id==="string"):[];return ptys.sweepTasks(new Set(ids)),{}}case"daemon.stop":return wipeFreezeOnStop=!0,setTimeout(()=>void stop(),0).unref(),{};default:throw Error(`unknown pty-host request: ${req.name}`)}}function drain(client){let nl=client.buffer.indexOf(`
3
+ `);while(nl!==-1){let line=client.buffer.slice(0,nl);if(client.buffer=client.buffer.slice(nl+1),line.trim().length>0){let frame=null;try{frame=JSON.parse(line)}catch{writeFrame(client,{type:"response",id:"parse-error",error:{message:"malformed frame"}})}if(frame)if(frame.type!=="request")writeFrame(client,{type:"response",id:"parse-error",error:{message:"requests only"}});else try{writeFrame(client,{type:"response",id:frame.id,name:frame.name,payload:dispatch(frame,client)})}catch(err){writeFrame(client,{type:"response",id:frame.id,name:frame.name,error:{message:err instanceof Error?err.message:String(err)}})}}nl=client.buffer.indexOf(`
4
+ `)}}if(await listenOnUnixSocket(server,socketPath),await writeFile(pidPath,`${process.pid}
5
+ `,"utf8"),!pipeSocket){let home=resolveDaemonHomeDir();await linkLegacyRuntimePath(socketPath,legacyPtyHostSocketPath(home)),await linkLegacyRuntimePath(pidPath,legacyPtyHostPidPath(home))}return armIdle(),log("boot",`pty host listening on ${socketPath}`),api}function writeFrame(client,frame){client.writer.write(frameToLine(frame),!0)}async function runPtyHostSubcommand(_argv){rotateLogIfNeeded(defaultPtyHostLogPath()),installDaemonCrashHandlers();let server=await startPtyHostServer({log:(event,message)=>console.log(`[pty-host ${event}] ${message}`),onStop:()=>process.exit(0)});console.log(`rove pty-host: listening on ${server.socketPath}`);let shutdown=async()=>{await server.close(),process.exit(0)};process.once("SIGINT",()=>void shutdown()),process.once("SIGTERM",()=>void shutdown())}export{runPtyHostSubcommand};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{ensurePluginEnginesLoaded,loadPluginEngines,registeredEngineIds,reloadPluginEngines}from"./chunk-ykvk2546.js";import"./chunk-jyv82gf0.js";import"./chunk-kwe61yx7.js";import"./chunk-df7qe9y7.js";import"./chunk-k3ksts6g.js";import"./chunk-gjf604bb.js";import"./chunk-2brn8e1y.js";import"./chunk-9c1e5z6a.js";import"./chunk-6dn6h640.js";import"./chunk-cb2fpajx.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";export{reloadPluginEngines,registeredEngineIds,loadPluginEngines,ensurePluginEnginesLoaded};
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ import{SUBCOMMAND_VERBS}from"./chunk-s76r56rj.js";import{expandTilde}from"./chunk-0vmrteyg.js";import{errorMessage}from"./chunk-2brn8e1y.js";import"./chunk-6dn6h640.js";import{activeCliName}from"./chunk-ef11h2am.js";import"./chunk-8ddjfc2v.js";import"./chunk-cb2fpajx.js";import"./chunk-3bfm4hrs.js";import{__require}from"./chunk-wqfp8d9w.js";import{readFileSync}from"fs";import{resolve}from"path";var CLI_NAME=activeCliName(),REPO_USAGE=[`Usage: ${CLI_NAME} repo <show|set|unset> [path] [options]`,"","Manage a repo's per-user init override (state.json fallback for repos","that don't ship .rove/init.sh / .rove/init-prompt.md).","","Commands:"," show [path] Print the override + repo convention files"," set [path] <options> Set the init script and/or first prompt"," unset [path] [--init-script] [--init-prompt] Clear one or both (default: both)","","Set options (later wins; *-file reads from disk):"," --init-script <text> Inline shell to run before the engine"," --init-script-file <path> Read the init script from a file"," --init-prompt <text> Inline first prompt for the engine"," --init-prompt-file <path> Read the first prompt from a file",""," path defaults to the current directory (resolved to its git toplevel).",""].join(`
3
+ `);function usageError(message){process.stderr.write(`${CLI_NAME} repo: ${message}
4
+
5
+ ${REPO_USAGE}
6
+ `),process.exit(2)}function readArgFile(path){try{return readFileSync(resolve(process.cwd(),expandTilde(path)),"utf8")}catch(err){usageError(`cannot read ${path}: ${errorMessage(err)}`)}}function parseRepoArgs(args){let out={};for(let i=0;i<args.length;i++){let a=args[i],need=()=>{let v=args[++i];if(v===void 0)usageError(`${a} requires a value`);return v};if(a==="--init-script")out.initScript=need();else if(a==="--init-script-file")out.initScript=readArgFile(need());else if(a==="--init-prompt")out.initPrompt=need();else if(a==="--init-prompt-file")out.initPrompt=readArgFile(need());else if(a.startsWith("-"))usageError(`unknown flag "${a}"`);else if(out.path===void 0)out.path=a;else usageError(`unexpected argument "${a}"`)}return out}function parseUnsetArgs(args){let path,clearScript=!1,clearPrompt=!1;for(let a of args)if(a==="--init-script")clearScript=!0;else if(a==="--init-prompt")clearPrompt=!0;else if(a.startsWith("-"))usageError(`unknown flag "${a}"`);else if(path===void 0)path=a;else usageError(`unexpected argument "${a}"`);if(!clearScript&&!clearPrompt)clearScript=!0,clearPrompt=!0;return{path,clearScript,clearPrompt}}async function runRepoSubcommand(args){let[verb,...rest]=args;if(verb===void 0||verb==="--help"||verb==="-h"||verb==="help"){process.stdout.write(`${REPO_USAGE}
7
+ `);return}if(!SUBCOMMAND_VERBS.repo.includes(verb))usageError(`unknown verb "${verb}"`);let{getRepoInitOverride,setRepoInitOverride,resolveRepoRoot}=await import("./chunk-mfqjdpd8.js"),{existsSync}=await import("fs"),{join}=await import("path");if(verb==="show"){let[pathArg]=rest.filter((a)=>!a.startsWith("-")),repo=resolveRepoRoot(resolve(process.cwd(),expandTilde(pathArg??"."))),override=getRepoInitOverride(repo),hasRoveScript=existsSync(join(repo,".rove","init.sh")),hasRovePrompt=existsSync(join(repo,".rove","init-prompt.md")),hasKobeScript=existsSync(join(repo,".kobe","init.sh")),hasKobePrompt=existsSync(join(repo,".kobe","init-prompt.md"));console.log(`repo: ${repo}`),console.log(` .rove/init.sh: ${hasRoveScript?"present (wins)":"absent"}`),console.log(` .rove/init-prompt.md: ${hasRovePrompt?"present (wins)":"absent"}`),console.log(` .kobe/init.sh: ${hasKobeScript?"present (legacy fallback)":"absent"}`),console.log(` .kobe/init-prompt.md: ${hasKobePrompt?"present (legacy fallback)":"absent"}`),console.log(` override initScript: ${override.initScript?quotePreview(override.initScript):"(unset)"}`),console.log(` override initPrompt: ${override.initPrompt?quotePreview(override.initPrompt):"(unset)"}`);return}if(verb==="set"){let flags=parseRepoArgs(rest);if(flags.initScript===void 0&&flags.initPrompt===void 0)usageError("set needs at least one of --init-script(-file) / --init-prompt(-file)");let repo=resolveRepoRoot(resolve(process.cwd(),expandTilde(flags.path??"."))),next=setRepoInitOverride(repo,{...flags.initScript!==void 0?{initScript:flags.initScript}:{},...flags.initPrompt!==void 0?{initPrompt:flags.initPrompt}:{}});console.log(`updated override for ${repo}`),console.log(` initScript: ${next.initScript?quotePreview(next.initScript):"(unset)"}`),console.log(` initPrompt: ${next.initPrompt?quotePreview(next.initPrompt):"(unset)"}`);return}if(verb==="unset"){let{path,clearScript,clearPrompt}=parseUnsetArgs(rest),repo=resolveRepoRoot(resolve(process.cwd(),expandTilde(path??"."))),next=setRepoInitOverride(repo,{...clearScript?{initScript:""}:{},...clearPrompt?{initPrompt:""}:{}});console.log(`cleared override for ${repo}`),console.log(` initScript: ${next.initScript?quotePreview(next.initScript):"(unset)"}`),console.log(` initPrompt: ${next.initPrompt?quotePreview(next.initPrompt):"(unset)"}`);return}usageError(`unknown verb "${verb}"`)}function quotePreview(value){let oneLine=value.replace(/\s+/g," ").trim();return oneLine.length>60?`"${oneLine.slice(0,57)}\u2026"`:`"${oneLine}"`}export{runRepoSubcommand};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{activeCliName}from"./chunk-ef11h2am.js";import"./chunk-8ddjfc2v.js";import"./chunk-cb2fpajx.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";var SEARCH_TIMEOUT_MS=5000,CLI_NAME=activeCliName(),FIRST_PARTY=[{ref:"Sma1lboy/kobe-plugins/notify",desc:"Desktop/ntfy notifications when an agent finishes or needs input"},{ref:"Sma1lboy/kobe-plugins/github-start",desc:"Start a Rove task from a GitHub issue or PR"},{ref:"Sma1lboy/kobe-plugins/worktree-include",desc:"Copy .worktreeinclude-matched files into new worktrees"},{ref:"Sma1lboy/kobe-plugins/linear-start",desc:"Pick a Linear issue (fzf) and start a task on its branch"},{ref:"Sma1lboy/kobe-plugins/lazygit",desc:"lazygit on the task worktree, as a pane tab"},{ref:"Sma1lboy/kobe-plugins/browser",desc:"Chromium rendered as terminal cells (carbonyl) in a pane tab"}];async function fetchTopic(topic,query){let q=encodeURIComponent(`topic:${topic}${query?` ${query}`:""}`),ctrl=new AbortController,timer=setTimeout(()=>ctrl.abort(),SEARCH_TIMEOUT_MS);try{let res=await fetch(`https://api.github.com/search/repositories?q=${q}&sort=stars&order=desc&per_page=50`,{signal:ctrl.signal,headers:{accept:"application/vnd.github+json"}});if(!res.ok)return null;let body=await res.json();if(!Array.isArray(body.items))return null;return body.items.filter((r)=>typeof r.full_name==="string").map((r)=>({ref:r.full_name,desc:r.description??"",stars:r.stargazers_count}))}catch{return null}finally{clearTimeout(timer)}}async function searchMarketplace(query){let topicResults=await Promise.all([fetchTopic("rove-plugin",query),fetchTopic("kobe-plugin",query)]),lower=query?.toLowerCase(),seeds=FIRST_PARTY.filter((s)=>!lower||`${s.ref} ${s.desc}`.toLowerCase().includes(lower)).map((s)=>({...s,firstParty:!0})),entries=dedupeEntries([...seeds,...topicResults.flatMap((result)=>result??[])]);if(topicResults.every((result)=>result===null))console.error("(GitHub search unreachable \u2014 showing first-party plugins only)");if(entries.length===0){console.log(query?`no plugins match \`${query}\``:"no plugins found");return}let width=Math.min(48,Math.max(...entries.map((e)=>e.ref.length))+2);for(let e of entries){let stars=e.stars!==void 0?`\u2605${e.stars}`:e.firstParty?"first-party":"";console.log(`${e.ref.padEnd(width)}${stars.padEnd(12)}${e.desc}`)}console.log(`
3
+ install: ${CLI_NAME} plugin install <owner/repo[/subdir]> \u2014 browse: https://rove.run/plugins`)}function dedupeEntries(entries){let seen=new Set;return entries.filter((entry)=>{let key=entry.ref.toLowerCase();if(seen.has(key))return!1;return seen.add(key),!0})}export{searchMarketplace};
@@ -0,0 +1,15 @@
1
+ // @bun
2
+ import{$jsx,$jsxs,pageCloseBindings,require_react_production,useBindings,useRenderer,useT,useTheme}from"./chunk-3rqa4pyt.js";import{CURRENT_VERSION,UPDATE_COMMAND,checkLatestVersion,fetchReleaseNotesRange,releasePageUrl}from"./chunk-eq5f3z2f.js";import{__toESM}from"./chunk-wqfp8d9w.js";import{spawnSync}from"child_process";function waitForKeypress(){if(!process.stdin.isTTY)return Promise.resolve();return new Promise((resolve)=>{let stdin=process.stdin,done=()=>{stdin.off("data",done),stdin.setRawMode?.(!1),stdin.pause(),resolve()};stdin.setRawMode?.(!0),stdin.resume(),stdin.once("data",done)})}async function runShellUpdater(opts){opts.renderer?.destroy(),process.stdout.write(`
3
+ rove ${CURRENT_VERSION} -> ${opts.targetLabel}
4
+ `),process.stdout.write(`running: ${opts.command}
5
+
6
+ `);let result=spawnSync("sh",["-c",opts.command],{stdio:"inherit"}),code=result.status??(result.error?1:0);if(result.error)process.stderr.write(`
7
+ rove update: failed to start updater: ${result.error.message}
8
+ `);process.stdout.write(code===0?`
9
+ ${opts.t("update.updateComplete")}
10
+ `:`
11
+ ${opts.t("update.updateFailed",{code:String(code)})}
12
+ `),process.stdout.write(opts.t("update.pressAnyKey")),await waitForKeypress(),process.exit(code)}import{TextAttributes}from"@opentui/core";var import_react2=__toESM(require_react_production(),1);import{spawn}from"child_process";function spawnDetached(cmd,args,opts={}){try{let child=spawn(cmd,args,{detached:!0,stdio:"ignore"});return child.on("error",(err)=>opts.onError?.(err)),child.unref(),!0}catch(err){if(opts.onError&&err instanceof Error)opts.onError(err);return!1}}function systemOpenArgv(absPath,platform=process.platform){if(platform==="darwin")return["open",absPath];if(platform==="win32")return["cmd","/c","start","",absPath];return["xdg-open",absPath]}function openWithSystemViewer(absPath){let[cmd,...args]=systemOpenArgv(absPath);return spawnDetached(cmd??"",args)}function releaseBodyLines(body){return body.replace(/\r\n/g,`
13
+ `).split(`
14
+ `).map((line)=>line.trim()).filter(Boolean)}function UpdatePage(props){let{theme}=useTheme(),t=useT(),renderer=useRenderer(),[info,setInfo]=import_react2.useState(null),[checked,setChecked]=import_react2.useState(!1),[releaseNotes,setReleaseNotes]=import_react2.useState([]),[loadingNotes,setLoadingNotes]=import_react2.useState(!0),[selected,setSelected]=import_react2.useState("close"),[status,setStatus]=import_react2.useState(null),latest=info?.latest??CURRENT_VERSION,latestUnknown=checked&&info===null,hasUpdate=info?.hasUpdate===!0,upToDate=checked&&info!==null&&!hasUpdate,releaseUrl=releaseNotes[0]?.url??releasePageUrl(latest),actions=[...hasUpdate?[{id:"update",key:"U",label:t("update.actions.updateNow"),detail:UPDATE_COMMAND}]:[],{id:"release",key:"R",label:t("update.actions.openRelease"),detail:releaseUrl??t("update.releaseUrlUnavailable")},{id:"close",key:"Q",label:t("update.actions.close"),detail:t("update.actions.closeDetail")}];import_react2.useEffect(()=>{load()},[]);async function load(){let next=await checkLatestVersion({force:!0});if(setInfo(next),setChecked(!0),next?.hasUpdate)setSelected("update");let fetched=next?.hasUpdate?await fetchReleaseNotesRange({current:CURRENT_VERSION,latest:next.latest}):[];setReleaseNotes(fetched),setLoadingNotes(!1)}function move(delta){let ids=actions.map((a)=>a.id),next=(ids.indexOf(selected)+delta+ids.length)%ids.length;setSelected(ids[next]??"close")}function activate(id=selected){if(id==="close"){props.onClose();return}if(id==="update"&&!hasUpdate)return;if(id==="release"){setStatus(openWithSystemViewer(releaseUrl)?t("update.statusReleaseOpened"):t("update.statusReleaseError"));return}runUpdater()}async function runUpdater(){setStatus(t("update.statusRunningUpdater")),await new Promise((resolve)=>setTimeout(resolve,30)),await runShellUpdater({renderer,t,targetLabel:latest,command:UPDATE_COMMAND})}return useBindings(()=>({bindings:[{key:"up",cmd:()=>move(-1)},{key:"down",cmd:()=>move(1)},{key:"k",cmd:()=>move(-1)},{key:"j",cmd:()=>move(1)},{key:"return",cmd:()=>activate()},{key:"u",cmd:()=>activate("update")},{key:"r",cmd:()=>activate("release")},...pageCloseBindings(()=>activate("close"))]})),$jsxs("box",{flexDirection:"column",flexGrow:1,backgroundColor:theme.background,paddingTop:1,paddingLeft:2,paddingRight:2,children:[$jsxs("box",{flexDirection:"row",justifyContent:"space-between",flexShrink:0,children:[$jsx("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"none",children:t("update.pageTitle")}),$jsx("text",{fg:theme.textMuted,wrapMode:"none",onMouseUp:()=>activate("close"),children:"q / esc"})]}),$jsxs("box",{flexDirection:"row",gap:2,flexShrink:0,paddingTop:1,children:[$jsx("text",{fg:theme.textMuted,wrapMode:"none",children:t("update.current")}),$jsxs("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"none",children:["v",CURRENT_VERSION]}),$jsx("text",{fg:theme.textMuted,wrapMode:"none",children:t("update.latest")}),latestUnknown?$jsx("text",{fg:theme.textMuted,attributes:TextAttributes.BOLD,wrapMode:"none",children:t("update.latestUnknown")}):$jsxs("text",{fg:hasUpdate?theme.warning:theme.success,attributes:TextAttributes.BOLD,wrapMode:"none",children:["v",latest]})]}),upToDate?$jsx("box",{flexShrink:0,paddingTop:1,children:$jsx("text",{fg:theme.success,wrapMode:"word",children:t("update.upToDate")})}):null,$jsx("box",{flexDirection:"column",flexShrink:0,paddingTop:1,gap:0,children:actions.map((action)=>$jsxs("box",{flexDirection:"row",gap:1,paddingLeft:1,paddingRight:1,backgroundColor:selected===action.id?theme.primary:void 0,onMouseUp:()=>activate(action.id),children:[$jsx("box",{width:4,flexShrink:0,children:$jsxs("text",{fg:selected===action.id?theme.selectedListItemText:theme.accent,attributes:TextAttributes.BOLD,wrapMode:"none",children:["[",action.key,"]"]})}),$jsx("box",{width:14,flexShrink:0,children:$jsx("text",{fg:selected===action.id?theme.selectedListItemText:theme.text,wrapMode:"none",children:action.label})}),$jsx("text",{fg:selected===action.id?theme.selectedListItemText:theme.textMuted,wrapMode:"word",children:action.detail})]},action.id))}),status?$jsx("text",{fg:theme.info,wrapMode:"word",children:status}):null,hasUpdate?$jsx("box",{flexShrink:0,paddingTop:1,children:$jsx("text",{fg:theme.textMuted,attributes:TextAttributes.DIM,wrapMode:"none",children:t("update.changesSectionHeader",{from:CURRENT_VERSION,to:latest})})}):null,$jsx("scrollbox",{flexGrow:1,flexShrink:1,stickyScroll:!1,verticalScrollbarOptions:{trackOptions:{backgroundColor:theme.background,foregroundColor:theme.borderActive}},children:$jsxs("box",{flexDirection:"column",paddingRight:1,paddingBottom:1,gap:0,children:[loadingNotes?$jsx("text",{fg:theme.textMuted,children:t("update.loadingNotes")}):null,!loadingNotes&&hasUpdate&&releaseNotes.length===0?$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("update.notesUnavailable")}):null,releaseNotes.map((release)=>$jsxs("box",{flexDirection:"column",paddingBottom:1,gap:0,children:[$jsxs("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"none",children:["v",release.version]}),releaseBodyLines(release.body).map((line,i)=>$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:line},`${i}:${line}`))]},release.version))]})})]})}
15
+ export{runShellUpdater,spawnDetached,openWithSystemViewer,releaseBodyLines,UpdatePage};
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ import{installedSkillDirs}from"./chunk-g1ybdgad.js";import{CLAUDE_HOOK_EVENT_MAP,claudeSettingsPath,hasKobeActivityHooks,isObject}from"./chunk-7nhdvr5v.js";import"./chunk-2fypr41w.js";import"./chunk-k3ksts6g.js";import"./chunk-gjf604bb.js";import"./chunk-qtke4x42.js";import"./chunk-xy8be792.js";import"./chunk-9c1e5z6a.js";import"./chunk-6dn6h640.js";import"./chunk-h82kzdsn.js";import"./chunk-ef11h2am.js";import"./chunk-8ddjfc2v.js";import"./chunk-cb2fpajx.js";import"./chunk-grh6dvfv.js";import"./chunk-pw6yfj10.js";import"./chunk-x760j10f.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";import{readFileSync}from"fs";import{homedir}from"os";var PLUGIN_NAME="rove";function readSettings(path){try{let parsed=JSON.parse(readFileSync(path,"utf8"));return isObject(parsed)?parsed:null}catch{return null}}function isRovePluginEnabled(settingsFilePath=claudeSettingsPath()){let settings=readSettings(settingsFilePath);if(!settings||!isObject(settings.enabledPlugins))return!1;return Object.entries(settings.enabledPlugins).some(([key,on])=>on===!0&&key.split("@")[0]===PLUGIN_NAME)}function detectLegacyInstalls(opts={}){let settingsFilePath=opts.settingsFilePath??claudeSettingsPath(),home=opts.home??homedir(),settings=readSettings(settingsFilePath),legacyHooks=settings!==null&&(hasKobeActivityHooks(settings,CLAUDE_HOOK_EVENT_MAP)||settingsHasWorktreeWatch(settings)),legacySkillDirs=installedSkillDirs(home);return{legacyHooks,legacySkillDirs}}function settingsHasWorktreeWatch(settings){let hooks=isObject(settings.hooks)?settings.hooks:{};return Object.values(hooks).some((groups)=>Array.isArray(groups)&&groups.some((g)=>isObject(g)&&Array.isArray(g.hooks)&&g.hooks.some((h)=>isObject(h)&&typeof h.command==="string"&&h.command.includes("worktree-created")&&!h.command.includes("CLAUDE_PLUGIN_ROOT"))))}function migrationHint(findings,cliName){if(!findings.legacyHooks&&findings.legacySkillDirs.length===0)return null;let lines=[`${cliName}: the Rove Claude Code plugin is enabled, but legacy installs remain:`];if(findings.legacyHooks)lines.push(" \u2022 ~/.claude/settings.json still has the settings-managed Rove hooks (every event fires TWICE).",` Run \`${cliName} hook cleanup\` to remove them (only Rove's own entries are touched).`);for(let dir of findings.legacySkillDirs)lines.push(` \u2022 legacy skill copy at ${dir} (the plugin bundles the skill \u2014 two copies double-register).`," Remove that directory to keep the plugin's copy only.");return lines.push(" Reading this inside a session? Just ask your agent to run the cleanup for you."),`${lines.join(`
3
+ `)}
4
+ `}export{migrationHint,isRovePluginEnabled,detectLegacyInstalls};
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ import{readFileSync,statSync}from"fs";import{isAbsolute,join,resolve}from"path";var DEFAULT_BASE_REF_CANDIDATES=["origin/main","origin/master","main","master"];function mtime(path){let st=statSync(path,{throwIfNoEntry:!1});return st?st.mtimeMs:null}function readText(path){try{return readFileSync(path,"utf8")}catch{return null}}function resolveGitDirs(worktreePath){let dotGit=join(worktreePath,".git"),st=statSync(dotGit,{throwIfNoEntry:!1});if(!st)return null;let gitDir=dotGit;if(!st.isDirectory()){let pointer=readText(dotGit)?.match(/^gitdir:\s*(.+)$/m)?.[1]?.trim();if(!pointer)return null;gitDir=isAbsolute(pointer)?pointer:resolve(worktreePath,pointer)}let common=readText(join(gitDir,"commondir"))?.trim(),commonDir=common?isAbsolute(common)?common:resolve(gitDir,common):gitDir;return{gitDir,commonDir}}function looseRefPaths(dirs,ref){let rel=ref.startsWith("refs/")?ref:`refs/remotes/${ref}`,alt=ref.startsWith("refs/")?null:`refs/heads/${ref}`,names=alt?[rel,alt]:[rel],out=[];for(let name of names)if(out.push(join(dirs.gitDir,name)),dirs.commonDir!==dirs.gitDir)out.push(join(dirs.commonDir,name));return out}function readRefSha(dirs,ref){for(let path of looseRefPaths(dirs,ref)){let raw=readText(path)?.trim();if(raw&&/^[0-9a-f]{40,64}$/.test(raw))return raw}let packed=readText(join(dirs.commonDir,"packed-refs"));if(!packed)return null;let wanted=new Set(looseRefPaths(dirs,ref).map((p)=>p.replace(/^.*?(refs\/)/,"$1")));for(let line of packed.split(`
3
+ `)){if(!line||line.startsWith("#")||line.startsWith("^"))continue;let[sha,name]=line.split(" ",2);if(sha&&name&&wanted.has(name.trim()))return sha}return null}function readHeadSha(dirs){let head=readText(join(dirs.gitDir,"HEAD"))?.trim();if(!head)return null;if(/^[0-9a-f]{40,64}$/.test(head))return head;let ref=head.match(/^ref:\s*(.+)$/)?.[1]?.trim();return ref?readRefSha(dirs,ref):null}function worktreeFingerprint(worktreePath,baseRef){try{let dirs=resolveGitDirs(worktreePath);if(!dirs)return null;let head=readText(join(dirs.gitDir,"HEAD"))?.trim();if(!head)return null;let parts=[head,mtime(worktreePath),mtime(join(dirs.gitDir,"index")),mtime(join(dirs.gitDir,"HEAD")),mtime(join(dirs.gitDir,"FETCH_HEAD")),mtime(join(dirs.commonDir,"FETCH_HEAD")),mtime(join(dirs.commonDir,"packed-refs"))],headRef=head.match(/^ref:\s*(.+)$/)?.[1]?.trim();if(headRef)for(let path of looseRefPaths(dirs,headRef))parts.push(mtime(path));parts.push(mtime(join(dirs.commonDir,"refs/remotes/origin/HEAD")));for(let ref of baseRef?[baseRef,...DEFAULT_BASE_REF_CANDIDATES]:DEFAULT_BASE_REF_CANDIDATES)for(let path of looseRefPaths(dirs,ref))parts.push(mtime(path));return parts.join("|")}catch{return null}}
4
+ export{DEFAULT_BASE_REF_CANDIDATES,resolveGitDirs,readRefSha,readHeadSha,worktreeFingerprint};
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ import{ensurePluginEnginesLoaded,registeredEngineIds}from"./chunk-ykvk2546.js";import{resolveActiveTaskId}from"./chunk-1rkb0j1j.js";import{DIRTY_WORKTREE_CODE,EMPTY_BRANCH_DIRTY_WORKTREE_CODE,deriveTitleFromPrompt}from"./chunk-aq912vvw.js";import{DEFAULT_FEEDBACK_CATEGORY_SLUG,submitFeedback}from"./chunk-wsvf9eka.js";import{ulid}from"./chunk-qkehg2c1.js";import{expandTilde}from"./chunk-0vmrteyg.js";import{CURRENT_VERSION}from"./chunk-eq5f3z2f.js";import{ApiError,ENGINE_NOT_OBSERVED_REASON,ENGINE_START_POLL_MS,ENGINE_START_PROBE_MS,VERB_GROUP_IDS,closeTabsSnapshot,deferOrThrow,deliverHostedPrompt,deliverToKey,ensurePtyHost,findEngineKey,hasLiveEngineTab,helpStep,joinTaskTabs,killTaskSessions,listSessions,listSessionsOrNull,markCliTabSession,mintCliTab,openPtyHost,outcomeFields,publishCliTabSnapshot,readTabsSnapshot,resolveComposerManifest,restoredTabLaunch,splitDaemonCode,taskEngineArgv,taskKeys,unregisteredTabIds}from"./chunk-jvpzy378.js";import{tabPtyKeyFor}from"./chunk-y2yq6zca.js";import{ComposerBusyError,awaitEngineProcess,buildEngineSessionLaunch,hostedSessionFailureLine,readPersistedTerminalDefaultColors,sessionHasEngine,trustEngineWorktree}from"./chunk-adez6hq4.js";import{GENERIC_PROTOCOL,describePreset,engineLaunchArgv,kobeApiInvocation,listEnginePresets,protocolEntry,resolveCommandProtocol,sessionProtocol,withPinnedSessionId}from"./chunk-q7dr9eh6.js";import{resolveLoginShell}from"./chunk-b99tf0jr.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-gbasmvsc.js";import{engineEntry,supportsStructuredHistory,vendorsWithTurnReader}from"./chunk-yhcz2d2r.js";import{installedEngineIds}from"./chunk-df7qe9y7.js";import{isRemoteRepoKey}from"./chunk-k3ksts6g.js";import{loadStateFile}from"./chunk-gjf604bb.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{homeDir}from"./chunk-6dn6h640.js";import{ALL_VENDORS,DEFAULT_VENDOR,coerceVendorId}from"./chunk-h82kzdsn.js";import{activeCliName}from"./chunk-ef11h2am.js";import{terminalRows}from"./chunk-rxvbm4vt.js";import{__require}from"./chunk-wqfp8d9w.js";import{readFileSync}from"fs";import{resolve}from"path";var FANOUT_CAP=10;function parsePositiveInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>0?n:void 0}function parseNonNegativeInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>=0?n:void 0}function parseBoolLiteral(raw){if(["true","1","yes"].includes(raw))return!0;if(["false","0","no"].includes(raw))return!1;return}var FANOUT_STEP=helpStep("add"),F={repo:(required=!0)=>({name:"repo",type:"string",required,placeholder:"PATH",description:"Repo root (git toplevel). Relative paths resolve against $PWD."}),taskId:(required=!0)=>({name:"task-id",type:"string",required,placeholder:"ID",description:"Target task id (from `list` / `add`)."}),vendor:()=>({name:"vendor",type:"enum",values:ALL_VENDORS,placeholder:"V",description:"Engine vendor for the task."}),command:()=>({name:"command",type:"string",placeholder:"CMD",description:"Engine launch command, verbatim \u2014 an engine id from `engine-list` (e.g. claude) or a full command line (e.g. 'codex --search'). Unvalidated: probe an unfamiliar engine's flags with `<cmd> --help` first. Omitted = the repo's default engine."}),title:()=>({name:"title",type:"string",placeholder:"T",description:"Human task title."}),prompt:(required,desc)=>({name:"prompt",type:"string",required,placeholder:"TEXT",description:required?`${desc} Required unless --prompt-file is given.`:desc}),promptFile:()=>({name:"prompt-file",type:"string",placeholder:"PATH",description:"Read the prompt from this file instead of --prompt (`-` = stdin). Use it whenever the text has backticks, $vars, or quotes you don't want the shell to touch. Exactly one of --prompt / --prompt-file."})};function parseFlags(argv,booleanFlags=new Set){let flags=new Map,pretty=!1,help=!1;for(let i=0;i<argv.length;i++){let arg=argv[i];if(!arg.startsWith("--")&&arg!=="-h")throw new ApiError(`unexpected positional arg: ${arg}`,"BAD_FLAG");if(arg==="-h"){help=!0;continue}let eq=arg.indexOf("=");if(eq!==-1){let key2=arg.slice(2,eq),value=arg.slice(eq+1);if(key2==="pretty")pretty=value!=="false"&&value!=="0";else if(key2==="help")help=value!=="false"&&value!=="0";else flags.set(key2,value);continue}let key=arg.slice(2);if(key==="pretty"){pretty=!0;continue}if(key==="help"){help=!0;continue}if(booleanFlags.has(key)){let next2=argv[i+1];if(next2!==void 0&&parseBoolLiteral(next2)!==void 0)flags.set(key,next2),i+=1;else flags.set(key,"true");continue}let next=argv[i+1];if(next===void 0||next.startsWith("--"))throw new ApiError(`flag --${key} requires a value`,"BAD_FLAG");flags.set(key,next),i+=1}return{flags,pretty,help}}function validateAgainstSpec(verb,flags){let known=new Set(verb.flags.map((f)=>f.name));for(let key of flags.keys())if(!known.has(key))throw new ApiError(`unknown flag --${key} for "${verb.name}"`,"BAD_FLAG",helpStep(verb.name));for(let f of verb.flags){let satisfied=flags.get(f.name)||f.name==="prompt"&&flags.get("prompt-file");if(f.required&&!satisfied)throw new ApiError(`--${f.name} is required for "${verb.name}"`,"MISSING_FLAG",helpStep(verb.name));if(f.type==="enum"&&f.values){let raw=flags.get(f.name);if(raw!==void 0&&!f.values.includes(raw)){if(f.name==="vendor"&&registeredEngineIds().includes(raw))continue;throw new ApiError(`--${f.name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(verb.name))}}if(f.type==="int"){let raw=flags.get(f.name);if(raw!==void 0&&parsePositiveInt(raw)===void 0)throw new ApiError(`--${f.name} must be a positive integer`,"BAD_FLAG")}if(f.type==="uint"){let raw=flags.get(f.name);if(raw!==void 0&&parseNonNegativeInt(raw)===void 0)throw new ApiError(`--${f.name} must be a non-negative integer`,"BAD_FLAG")}}}class VerbArgs{verb;flags;constructor(verb,flags){this.verb=verb;this.flags=flags}spec(name){let f=this.verb.flags.find((s)=>s.name===name);if(!f)throw Error(`internal: --${name} is not declared on verb "${this.verb.name}"`);return f}str(name){this.spec(name);let v=this.flags.get(name);return v&&v.length>0?v:void 0}present(name){return this.spec(name),this.flags.get(name)!==void 0}promptText(){if(this.promptMemo!==void 0)return this.promptMemo.value;let value=this.readPromptText();return this.promptMemo={value},value}promptMemo;readPromptText(){let inline=this.str("prompt"),file=this.str("prompt-file");if(inline!==void 0&&file!==void 0)throw new ApiError("pass --prompt or --prompt-file, not both","BAD_FLAG",helpStep(this.verb.name));if(file===void 0)return inline;let text=readFileSync(file==="-"?0:resolve(process.cwd(),expandTilde(file)),"utf8");if(text.trim().length===0)throw new ApiError(`--prompt-file ${file} is empty`,"BAD_FLAG");return text}require(name){let v=this.str(name);if(v===void 0)throw new ApiError(`--${name} is required`,"MISSING_FLAG");return v}enumOf(name){let f=this.spec(name),v=this.str(name);if(v===void 0)return;if(f.values&&!f.values.includes(v))throw new ApiError(`--${name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(this.verb.name));return v}requireEnum(name){return this.require(name),this.enumOf(name)}vendor(){let value=this.str("vendor");if(value===void 0)return;let builtins=this.spec("vendor").values??ALL_VENDORS;if(builtins.includes(value))return value;if(registeredEngineIds().includes(value))return value;throw new ApiError(`--vendor must be a built-in (${builtins.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",helpStep(this.verb.name))}bool(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let value=parseBoolLiteral(raw);if(value===void 0)throw new ApiError(`--${name} must be a boolean (true/false)`,"BAD_FLAG");return value}int(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parsePositiveInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a positive integer`,"BAD_FLAG");return n}nonNegativeInt(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parseNonNegativeInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a non-negative integer`,"BAD_FLAG");return n}path(name){let v=this.str(name);return v===void 0?void 0:resolve(process.cwd(),expandTilde(v))}requirePath(name){return resolve(process.cwd(),expandTilde(this.require(name)))}requireRepo(name){let raw=this.require(name);return isRemoteRepoKey(raw)?raw:resolve(process.cwd(),expandTilde(raw))}}function parseAgentsSpec(spec){let out=[];for(let part of spec.split(",")){let trimmed=part.trim();if(!trimmed)continue;let colon=trimmed.indexOf(":");if(colon===-1)throw new ApiError(`--agents entry "${trimmed}" must be engine:count`,"BAD_FLAG",FANOUT_STEP);let vendor=trimmed.slice(0,colon);if(!ALL_VENDORS.includes(vendor)&&!registeredEngineIds().includes(vendor))throw new ApiError(`--agents engine "${vendor}" must be a built-in (${ALL_VENDORS.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",FANOUT_STEP);let count=parsePositiveInt(trimmed.slice(colon+1));if(count===void 0)throw new ApiError(`--agents count for "${vendor}" must be a positive integer`,"BAD_FLAG",FANOUT_STEP);if(out.length+count>FANOUT_CAP)throw new ApiError(`--agents requests ${out.length+count} agents, exceeds the cap of ${FANOUT_CAP}`,"BAD_FLAG",FANOUT_STEP);for(let i=0;i<count;i++)out.push(vendor)}if(out.length===0)throw new ApiError('--agents specified no agents (e.g. "claude:2,codex:1")',"BAD_FLAG",FANOUT_STEP);return out}function buildCountPlan(count,vendor){if(count>FANOUT_CAP)throw new ApiError(`--count ${count} exceeds the parallel cap of ${FANOUT_CAP} \u2014 spawn in batches`,"BAD_FLAG",FANOUT_STEP);return Array(count).fill(vendor)}function daemonOf(ctx){if(!ctx.client)throw new ApiError("daemon required","BAD_DAEMON");return ctx.client}async function repoFilter(runtime,repoFlag,repos){let target=await runtime.resolveRepoRoot(repoFlag);if(!await runtime.isUsableRepo(target))throw new ApiError(`--repo ${repoFlag} is not a readable git repository (resolved to ${target}) \u2014 the project may have moved or been deleted; \`rove api list\` still shows its tasks`,"REPO_UNRESOLVABLE");let resolved=new Map,unresolvable=new Set;for(let repo of new Set(repos)){let root=await runtime.resolveRepoRoot(repo);if(resolved.set(repo,root),root!==target&&!await runtime.isUsableRepo(root))unresolvable.add(repo)}return{target,matches:(repo)=>resolved.get(repo)===target,unresolvableRepos:[...unresolvable].sort()}}async function simpleRpc(ctx,name,payload){return daemonOf(ctx).request(name,payload)}async function handlePtyList(){let[{KobeDaemonClient},{defaultPtyHostSocketPath}]=await Promise.all([import("./chunk-arf294te.js"),import("./chunk-ewdhz3dv.js")]),client=new KobeDaemonClient(defaultPtyHostSocketPath());try{return await client.connect(),await client.request("pty.list",{})}catch{return{sessions:null}}finally{client.close()}}async function listAllEnginePresets(){ensurePluginEnginesLoaded();let presets=[...listEnginePresets()],seen=new Set(presets.map((p)=>p.id));for(let id of await installedEngineIds())if(!seen.has(id))presets.push(describePreset(id));return presets}var ENGINE_LIST_VERB={name:"engine-list",group:"discover",summary:"List every engine Rove can launch \u2014 built-ins, registered presets, the shipped contrib engines whose CLI is on PATH (gemini, opencode, cursor, grok, droid, amp), and engines contributed by enabled plugins \u2014 each with its RAW launch command, exactly as it runs. Copy one into `add --command` / `send --tab new --command` verbatim, or edit its flags first. `protocol` is the adapter Rove speaks to it (history, trust, delivery); `generic` = none, which still runs fine but loses transcript reads. Returns { engines }.",flags:[],offline:!0,handler:async()=>({engines:await listAllEnginePresets()})};async function setCommand(ctx){let command=ctx.args.require("command"),vendor=resolveCommandProtocol(command);return await simpleRpc(ctx,"task.setCommand",{taskId:ctx.args.require("task-id"),command,vendor}),{ok:!0,command,protocol:vendor,...vendor===GENERIC_PROTOCOL?{generic:!0}:{}}}var SET_COMMAND_VERB={name:"set-command",group:"edit",summary:"Set a task's engine launch command (takes effect on the next session rebuild). The protocol Rove speaks to it is derived from the command \u2014 the result reports which one, `generic` when the command names no engine Rove knows.",flags:[F.taskId(),{...F.command(),required:!0}],handler:setCommand};function taskEngine(task){let command=task.command?.trim();if(command){let resolved=resolveCommandProtocol(command);if(resolved!==GENERIC_PROTOCOL)return resolved}return coerceVendorId(task.vendor)}async function setEffort(ctx){let taskId=ctx.args.require("task-id"),level=ctx.args.require("level").trim(),daemon=daemonOf(ctx),{task}=await daemon.request("task.get",{taskId}),engine=taskEngine(task),levels=engineEntry(engine).effortLevels??[];if(levels.length===0)throw new ApiError(`engine ${engine} declares no reasoning effort levels`,"BAD_EFFORT",{engine,hint:"Only engines with declared levels accept one (codex today). Check the task's engine with `get-task`.",nextCommandArgs:["api","get-task","--task-id",taskId]});if(!levels.includes(level))throw new ApiError(`engine ${engine} does not accept effort level ${JSON.stringify(level)} \u2014 it declares ${levels.join(", ")}`,"BAD_EFFORT",{engine,levels,hint:`Pass one of: ${levels.join(", ")}.`});return await simpleRpc(ctx,"task.setVendor",{taskId,vendor:engine,effort:level}),{ok:!0,taskId,engine,effort:level}}var SET_EFFORT_VERB={name:"set-effort",group:"edit",summary:"Set a task's reasoning effort level (takes effect on the next session rebuild). Rejected when the task's engine declares no levels, or does not declare THIS one \u2014 the error names the levels it does accept. Codex accepts none/low/medium/high/xhigh/max; claude has none.",flags:[F.taskId(),{name:"level",type:"string",required:!0,placeholder:"LEVEL",description:"Effort level the task's engine declares (codex: none, low, medium, high, xhigh, max)."}],handler:setEffort};var API_SCHEMA_VERSION=2,GLOBAL_FLAGS=[{name:"pretty",type:"bool",description:"Pretty-print stdout JSON."},{name:"help",type:"bool",description:"Show usage for the verb and exit."}];function displayValues(f){if(!f.values)return;if(f.name!=="vendor")return f.values;let registered=registeredEngineIds().filter((id)=>!f.values.includes(id));return registered.length>0?[...f.values,...registered]:f.values}function flagJson(f){let values=displayValues(f);return{name:f.name,type:f.type,required:f.required??!1,...values?{values}:{},...f.default!==void 0?{default:f.default}:{},...f.placeholder?{placeholder:f.placeholder}:{},description:f.description}}function verbSchema(v){return{name:v.name,group:v.group,summary:v.summary,offline:v.offline??!1,flags:v.flags.map(flagJson)}}function schemaIndex(){let cliName=activeCliName();return{apiVersion:API_SCHEMA_VERSION,kobeVersion:CURRENT_VERSION,hint:`Compact index. Drill into ONE verb: \`${cliName} api schema --verb <name>\` (or \`${cliName} api <verb> --help\`). One group: \`--group <g>\`. Whole spec: \`--all\`.`,groups:VERB_GROUPS,verbs:VERBS.map((v)=>({name:v.name,group:v.group,summary:v.summary})),globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES}}function groupSchema(group){let verbs=VERBS.filter((v)=>v.group===group);if(verbs.length===0)throw new ApiError(`unknown group: ${group}. Groups: ${Object.keys(VERB_GROUPS).join(", ")}`,"BAD_FLAG");return{group,verbs:verbs.map((v)=>({name:v.name,summary:v.summary}))}}function fullSchema(){return{apiVersion:API_SCHEMA_VERSION,kobeVersion:CURRENT_VERSION,output:{success:"one JSON object on stdout, newline-terminated, exit 0",error:'{"error":{"message","code"}} on stderr, exit != 0',pretty:"--pretty indents stdout JSON"},globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES,groups:VERB_GROUPS,verbs:VERBS.map(verbSchema)}}function flagSignature(verb){return verb.flags.map((f)=>{let meta=f.type==="enum"&&f.values?displayValues(f).join("|"):f.placeholder??(f.type==="bool"?"":"X"),core=meta?`--${f.name} ${meta}`:`--${f.name}`;return f.required?core:`[${core}]`}).join(" ")}function verbHelp(verb){let lines=[`${activeCliName()} api ${verb.name} ${flagSignature(verb)}`.trimEnd(),"",verb.summary,""],alias=Object.entries(VERB_ALIASES).find(([,canon])=>canon===verb.name)?.[0];if(alias)lines.push(`Alias: ${alias}`,"");if(verb.flags.length>0){lines.push("Flags:");for(let f of verb.flags){let req=f.required?" (required)":"",def=f.default!==void 0?` [default: ${f.default}]`:"",vals=f.type==="enum"&&f.values?` {${displayValues(f).join("|")}}`:"";lines.push(` --${f.name}${vals}${req}${def} ${f.description}`)}lines.push("")}return lines.push("Global: [--pretty] [--help]"),lines.join(`
3
+ `)}function apiUsage(){let cliName=activeCliName(),rows=VERBS.map((v)=>` ${v.name.padEnd(18)} ${v.summary}`);return[`usage: ${cliName} api <verb> [flags] [--pretty] [--help]`,"",`Explore the full surface (names, flags, types) with: ${cliName} api schema`,"","verbs:",...rows,"","Output is one JSON object on stdout (exit 0); errors are JSON on stderr (exit != 0)."].join(`
4
+ `)}var selfSessionOnce,identityWarning=null;async function realProbe(){let[{openPtyHost:openPtyHost2,listSessions:listSessions2},{psSnapshot:psSnapshot2}]=await Promise.all([import("./chunk-pbqwegjy.js"),import("./chunk-xbc3gcry.js")]);return{sessions:async()=>{let host=await openPtyHost2();if(!host)return[];try{return await listSessions2(host.rpc)}finally{host.close()}},ps:psSnapshot2,pid:process.pid}}async function verifiedSelfSession(env=process.env,probe){if(!probe&&selfSessionOnce)return selfSessionOnce;let run=resolveSelfSession(env,probe);return selfSessionOnce=run,run}async function resolveSelfSession(env,probe){let taskId=env.KOBE_TASK_ID;if(!taskId)return null;let tabId=env.KOBE_TAB_ID||"tab-1";try{let p=probe??await realProbe(),key=`${taskId}::${tabId}`,session=(await p.sessions()).find((s)=>s.key===key&&s.alive);if(session?.pid){let{hasAncestor,parsePsSnapshot:parsePsSnapshot2}=await import("./chunk-xbc3gcry.js");if(hasAncestor(parsePsSnapshot2(await p.ps()),p.pid,session.pid))return identityWarning=null,{taskId,tabId}}}catch{}return identityWarning=`$ROVE_TASK_ID/$KOBE_TASK_ID names task ${taskId} ${tabId}, but this process is not running inside that tab (an inherited env, not an identity) \u2014 dispatcher/peer provenance omitted`,null}function takeIdentityWarning(){let warning=identityWarning;return identityWarning=null,warning}async function dispatcherEnvPayload(env=process.env,probe){let self=await verifiedSelfSession(env,probe);if(!self)return{};return{dispatcherTaskId:self.taskId,dispatcherTabId:self.tabId}}async function readOwnDispatcher(daemon){let self=await verifiedSelfSession();if(!self)return null;try{return(await daemon.request("task.get",{taskId:self.taskId})).task.dispatcher??null}catch{return null}}async function resolveDispatcherTab(runtime,dispatcher){let{tabs,running}=await runtime.taskTabs(dispatcher.taskId);if(tabs.some((t)=>t.id===dispatcher.tabId&&t.alive))return dispatcher.tabId;if(running)return;throw new ApiError(`dispatcher tab ${dispatcher.tabId} on task ${dispatcher.taskId} is dead and the task has no live engine tab \u2014 the reply has nowhere to land`,"DISPATCHER_UNREACHABLE",{dispatcher,hint:`address an alive target explicitly with --task-id/--tab (see \`${activeCliName()} api pty-list\`), or notify the user with \`${activeCliName()} api notify\``,nextCommandArgs:["api","pty-list"]})}async function withPeerProvenance(daemon,targetTaskId,prompt){let self=await verifiedSelfSession(),senderId=self?.taskId;if(!senderId||senderId===targetTaskId)return prompt;let label=senderId;try{let res=await daemon.request("task.get",{taskId:senderId});label=res.task.title||res.task.branch||senderId}catch{}let api=kobeApiInvocation(),replyTarget=`--task-id ${senderId} --tab ${self.tabId}`;return`[ROVE PEER] from "${label}" (task ${senderId} \u2014 load the Rove agent skill FIRST (registered as /rove; legacy /kobe installs still work), then reply: \`${api} send ${replyTarget} --prompt "<text>"\`; verb reference: \`${api} schema\`)
5
+
6
+ ${prompt}`}async function deliverToExactTab(rpc,taskId,tabId,cwd,prompt,opts){let key=`${taskId}::${tabId}`,{sessions=[]}=await rpc.request("pty.list",{}),session=sessions.find((s)=>s.key===key);if(!session?.alive){if(session?.restored===!0){if(!opts?.respawn)throw restoredTabError(taskId,tabId,prompt);let launch=opts.respawn(),open=await rpc.request("pty.open",{key,cwd,...launch?{command:launch.command}:{},defaultColors:readPersistedTerminalDefaultColors()});if(open.respawned!==!0&&open.alive!==!0)throw new ApiError(`tab ${tabId} on task ${taskId} could not be respawned`,"SESSION_FAILED",{hint:"the frozen record is still on disk; open the task in the TUI to see the session's own output",nextCommandArgs:["api","read-output","--task-id",taskId,"--tab",tabId,"--source","terminal"]});if(await awaitEngineProcess(rpc,key,opts.engineBin,{timeoutMs:ENGINE_START_PROBE_MS,intervalMs:ENGINE_START_POLL_MS,snapshot:opts.snapshot})===null)return{session:key,pane:key,started:!1,respawned:!0,engineReady:!1,delivered:!1,reason:await hostedSessionFailureLine(rpc,key)??ENGINE_NOT_OBSERVED_REASON};return await deliverRespawned(rpc,key,prompt,opts,taskId,tabId)}throw new ApiError(`tab ${tabId} has no live session on task ${taskId} \u2014 see \`rove api pty-list\` for alive tabs`,"TAB_NOT_FOUND")}if(!await sessionHasEngine(session.pid,opts?.engineBin,opts?.snapshot))throw new ApiError(`tab ${tabId} on task ${taskId} has no live engine process \u2014 it is a plain shell right now`,"ENGINE_NOT_RUNNING",{hint:"spawn a fresh engine tab for this prompt with --tab new, or pick an engine tab from pty-list",nextCommandArgs:["api","pty-list"]});let deliveryOpts={screenManifest:resolveComposerManifest(opts?.vendor)},outcome;try{outcome=await deliverToKey(rpc,key,prompt,deliveryOpts)}catch(err){if(err instanceof ComposerBusyError)return deferOrThrow(err,opts?.defer,taskId,tabId,prompt);throw err}return{session:key,pane:key,started:!1,...outcomeFields(outcome)}}async function deliverRespawned(rpc,key,prompt,opts,taskId,tabId){let outcome;try{outcome=await deliverToKey(rpc,key,prompt,{screenManifest:resolveComposerManifest(opts.vendor)})}catch(err){if(err instanceof ComposerBusyError)return deferOrThrow(err,opts.defer,taskId,tabId,prompt);throw err}return{session:key,pane:key,started:!1,respawned:!0,...outcomeFields(outcome)}}function restoredTabError(taskId,tabId,prompt){return new ApiError(`tab ${tabId} on task ${taskId} is a freeze-restored session \u2014 its pty host restarted, so the tab exists with its scrollback but nothing is running in it`,"TAB_RESTORED",{taskId,tabId,hint:"revive it with --respawn (resumes the tab's pinned conversation when it has one \u2014 `get-task` shows `sessionId`; a tab without one replays its recorded launch command)",nextCommandArgs:["api","send","--task-id",taskId,"--tab",tabId,"--respawn","--prompt",prompt]})}async function deliverHosted(target,worktree,prompt,defer){let host;try{host=await ensurePtyHost()}catch(error){throw new ApiError(`failed to start PTY host for ${target.id}: ${error instanceof Error?error.message:String(error)}`,"SESSION_FAILED")}try{if(target.tab&&target.tab!=="new"){let engineBin=engineLaunchArgv({command:target.command,vendor:target.vendor,effort:target.modelEffort})[0],tabId=target.tab;return await deliverToExactTab(host.rpc,target.id,tabId,worktree,prompt,{engineBin,vendor:target.vendor,defer,...target.respawn?{respawn:()=>restoredTabLaunch(target,tabId,worktree,process.env.SHELL?.trim()||"/bin/zsh")}:{}})}let newTab=target.tab==="new"?mintCliTab(target.id,target.tabVendor,target.tabCommand):void 0,launchVendor=target.tabVendor??target.vendor,launchCommand=target.tabCommand??(target.tabVendor?void 0:target.command),{argv,sessionId}=withPinnedSessionId(engineLaunchArgv({command:launchCommand,vendor:launchVendor,effort:target.modelEffort}),launchVendor);trustEngineWorktree(launchVendor,worktree);let launch=buildEngineSessionLaunch({task:{id:target.id,kind:target.kind,vendor:launchVendor,repo:target.repo},worktreePath:worktree,shell:process.env.SHELL?.trim()||"/bin/zsh",argv,promptIntent:target.newTask?{kind:"new-task",prompt}:{kind:"explicit",prompt},tabId:newTab}),result=await deliverHostedPrompt(host.rpc,{id:target.id,engineBin:argv[0]},worktree,prompt,launch,{forceNew:newTab!==void 0,vendor:launchVendor,defer});if(result.started&&!result.delivered&&!result.deferred)throw new ApiError(`failed to start hosted engine session for ${target.id}`,"SESSION_FAILED",{taskId:target.id,session:result.session,engineReady:result.engineReady,...result.reason?{reason:result.reason}:{},hint:"the session was created but no engine ran in it \u2014 fix the task's launch command (`api update --command`), then retry with `api send --tab new`",nextCommandArgs:["api","read-output","--task-id",target.id,"--source","terminal"]});if(!newTab)publishCliTabSnapshot(target.id,result.started?sessionId:void 0);if(result.started&&sessionId){let startedTab=newTab??result.session.split("::")[1];if(startedTab)markCliTabSession(target.id,startedTab,sessionId)}return result}catch(error){if(error instanceof ApiError)throw error;throw new ApiError(`hosted engine session failed for ${target.id}: ${error instanceof Error?error.message:String(error)}`,"SESSION_FAILED")}finally{host.close()}}var realPromptDeliveryOps={deliverHosted:(target,worktree,prompt,defer)=>deliverHosted(target,worktree,prompt,defer)};async function closeHeadlessTerminalTab(taskId,tabId){let host=await openPtyHost();try{let sessions=host?await listSessions(host.rpc):[],saved=readTabsSnapshot(taskId)?.tabs.find((tab)=>tab.id===tabId),directKey=`${taskId}::${tabId}`,unregisteredAlive=sessions.some((session)=>session.key===directKey&&session.alive);if(!saved&&!unregisteredAlive)throw new ApiError(`tab ${tabId} does not exist on task ${taskId}`,"TAB_NOT_FOUND",{hint:"refresh the task's tab ids with get-task, then retry with one of its .tabs[].id values",nextCommandArgs:["api","get-task","--task-id",taskId]});let closing=saved?closeTabsSnapshot(taskId,tabId):void 0;if(saved&&!closing)throw new ApiError(`tab ${tabId} no longer exists on task ${taskId}`,"TAB_NOT_FOUND",{hint:"the tab closed while this command was running; refresh with get-task before retrying",nextCommandArgs:["api","get-task","--task-id",taskId]});let baseKey=closing?tabPtyKeyFor(taskId,closing):directKey,keys=!(closing?.kind==="engine"&&closing.ptyTask)?sessions.filter((session)=>session.key===baseKey||session.key.startsWith(`${baseKey}::`)).map((session)=>session.key):[],wasAlive=sessions.some((session)=>keys.includes(session.key)&&session.alive);if(host)await killTaskSessions(host.rpc,keys);return{kind:closing?.kind??"engine",wasAlive}}finally{host?.close()}}async function deliverPrompt(client,target,prompt,ops=realPromptDeliveryOps){let worktree=target.worktreePath;if(!worktree)worktree=(await client.request("task.ensureWorktree",{taskId:target.id})).worktreePath;if(!worktree)throw new ApiError(`task ${target.id} has no worktree`,"NO_WORKTREE");if(target.newTask)await client.request("task.observeLanguage",{taskId:target.id,text:prompt}).catch(()=>{});let defer={defer:async(info)=>{let result=await client.request("deferredPrompt.fileIfVacant",info);if(!result||typeof result!=="object"||Array.isArray(result)||!("kind"in result)||!("id"in result))throw Error("invalid deferredPrompt.fileIfVacant response");let{kind,id}=result;if(kind!=="filed"&&kind!=="occupied"||typeof id!=="string"||id.length===0)throw Error("invalid deferredPrompt.fileIfVacant response");let expiresAt="expiresAt"in result?result.expiresAt:void 0;return{kind,id,...typeof expiresAt==="string"?{expiresAt}:{}}}},hosted=await ops.deliverHosted(target,worktree,prompt,defer);if(!hosted)throw new ApiError(`failed to start hosted engine session for ${target.id}`,"SESSION_FAILED");return hosted}var defaultApiRuntime={isTaskRunning:async(taskId,engineArgv)=>(await defaultApiRuntime.taskTabs(taskId,engineArgv)).running,taskTabs:async(taskId,engineArgv)=>{let listed=null,host=await openPtyHost();if(host)try{listed=await listSessionsOrNull(host.rpc)}finally{host.close()}let sessions=listed??[],hostReachable=listed!==null,liveVendors,engineAlive;try{let{engineProcessIn,foregroundEngineIn:foregroundEngineIn2,parsePsSnapshot:parsePsSnapshot2,psSnapshot:psSnapshot2}=await import("./chunk-xbc3gcry.js"),walkable=sessions.filter((s)=>s.alive&&typeof s.pid==="number"&&s.pid>0);if(walkable.length>0){let rows=parsePsSnapshot2(await psSnapshot2());liveVendors=new Map(walkable.map((s)=>[s.key,foregroundEngineIn2(rows,s.pid)?.vendor??null])),engineAlive=new Map(walkable.map((s)=>[s.key,engineProcessIn(rows,s.pid,engineArgv)]))}else engineAlive=new Map}catch{}let exits={};try{exits=(await import("./chunk-5c3d1y0r.js")).readPtyExitRecords()}catch{}let snapshot=readTabsSnapshot(taskId),sessionIds=new Map((snapshot?.tabs??[]).map((t)=>[t.id,t.sessionId??void 0]));return{tabs:joinTaskTabs(snapshot,taskId,listed,exits,liveVendors,engineAlive).map((row)=>{let sessionId=sessionIds.get(row.id);return sessionId?{...row,sessionId}:row}),running:hostReachable?hasLiveEngineTab(snapshot,taskId,sessions,engineAlive):null}},closeTerminalTab:closeHeadlessTerminalTab,deliverPrompt:(client,target,prompt)=>deliverPrompt(client,target,prompt),resolveRepoRoot:async(absPath)=>(await import("./chunk-mfqjdpd8.js")).resolveMainRepoRoot(absPath),isUsableRepo:async(absPath)=>{let{isGitRepo,isRemoteRepoKey:isRemoteRepoKey2}=await import("./chunk-mfqjdpd8.js");return isRemoteRepoKey2(absPath)||isGitRepo(absPath)},defaultVendor:async(repo)=>{let{getGlobalDefaultVendor,getRepoLastActiveVendor}=await import("./chunk-awsawfrm.js");return(repo?getRepoLastActiveVendor(repo):void 0)??getGlobalDefaultVendor()},readWorktreeChanges:async(worktreePath)=>(await import("./chunk-6frfschk.js")).readWorktreeChanges(worktreePath),readBranchSignals:async(worktreePath,recordedBaseRef)=>(await import("./chunk-j4xfsn9g.js")).readBranchSignals(worktreePath,recordedBaseRef),tearDownSession:async(taskId)=>{let host=await openPtyHost();if(host)try{await killTaskSessions(host.rpc,taskKeys(await listSessions(host.rpc),taskId))}catch{}finally{host.close()}}};async function issueUpdate(ctx){let title=ctx.args.str("title"),body=ctx.args.str("body"),task=ctx.args.str("task");if(title===void 0&&body===void 0&&task===void 0)throw new ApiError("issue-update requires --title, --body, and/or --task","MISSING_FLAG");let repoRoot=ctx.args.requireRepo("repo"),id=ctx.args.int("id");return simpleRpc(ctx,"issue.mutate",{repoRoot,op:{type:"update",id,title,body,...task===void 0?{}:{taskId:task==="none"?null:task}}})}async function assertNotEmptySuccess(daemon,ctx,prompt){if(ctx.args.bool("allow-empty"))return;if(!/^\s*succeeded\s*[:\uff1a]/i.test(prompt))return;let self=await verifiedSelfSession();if(!self)return;let sender;try{sender=(await daemon.request("task.get",{taskId:self.taskId})).task}catch{return}if(sender.kind==="main"||sender.kind==="dir")return;if(!sender.worktreePath)return;let ahead;try{ahead=(await ctx.runtime.readBranchSignals(sender.worktreePath,sender.baseRef)).ahead}catch{return}if(ahead!==0)return;let branch=sender.branch||"your branch";throw new ApiError(`refusing to report success: ${branch} has 0 commits \u2014 "succeeded" means COMMITTED, and this report would reach the coordinator as a clean success with nothing to land`,"EMPTY_SUCCESS_REPORT",{taskId:self.taskId,branch,hint:"commit your work with a real message and send again \u2014 or, if this task genuinely produced no commits (an investigation or a review), re-send with --allow-empty to say so explicitly",nextCommandArgs:["api","send",...ctx.args.str("task-id")?["--task-id",ctx.args.str("task-id")]:[],...ctx.args.str("tab")?["--tab",ctx.args.str("tab")]:[],"--allow-empty","--prompt",prompt]})}function requirePromptText(ctx,verb){let text=ctx.args.promptText();if(text===void 0)throw new ApiError("--prompt (or --prompt-file) is required","MISSING_FLAG",helpStep(verb));return text}async function send(ctx){let daemon=daemonOf(ctx),prompt=requirePromptText(ctx,"send"),tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let tabCommand=ctx.args.str("command");if(tabCommand&&tab!=="new")throw new ApiError(`--command only applies to a new tab; pass --tab new (got --tab ${tab??"<canonical>"})`,"BAD_FLAG",helpStep("send"));let respawn=ctx.args.bool("respawn");if(respawn&&(tab===void 0||tab==="new"))throw new ApiError(`--respawn addresses one frozen tab; pass --tab tab-N (got --tab ${tab??"<canonical>"})`,"BAD_FLAG",helpStep("send"));let tabVendor=tabCommand?resolveCommandProtocol(tabCommand):void 0,taskId=ctx.args.str("task-id");if(!taskId){let dispatcher=await readOwnDispatcher(daemon);if(dispatcher){if(taskId=dispatcher.taskId,tab===void 0)tab=await resolveDispatcherTab(ctx.runtime,dispatcher)}else{let active=await resolveActiveTaskId(daemon);if(!active)throw new ApiError("no --task-id given and no active task \u2014 open a task first or pass --task-id","MISSING_TARGET");taskId=active}}let res=await daemon.request("task.get",{taskId});await assertNotEmptySuccess(daemon,ctx,prompt);let text=ctx.args.bool("plain")?prompt:await withPeerProvenance(daemon,taskId,prompt),delivered=await ctx.runtime.deliverPrompt(daemon,{id:taskId,worktreePath:res.task.worktreePath,kind:res.task.kind,vendor:res.task.vendor,command:res.task.command,modelEffort:tabCommand?void 0:res.task.modelEffort,repo:res.task.repo,tab,tabVendor,tabCommand,respawn},text);if(!delivered.delivered&&!delivered.deferred)throw new ApiError(`prompt was not confirmed in ${taskId}'s engine (paste did not land)`,"NOT_DELIVERED");return{ok:!0,taskId,session:delivered.session,started:delivered.started,engineReady:delivered.engineReady,delivered:delivered.delivered,...delivered.bytes===void 0?{}:{bytes:delivered.bytes},...delivered.promptEcho?{promptEcho:delivered.promptEcho}:{},...delivered.reason?{reason:delivered.reason}:{},...delivered.deferred?{deferred:delivered.deferred}:{},...delivered.respawned?{respawned:!0}:{},...delivered.frozenTabs?.length?{frozenTabs:delivered.frozenTabs}:{}}}async function dispatch(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.require("task-id"),text=requirePromptText(ctx,"dispatch"),tabId=ctx.args.str("tab"),reply=await daemon.request("session.deliver",{taskId,text,...tabId!==void 0?{tabId}:{},source:"dispatcher"});return{ok:!0,taskId,...reply?.tabId!==void 0?{tabId:reply.tabId}:tabId!==void 0?{tabId}:{},routed:"session.deliver",...reply?.delivered!==void 0?{delivered:reply.delivered}:{},...reply?.reason!==void 0?{reason:reply.reason}:{},...reply?.layer!==void 0?{layer:reply.layer}:{},...reply?.clients!==void 0?{clients:reply.clients}:{}}}var DISPATCH_VERB={name:"dispatch",group:"drive",summary:'Route text into a task\'s live session. The dispatcher\'s messenger (docs/design/dispatcher.md); unlike `send`, it never starts an engine \u2014 it requires an already-hosted session. The daemon pastes into it and reports `delivered`; `delivered:false` with `reason:"busy"` means a human is mid-message, and `reason:"broadcast"` means no hosted session answered and the text went out on the session.deliver channel for a browser to pick up (unconfirmable; `clients: 0` proves it reached nobody).',flags:[F.taskId(!0),F.prompt(!0,"Text delivered into the task's engine session."),F.promptFile(),{name:"tab",type:"string",required:!1,placeholder:"TAB",description:"Deliver into exactly this tab (e.g. tab-3) instead of the canonical engine tab."}],handler:dispatch};async function note(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.require("task-id"),text=ctx.args.require("text");return await daemon.request("note.file",{taskId,text})}async function getTask(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.require("task-id"),res=await daemon.request("task.get",{taskId}),{tabs,running}=await ctx.runtime.taskTabs(taskId,taskEngineArgv(res.task));return{task:res.task,running,tabs}}async function list(ctx){return daemonOf(ctx).request("task.list")}async function setActive(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.bool("none")?null:ctx.args.require("task-id");return await daemon.request("task.setActive",{taskId}),{ok:!0,activeTaskId:taskId}}var SCHEDULE_FLAG={name:"schedule",type:"string",placeholder:"CRON",description:"Five-field cron in the daemon host's local time, e.g. '0 9 * * MON-FRI'."},PRECHECK_FLAGS=[{name:"precheck",type:"string",placeholder:"CMD",description:"Shell command run in the repo BEFORE the engine starts. Non-zero exit skips the run without spawning an agent \u2014 the cheap way to avoid burning a turn when nothing changed."},{name:"precheck-timeout",type:"int",placeholder:"SEC",default:"120",description:"Seconds before the precheck is killed and the run skipped."}],GRACE_FLAG={name:"grace",type:"uint",placeholder:"MIN",default:"60",description:"How late a missed occurrence may still run when the daemon was down. Only the most recent missed occurrence is ever run."},PERSISTENT_FLAG={name:"persistent-session",type:"bool",description:"Re-deliver into ONE standing task instead of a fresh worktree per run \u2014 for a routine that needs yesterday's context (a trend check). Its task is folded behind the sidebar's routine count row. Leave off for a routine that EDITS code: a week of runs on one branch is a branch nobody can land."};function precheckPayload(ctx){if(!ctx.args.present("precheck")){if(ctx.args.present("precheck-timeout"))throw new ApiError("--precheck-timeout requires --precheck (pass the command again to change its timeout)","BAD_FLAG");return{}}let command=ctx.args.str("precheck");if(command===void 0)return{precheck:null};return{precheck:{command,timeoutSeconds:ctx.args.int("precheck-timeout")??120}}}var ROUTINE_VERBS=[{name:"routine-list",group:"routine",summary:"List scheduled routines with their next run time.",flags:[],handler:(ctx)=>simpleRpc(ctx,"automation.list",{})},{name:"routine-create",group:"routine",summary:"Schedule a prompt. Each firing creates a fresh task (worktree + engine) and delivers it. An enabled routine keeps the daemon alive so it fires with no TUI attached.",flags:[F.repo(),{name:"name",type:"string",required:!0,placeholder:"N",description:"Routine name."},F.prompt(!0,"Text delivered as the new session's first message."),F.promptFile(),{...SCHEDULE_FLAG,required:!0},F.vendor(),{name:"base-branch",type:"string",placeholder:"B",description:"Base ref each run's worktree branches from."},...PRECHECK_FLAGS,GRACE_FLAG,PERSISTENT_FLAG,{name:"disabled",type:"bool",description:"Create it paused instead of active."}],handler:(ctx)=>simpleRpc(ctx,"automation.create",{repo:ctx.args.requireRepo("repo"),name:ctx.args.require("name"),prompt:requirePromptText(ctx,"routine-create"),schedule:ctx.args.require("schedule"),...ctx.args.vendor()?{vendor:ctx.args.vendor()}:{},...ctx.args.str("base-branch")?{baseRef:ctx.args.str("base-branch")}:{},...precheckPayload(ctx),...ctx.args.nonNegativeInt("grace")!==void 0?{missedRunGraceMinutes:ctx.args.nonNegativeInt("grace")}:{},...ctx.args.bool("persistent-session")?{persistentSession:!0}:{},...ctx.args.bool("disabled")?{enabled:!1}:{}})},{name:"routine-update",group:"routine",summary:"Change a routine. A new --schedule re-anchors its next run; --precheck '' clears the precheck.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Routine id."},{name:"name",type:"string",placeholder:"N",description:"New name."},F.prompt(!1,"New prompt."),F.promptFile(),SCHEDULE_FLAG,F.vendor(),{name:"base-branch",type:"string",placeholder:"B",description:"New base ref ('' to clear)."},...PRECHECK_FLAGS,GRACE_FLAG,PERSISTENT_FLAG],handler:(ctx)=>simpleRpc(ctx,"automation.update",{id:ctx.args.require("id"),...ctx.args.str("name")!==void 0?{name:ctx.args.str("name")}:{},...ctx.args.promptText()!==void 0?{prompt:ctx.args.promptText()}:{},...ctx.args.str("schedule")!==void 0?{schedule:ctx.args.str("schedule")}:{},...ctx.args.vendor()?{vendor:ctx.args.vendor()}:{},...ctx.args.present("base-branch")?{baseRef:ctx.args.str("base-branch")??null}:{},...precheckPayload(ctx),...ctx.args.nonNegativeInt("grace")!==void 0?{missedRunGraceMinutes:ctx.args.nonNegativeInt("grace")}:{},...ctx.args.present("persistent-session")?{persistentSession:ctx.args.bool("persistent-session")??!0}:{}})},{name:"routine-set-enabled",group:"routine",summary:"Pause or resume a routine. Disabling the last active one releases the daemon's keep-alive hold.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Routine id."},{name:"enabled",type:"bool",required:!0,description:"true to resume, false to pause."}],handler:(ctx)=>simpleRpc(ctx,"automation.update",{id:ctx.args.require("id"),enabled:ctx.args.bool("enabled")})},{name:"routine-delete",group:"routine",summary:"Delete a routine and its run history. Tasks it already created are untouched.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Routine id."}],handler:(ctx)=>simpleRpc(ctx,"automation.delete",{id:ctx.args.require("id")})},{name:"routine-run-now",group:"routine",summary:"Run a routine immediately, skipping its precheck (asking for it IS the answer). Does not shift its schedule.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Routine id."}],handler:(ctx)=>simpleRpc(ctx,"automation.runNow",{id:ctx.args.require("id")})},{name:"routine-runs",group:"routine",summary:"Run history, newest first. Statuses: dispatched, revived (standing session respawned \u2014 files kept, conversation did not), deferred (composer busy; the prompt is queued in the Inbox, NOT lost), skipped_precheck (nothing to do), skipped_missed, skipped_unavailable, dispatch_failed.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Routine id."}],handler:(ctx)=>simpleRpc(ctx,"automation.runs",{id:ctx.args.require("id")})}];async function engineChoice(ctx,repo){let command=ctx.args.str("command");if(command)return{command,vendor:resolveCommandProtocol(command)};let fallback=await ctx.runtime.defaultVendor(repo);return fallback?{command:fallback,vendor:fallback}:{}}function enginePayload(choice){return{...choice.command?{command:choice.command}:{},...choice.vendor?{vendor:choice.vendor}:{}}}async function applyPostCreateFlags(daemon,taskId,args){let status=args.enumOf("status");if(status)await daemon.request("task.status",{taskId,status});let pin=args.bool("pin");if(pin!==void 0)await daemon.request("task.pin",{taskId,pinned:pin});return Boolean(status)||pin!==void 0}async function add(ctx){let{args,runtime}=ctx,repo=await runtime.resolveRepoRoot(args.requireRepo("repo"));if(!await runtime.isUsableRepo(repo))throw new ApiError(`--repo ${repo} is not a git repository \u2014 a task needs one to cut its worktree and branch from (run \`git init\` there, or point --repo at a checkout)`,"NOT_A_REPO",helpStep("add"));let count=args.int("count"),agentsSpec=args.str("agents");if(count!==void 0||agentsSpec)return addParallel(ctx,repo,count,agentsSpec);return addOne(ctx,repo)}async function addOne(ctx,repo){let daemon=daemonOf(ctx),{args}=ctx,prompt=args.promptText(),choice=await engineChoice(ctx,repo),payload={repo,...await dispatcherEnvPayload(),...enginePayload(choice)},title=args.str("title")||(prompt?deriveTitleFromPrompt(prompt):"");if(title)payload.title=title;let branch=args.str("branch");if(branch)payload.branch=branch;let baseRef=args.str("base-branch");if(baseRef)payload.baseRef=baseRef;let res=await daemon.request("task.create",payload),taskId=res.taskId;if(args.bool("activate"))await daemon.request("task.setActive",{taskId});let task=res.task;if(await applyPostCreateFlags(daemon,taskId,args))task=(await daemon.request("task.get",{taskId})).task;if(!prompt)return{taskId,task,home:homeDir(),started:!1};let brief=await withPeerProvenance(daemon,taskId,prompt),delivered=await ctx.runtime.deliverPrompt(daemon,{id:taskId,worktreePath:task.worktreePath,kind:task.kind,vendor:task.vendor,command:task.command,modelEffort:task.modelEffort,repo:task.repo,newTask:!0},brief);if(!delivered.delivered&&!delivered.deferred)throw new ApiError(`task ${taskId} created but the prompt was not delivered (paste did not land)`,"NOT_DELIVERED",{taskId});let promptPersisted=await persistPrompt(daemon,taskId,prompt);return task=(await daemon.request("task.get",{taskId})).task,{taskId,task,home:homeDir(),started:delivered.started,engineReady:delivered.engineReady,session:delivered.session,delivered:delivered.delivered,...delivered.bytes===void 0?{}:{bytes:delivered.bytes},...delivered.promptEcho?{promptEcho:delivered.promptEcho}:{},...delivered.reason?{reason:delivered.reason}:{},...delivered.deferred?{deferred:delivered.deferred}:{},...promptPersisted?{}:{promptPersisted:!1}}}async function persistPrompt(daemon,taskId,prompt){try{return await daemon.request("task.setPrompt",{taskId,prompt}),!0}catch(err){return console.error(`[rove api add] task.setPrompt failed for ${taskId} \u2014 no "Run again" for this task:`,err),!1}}async function addParallel(ctx,repo,count,agentsSpec){let daemon=daemonOf(ctx),{args}=ctx,prompt=args.promptText();if(!prompt)throw new ApiError("--count/--agents spawn parallel attempts of ONE prompt \u2014 pass --prompt","MISSING_FLAG",helpStep("add"));if(args.str("branch"))throw new ApiError("--branch names ONE branch and cannot be shared by parallel siblings \u2014 drop it (each sibling gets its own auto branch) or spawn them one at a time","BAD_FLAG",helpStep("add"));if(agentsSpec){let conflict=count!==void 0?"--count":args.str("command")?"--command":null;if(conflict)throw new ApiError(`${conflict} conflicts with --agents, which already names each sibling's engine and how many \u2014 pass one or the other`,"BAD_FLAG",helpStep("add"))}let title=args.str("title")||deriveTitleFromPrompt(prompt),baseRef=args.str("base-branch"),choice=await engineChoice(ctx,repo),plan=agentsSpec?parseAgentsSpec(agentsSpec):buildCountPlan(count??1,choice.vendor??DEFAULT_VENDOR);if(plan.length>FANOUT_CAP)throw new ApiError(`a parallel round of ${plan.length} exceeds the cap of ${FANOUT_CAP} \u2014 spawn in batches`,"BAD_FLAG");let groupId=ulid(),created=[],createFailure=null,dispatcher=await dispatcherEnvPayload();for(let[i,vendor]of plan.entries()){let engine=agentsSpec?{command:vendor,vendor}:{...choice,vendor},payload={repo,groupId,...dispatcher,...enginePayload(engine)};if(title)payload.title=plan.length>1?`${title} #${i+1}/${plan.length}`:title;if(baseRef)payload.baseRef=baseRef;try{let res=await daemon.request("task.create",payload);created.push({taskId:res.taskId,vendor,task:res.task})}catch(err){let code=err instanceof ApiError?err.code:"CREATE_FAILED",message=err instanceof Error?err.message:String(err);createFailure={vendor,error:{message,code}};break}}for(let{taskId}of created)await applyPostCreateFlags(daemon,taskId,args);let settled=await Promise.allSettled(created.map(({taskId,vendor,task})=>ctx.runtime.deliverPrompt(daemon,{id:taskId,worktreePath:task.worktreePath,kind:task.kind,vendor,command:task.command,modelEffort:task.modelEffort,repo:task.repo,newTask:!0},prompt))),tasks=[],failures=[],persistedPrompts=[];if(settled.forEach((r,i)=>{let{taskId,vendor}=created[i];if(r.status==="fulfilled"&&(r.value.delivered||r.value.deferred)){let row={ok:!0,taskId,vendor,started:r.value.started,engineReady:r.value.engineReady,session:r.value.session,...r.value.reason?{reason:r.value.reason}:{},...r.value.deferred?{deferred:r.value.deferred}:{}};tasks.push(row),persistedPrompts.push(persistPrompt(daemon,taskId,prompt).then((ok)=>{if(!ok)row.promptPersisted=!1}));return}let err=r.status==="rejected"?r.reason:new ApiError(`prompt was not confirmed in ${taskId}'s engine`,"NOT_DELIVERED"),code=err instanceof ApiError?err.code:"DELIVER_FAILED",message=err instanceof Error?err.message:String(err);failures.push({ok:!1,taskId,vendor,error:{message,code}})}),createFailure)failures.push({ok:!1,vendor:createFailure.vendor,error:createFailure.error});await Promise.all(persistedPrompts);let result={count:created.length,requested:plan.length,groupId,home:homeDir(),tasks,failures};if(failures.length>0)throw new ApiError(`add delivered ${tasks.length}/${plan.length}`,"PARTIAL_FANOUT",result);return result}var TASK_STATUSES=["backlog","in_progress","in_review","done","canceled","error"];var CREATE_VERBS=[{name:"add",group:"create",summary:`Create a task (shows in the sidebar immediately). With --prompt it also starts the engine and delivers it. PARALLEL ATTEMPTS: --count N spawns N sibling tasks of the SAME prompt, each in its own worktree/branch (--agents claude:2,codex:1 for a mixed fleet); capped at ${FANOUT_CAP}, prefer 3-4. Does NOT steal focus \u2014 pass --activate to make it the active task. Alias: spawn-task.`,flags:[F.repo(),F.title(),{name:"branch",type:"string",placeholder:"B",description:"Explicit branch name (else derived from the title in the repo's own style). Single task only."},{name:"base-branch",type:"string",placeholder:"B",description:"Base ref the worktree branches from."},F.command(),{name:"count",type:"int",placeholder:"N",description:`Spawn N sibling tasks of one prompt (parallel attempts, cap ${FANOUT_CAP}). Requires --prompt.`},{name:"agents",type:"string",placeholder:"claude:2,codex:1",description:"Per-ENGINE counts for a mixed parallel round (alternative to --count). Engine ids only \u2014 see `engine-list`."},{name:"status",type:"enum",values:TASK_STATUSES,default:"backlog",description:"Initial lifecycle status."},{name:"pin",type:"bool",description:"Pin the task to the top of the sidebar."},{name:"activate",type:"bool",default:"false",description:"Make this the active task (pulls every mounted TUI's Tasks-pane focus). Off by default."},F.prompt(!1,"Optional first message \u2014 when set, materializes the worktree, starts the engine, and pastes it. Required with --count/--agents."),F.promptFile()],handler:add}];var LIST_STEP={hint:"no deferred prompt has that id \u2014 it was already released, dismissed, or swept past its 24h TTL; list what the daemon still holds",nextCommandArgs:["api","deferred-list"]};async function deferredList(ctx){let taskId=ctx.args.str("task-id"),{records}=await daemonOf(ctx).request("deferredPrompt.list",{});return{records:taskId?records.filter((record)=>record.taskId===taskId):records}}async function deferredRelease(ctx){let id=ctx.args.require("id"),report=await daemonOf(ctx).request("deferredPrompt.release",{id});if(report.kind==="missing")throw new ApiError(`no deferred prompt with id ${id}`,"DEFERRED_PROMPT_NOT_FOUND",{id,...LIST_STEP});if(report.kind==="in-flight")return{ok:!0,id,delivered:!1,reason:"in-flight"};let delivered=report.delivered.includes(id),retained=report.retained.find((row)=>row.id===id);return{ok:!0,id,delivered,...delivered?{}:{reason:retained?.reason??"unknown"},...retained?.layer?{layer:retained.layer}:{},...retained?.error?{error:retained.error}:{},...report.cleanupPending.length>0?{cleanupPending:report.cleanupPending}:{}}}var DEFERRED_VERBS=[{name:"deferred-list",group:"drive",summary:"Every prompt the daemon is holding because the target composer was busy when it arrived \u2014 the Inbox, for a caller with no screen. Each record carries its `id` (for deferred-release / deferred-dismiss), the verbatim `prompt`, the `layer` that blocked it, and `expiresAt`: the daemon sweeps a record 24h after it was filed, and a swept prompt is never delivered. Returns { records }.",flags:[F.taskId(!1)],handler:deferredList},{name:"deferred-release",group:"drive",summary:"Deliver one held prompt now (the Inbox's release action). Re-runs the delivery gate rather than bypassing it, so a composer that is still busy leaves the record held and reports `delivered:false` with the blocking `reason` \u2014 retry later. Returns { id, delivered, reason? }; an id the daemon no longer holds is DEFERRED_PROMPT_NOT_FOUND.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Record id from `deferred-list` (or the `deferred.id` a `send` returned)."}],handler:deferredRelease},{name:"deferred-dismiss",group:"drive",summary:"Drop one held prompt WITHOUT delivering it, and free its tab's deferred slot (the Inbox's dismiss action). The text is gone \u2014 dismiss a message that is no longer wanted, then send the replacement. Returns { dismissed }.",flags:[{name:"id",type:"string",required:!0,placeholder:"ID",description:"Record id from `deferred-list` (or the `deferred.id` a `send` returned)."}],handler:async(ctx)=>{let id=ctx.args.require("id"),result=await daemonOf(ctx).request("deferredPrompt.dismiss",{id});if(!result.dismissed)throw new ApiError(`no deferred prompt with id ${id}`,"DEFERRED_PROMPT_NOT_FOUND",{id,...LIST_STEP});return result}}];var PANE_VERB={name:"pane-open",group:"drive",summary:"Open a terminal pane in a task's workspace: split the focused tab (default, or --tab's tab) or open a separate command tab, optionally running a command. Broadcast over the daemon's tab.open channel \u2014 an attached TUI showing the task performs the split. Task defaults to $ROVE_TASK_ID, then the active task. Returns the resolved `title` (the label `pane-close --title` must match \u2014 derived from the command's first word when --title is omitted) plus `clients` (attached connections; 0 = nobody performed the split).",flags:[F.taskId(!1),{name:"tab",type:"string",placeholder:"TAB",description:"Host tab for the split (e.g. tab-3) instead of the focused tab (split placement only)."},{name:"command",type:"string",placeholder:"CMD",description:"Shell command the pane runs (via the login shell's `-ilc`, so pipes/args and your shell rc's PATH/exports work); the pane closes when it exits. Omit for an interactive shell."},{name:"direction",type:"enum",values:["right","down"],default:"right",description:"Split orientation relative to the active pane (split placement only)."},{name:"placement",type:"enum",values:["split","tab"],default:"split",description:"`split` joins the focused tab's split group; `tab` opens a separate command tab."},{name:"title",type:"string",placeholder:"TEXT",description:`Pane label (default: the command's first word, else "shell").`}],handler:async(ctx)=>{let client=daemonOf(ctx),taskId=ctx.args.str("task-id")??process.env.KOBE_TASK_ID??await resolveActiveTaskId(client);if(!taskId)throw new ApiError("no target task: pass --task-id (no $ROVE_TASK_ID, no active task)","TASK_NOT_FOUND");let command=ctx.args.str("command"),shell=resolveLoginShell({fallback:"/bin/sh"}),argv=command?[shell,"-ilc",command]:[shell,"-il"],title=ctx.args.str("title")??(command?command.trim().split(/\s+/)[0]??"shell":"shell"),tabId=ctx.args.str("tab");return{...await simpleRpc(ctx,"tab.open",{taskId,argv,title,...tabId!==void 0?{tabId}:{},placement:ctx.args.str("placement")??"split",direction:ctx.args.str("direction")??"right"}),title}}},PANE_CLOSE_VERB={name:"pane-close",group:"drive",summary:"Close panes opened by pane-open: every split pane / command tab in the task whose label matches --title. Broadcast over the daemon's tab.close channel \u2014 an attached TUI showing the task performs the close (headless no-op). Task defaults to $ROVE_TASK_ID, then the active task. Returns `clients` (attached connections; 0 = nobody performed the close).",flags:[F.taskId(!1),{name:"title",type:"string",required:!0,placeholder:"TEXT",description:"Pane label to close \u2014 the --title the pane was opened with (engine panes are never closed)."},{name:"tab",type:"string",placeholder:"TAB",description:"Scope the title match to one tab (e.g. tab-3) instead of every tab of the task."}],handler:async(ctx)=>{let client=daemonOf(ctx),taskId=ctx.args.str("task-id")??process.env.KOBE_TASK_ID??await resolveActiveTaskId(client);if(!taskId)throw new ApiError("no target task: pass --task-id (no $ROVE_TASK_ID, no active task)","TASK_NOT_FOUND");let tabId=ctx.args.str("tab");return simpleRpc(ctx,"tab.close",{taskId,title:ctx.args.str("title"),...tabId!==void 0?{tabId}:{}})}},TAB_CLOSE_VERB={name:"tab-close",group:"drive",summary:"Close one Terminal Tab by the id returned in get-task .tabs[]. Runs the same close path as ctrl+w when a TUI is attached; otherwise removes the persisted tab snapshot and ends its hosted PTYs directly. Engine, shell/command, and content tabs are all valid. Closing the last tab leaves the task open with no session.",flags:[F.taskId(!0),{name:"tab",type:"string",required:!0,placeholder:"TAB",description:"Exact Terminal Tab id from get-task .tabs[].id (for example tab-3)."}],handler:async(ctx)=>{let taskId=ctx.args.require("task-id"),tabId=ctx.args.require("tab");if((await daemonOf(ctx).request("terminalTab.close",{taskId,tabId})).handled)return{ok:!0,taskId,tabId,handledBy:"tui"};let result=await ctx.runtime.closeTerminalTab(taskId,tabId);return await daemonOf(ctx).request("deferredPrompt.discardTab",{taskId,tabId}),{ok:!0,taskId,tabId,handledBy:"headless",...result}}};var DRIVE_VERBS=[{name:"send",group:"drive",summary:"Paste a follow-up prompt into a task's running engine (one full turn). Without --task-id, a task spawned from another Rove session replies to its dispatcher's tab (then that task's live canonical engine; nothing alive = DISPATCHER_UNREACHABLE, never a silent spawn); otherwise the active task. Sent from inside another Rove task ($ROVE_TASK_ID), the prompt is prefixed with [ROVE PEER] provenance \u2014 who sent it and how to reply (tab-precise) \u2014 so agent-to-agent messaging needs no coordinator. When the target composer is busy (you'd paste into a half-typed message), the prompt is accepted-but-deferred: the daemon stores it and queues a `prompt_deferred` Inbox episode for a human to release \u2014 that outcome is a SUCCESS (exit 0, `deferred` in the JSON). Do NOT retry a deferred send: the daemon already owns the message. A later send to the same tab fails with DEFERRED_PROMPT_PENDING until the Inbox item is released, dismissed, or expires. A `succeeded:` report sent from a managed task whose branch has 0 commits is REFUSED (EMPTY_SUCCESS_REPORT) \u2014 commit first, or pass --allow-empty when the task genuinely produced no commits.",flags:[F.taskId(!1),F.prompt(!0,"Text pasted + submitted into the engine pane."),F.promptFile(),{name:"tab",type:"string",required:!1,placeholder:"TAB",description:'Tab addressing: "new" spawns the prompt in a fresh engine tab; "tab-N" delivers to that exact alive tab (error when dead/absent). Omitted = the canonical engine tab.'},{...F.command(),description:"Engine launch command for a `--tab new` tab \u2014 the API twin of the TUI's ctrl+e pick. Lets one worktree run two agents on the same files (e.g. hand the stuck work to codex without leaving the branch). An engine id from `engine-list` or a full command line; pinned to that tab, so it survives restarts and a later set-command on the task. Only valid with --tab new."},{name:"respawn",type:"bool",required:!1,description:"Revive a FREEZE-RESTORED --tab tab-N before delivering. After a pty-host restart (reboot, crash) a tab keeps its scrollback and launch command but nothing runs in it; without this flag such a tab is refused (TAB_RESTORED) rather than silently re-run. With it, the tab is respawned in place and resumes its pinned conversation when it has one (`get-task` shows each tab's sessionId) \u2014 a tab with none replays its recorded launch command, which for claude carries the task's original first prompt. Only valid with --tab tab-N."},{name:"plain",type:"bool",required:!1,description:"Deliver the prompt verbatim \u2014 skip the [ROVE PEER] provenance prefix."},{name:"allow-empty",type:"bool",required:!1,description:"Report success from a task with zero commits (EMPTY_SUCCESS_REPORT is refused otherwise). For work that legitimately produces no commits \u2014 an investigation, a review, a question answered."}],handler:send},DISPATCH_VERB,...DEFERRED_VERBS,{name:"note",group:"drive",summary:"File a one-line field note \u2014 a resolved, repo-level gotcha worth sharing. Appended to the repo's durable note store (every future session on this repo starts with it) and forwarded to the dispatcher session for live relay (docs/design/dispatcher.md).",flags:[F.taskId(!0),{name:"text",type:"string",required:!0,placeholder:"TEXT",description:"One line: the verified conclusion another session could act on."}],handler:note},{name:"note-list",group:"drive",summary:"Read a repo's accumulated field notes, newest first. Returns { notes }.",flags:[F.repo(!0)],handler:(ctx)=>simpleRpc(ctx,"note.list",{repo:ctx.args.requireRepo("repo")})},PANE_VERB,PANE_CLOSE_VERB,TAB_CLOSE_VERB,{name:"notify",group:"drive",summary:"Show a toast in every attached Rove UI \u2014 broadcast over the daemon's notice.event channel. Agents/scripts use it to surface 'done / needs input / error' moments without touching the task's session. Returns `clients` (attached connections; 0 = no UI showed the toast).",flags:[{name:"title",type:"string",required:!0,placeholder:"TEXT",description:"Toast text (one line)."},{name:"body",type:"string",placeholder:"TEXT",description:"Optional second line under the title \u2014 context, not a second message."},{name:"kind",type:"string",default:"done",placeholder:"KIND",description:`Free-form kind tag. "done", "needs_input" and "error" get the TUI's severity styling/unread mark; any other value renders neutrally.`},F.taskId(!1),{name:"source",type:"string",placeholder:"TAG",description:"Free-form origin tag (e.g. an agent name) recorded on the event."}],handler:async(ctx)=>{return simpleRpc(ctx,"notice.send",{title:ctx.args.str("title"),body:ctx.args.str("body"),kind:ctx.args.str("kind")??"done",taskId:ctx.args.str("task-id"),source:ctx.args.str("source")})}},{name:"prompt",group:"drive",summary:"Ask the human for a line of text through the attached TUI's input dialog (plugins' host-provided prompt). Blocks until answered, cancelled, or timed out; returns { value } or { cancelled, reason }.",flags:[{name:"title",type:"string",required:!0,placeholder:"TEXT",description:"Dialog title (shown verbatim)."},{name:"placeholder",type:"string",placeholder:"TEXT",description:"Input placeholder."},{name:"initial",type:"string",placeholder:"TEXT",description:"Pre-filled input value."},{name:"timeout",type:"string",placeholder:"MS",description:"Give up after this many milliseconds (default 120000, max 600000)."}],handler:async(ctx)=>{let timeoutRaw=ctx.args.str("timeout"),timeoutMs=timeoutRaw?Number.parseInt(timeoutRaw,10):void 0;return simpleRpc(ctx,"ui.prompt",{title:ctx.args.str("title"),placeholder:ctx.args.str("placeholder"),initial:ctx.args.str("initial"),...timeoutMs&&Number.isFinite(timeoutMs)?{timeoutMs}:{}})}},{name:"engine-report",group:"drive",summary:"Report a normalized engine-activity verb for a task \u2014 the public face of the same engine.reportEvent RPC the built-in hook adapters use. Lets a plugin-contributed engine (or any wrapper script) drive the sidebar badge, attention inbox, and plugin event stream without a built-in hook adapter. Kinds: session-start|turn-start|turn-complete|turn-failed|turn-interrupted|awaiting-input|session-end (state kinds) plus tool-pre|tool-post|tool-failed|pre-compact|post-compact|subagent-start|subagent-stop (plugin-only).",flags:[F.taskId(!1),{name:"kind",type:"string",required:!0,placeholder:"KIND",description:"Normalized activity verb (see summary). Unknown kinds are rejected."},{name:"engine",type:"string",placeholder:"ID",description:"Engine id producing the report (a plugin engine id, or a built-in vendor)."},{name:"tab",type:"string",placeholder:"TAB",description:"Terminal tab id the session runs in (defaults to $ROVE_TAB_ID / $KOBE_TAB_ID)."},{name:"detail",type:"string",placeholder:"JSON",description:`Optional detail JSON, e.g. '{"failure":"rate_limit"}' or '{"waiting":"input"}'.`}],handler:async(ctx)=>{let taskId=ctx.args.str("task-id")??process.env.ROVE_TASK_ID??process.env.KOBE_TASK_ID,tabId=ctx.args.str("tab")??process.env.ROVE_TAB_ID??process.env.KOBE_TAB_ID,detailRaw=ctx.args.str("detail"),detail;if(detailRaw!==void 0)try{detail=JSON.parse(detailRaw)}catch{throw Error("--detail must be valid JSON")}return simpleRpc(ctx,"engine.reportEvent",{kind:ctx.args.str("kind"),...taskId?{taskId}:{cwd:process.cwd()},...ctx.args.str("engine")?{engine:ctx.args.str("engine")}:{},...tabId?{tabId}:{},...detail!==void 0?{detail}:{}})}},{name:"set-active",group:"drive",summary:"Set the shared active task (the focus every Tasks pane highlights). Pass --none to clear.",flags:[F.taskId(!1),{name:"none",type:"bool",description:"Clear the active task instead of setting one."}],handler:setActive}];var EDIT_VERBS=[{name:"rename",group:"edit",summary:"Set a task's title.",flags:[F.taskId(),{name:"title",type:"string",required:!0,placeholder:"T",description:"New title."}],handler:(ctx)=>simpleRpc(ctx,"task.rename",{taskId:ctx.args.require("task-id"),title:ctx.args.require("title")})},{name:"set-branch",group:"edit",summary:"Rename a task's branch (git branch -m if materialized, else recorded).",flags:[F.taskId(),{name:"branch",type:"string",required:!0,placeholder:"B",description:"New branch name."}],handler:(ctx)=>simpleRpc(ctx,"task.setBranch",{taskId:ctx.args.require("task-id"),branch:ctx.args.require("branch")})},SET_COMMAND_VERB,SET_EFFORT_VERB,{name:"set-status",group:"edit",summary:"Set a task's lifecycle LABEL. Cosmetic \u2014 the task row, its worktree, its branch and its engine session all stay exactly as they are, so `--status canceled` does NOT close, stop, or clean up anything. To end a task use `delete` (which keeps the git branch).",flags:[F.taskId(),{name:"status",type:"enum",required:!0,values:TASK_STATUSES,description:"New status."}],handler:(ctx)=>simpleRpc(ctx,"task.status",{taskId:ctx.args.require("task-id"),status:ctx.args.requireEnum("status")})}];async function collect(ctx){let daemon=daemonOf(ctx),{args,runtime}=ctx,idsFlag=args.str("task-ids"),repoFlag=args.path("repo"),groupFlag=args.str("group"),taskIds,unresolvableRepos=[];if(idsFlag)taskIds=idsFlag.split(",").map((s)=>s.trim()).filter(Boolean);else if(repoFlag||groupFlag){let{tasks}=await daemon.request("task.list"),filter=repoFlag?await repoFilter(runtime,repoFlag,tasks.map((t)=>t.repo)):null;unresolvableRepos=filter?.unresolvableRepos??[],taskIds=[];for(let t of tasks){if(groupFlag&&t.groupId!==groupFlag)continue;if(filter&&!filter.matches(t.repo))continue;taskIds.push(t.id)}}else throw new ApiError("collect needs --task-ids id1,id2, --group GROUPID, or --repo PATH","MISSING_TARGET");let registry=null;try{registry=(await daemon.request("debug.inspect"))?.activity?.tasks??{}}catch{registry=null}let out=[];for(let taskId of taskIds){let{task}=await daemon.request("task.get",{taskId}),{tabs,running}=await runtime.taskTabs(taskId,taskEngineArgv(task)),changes=task.worktreePath?await runtime.readWorktreeChanges(task.worktreePath):null,base=task.worktreePath?await runtime.readBranchSignals(task.worktreePath,task.baseRef):{baseRef:null,ahead:null,behind:null,diff:null},entry=registry?.[task.id],activity=entry?{state:entry.state,at:new Date(entry.at).toISOString(),forMs:Math.max(0,Date.now()-entry.at)}:null;out.push({taskId:task.id,title:task.title,branch:task.branch,worktreePath:task.worktreePath,vendor:task.vendor,status:task.status,...task.groupId?{groupId:task.groupId}:{},...task.dispatcher?{dispatcher:task.dispatcher}:{},running,activity,tabs,changes,base})}return{tasks:out,...unresolvableRepos.length>0?{unresolvableRepos}:{}}}async function feedback(ctx){return{ok:!0,discussion:submitFeedback({title:ctx.args.require("title"),body:ctx.args.require("body"),categorySlug:ctx.args.str("category")})}}var FEEDBACK_VERBS=[{name:"feedback",group:"feedback",summary:"Create a GitHub Discussion in the Rove repo's Feedback category through `gh`.",flags:[{name:"title",type:"string",required:!0,placeholder:"T",description:"Discussion title."},{name:"body",type:"string",required:!0,placeholder:"TEXT",description:"Discussion body."},{name:"category",type:"string",default:DEFAULT_FEEDBACK_CATEGORY_SLUG,placeholder:"SLUG",description:"Discussion category slug."}],offline:!0,handler:feedback}];var ISSUE_STATUSES=["open","doing","hold","done"],ISSUE_VERBS=[{name:"issue-list",group:"issues",summary:"List daemon-owned issues for a repo.",flags:[F.repo()],handler:(ctx)=>simpleRpc(ctx,"issue.list",{repoRoot:ctx.args.requireRepo("repo")})},{name:"issue-create",group:"issues",summary:"Create a daemon-owned issue for a repo.",flags:[F.repo(),{name:"title",type:"string",required:!0,placeholder:"T",description:"Issue title."},{name:"body",type:"string",placeholder:"TEXT",description:"Issue body."}],handler:(ctx)=>simpleRpc(ctx,"issue.mutate",{repoRoot:ctx.args.requireRepo("repo"),op:{type:"create",title:ctx.args.require("title"),body:ctx.args.str("body")}})},{name:"issue-set-status",group:"issues",summary:"Set a daemon-owned issue's status.",flags:[F.repo(),{name:"id",type:"int",required:!0,placeholder:"N",description:"Issue id."},{name:"status",type:"enum",required:!0,values:ISSUE_STATUSES,description:"New issue status."}],handler:(ctx)=>simpleRpc(ctx,"issue.mutate",{repoRoot:ctx.args.requireRepo("repo"),op:{type:"setStatus",id:ctx.args.int("id"),status:ctx.args.requireEnum("status")}})},{name:"issue-update",group:"issues",summary:"Update a daemon-owned issue's title, body, and/or linked task.",flags:[F.repo(),{name:"id",type:"int",required:!0,placeholder:"N",description:"Issue id."},{name:"title",type:"string",placeholder:"T",description:"New title."},{name:"body",type:"string",placeholder:"TEXT",description:"New body."},{name:"task",type:"string",placeholder:"TASK_ID",description:"Link the issue to this task (kanban: In progress). Pass `none` to unlink."}],handler:issueUpdate}];var DELETE_WAIT_TIMEOUT_MS=60000,DELETE_POLL_INTERVAL_MS=250;async function deleteTask(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.require("task-id"),force=ctx.args.bool("force")??!1,deleteBranch=ctx.args.bool("delete-branch")??!1,self=await verifiedSelfSession(),res;try{res=await daemon.request("task.delete",{taskId,force,deleteBranch,...self?{requestedByTaskId:self.taskId,requestedByTabId:self.tabId}:{}})}catch(err){throw deleteRecoveryError(err,taskId)}if(await ctx.runtime.tearDownSession(taskId),!res.queued)return{...res,status:"not_found"};if(!ctx.args.bool("wait"))return{...res,status:"queued"};return{...res,...await awaitDeletion(daemon,taskId)}}async function awaitDeletion(daemon,taskId){let deadline=Date.now()+DELETE_WAIT_TIMEOUT_MS;for(;;){let{tasks}=await daemon.request("task.list"),task=tasks.find((t)=>t.id===taskId);if(!task)return{status:"removed"};let deletion=task.deletion;if(deletion?.phase==="error")return{status:"failed",error:deletion.error??"worktree removal failed"};if(Date.now()>=deadline)return{status:"pending"};await new Promise((resolve2)=>setTimeout(resolve2,DELETE_POLL_INTERVAL_MS))}}async function land(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.require("task-id");if(ctx.args.bool("dry-run")===!0)return(await daemon.request("task.landPreflight",{taskId})).result;let strategy=ctx.args.str("strategy")==="squash"?"squash":"merge",res;try{res=await daemon.request("task.land",{taskId,strategy,deleteBranch:ctx.args.bool("delete-branch")??!1,removeWorktree:ctx.args.bool("remove-worktree"),callerCwd:process.cwd()})}catch(err){throw landRecoveryError(err,taskId)}return{ok:!0,taskId,...res.result}}function landRecoveryError(err,taskId){let message=errorMessage(err);if(!message.includes(EMPTY_BRANCH_DIRTY_WORKTREE_CODE))return err;let branch=/EMPTY_BRANCH_DIRTY_WORKTREE: '([^']+)'/.exec(message)?.[1]??"your task branch";return new ApiError(splitDaemonCode(message)?.rest??message,EMPTY_BRANCH_DIRTY_WORKTREE_CODE,{hint:"the worker wrote files but never committed them \u2014 send it back to commit its own work, then land again",nextCommandArgs:["api","send","--task-id",taskId,"--prompt",`your work is uncommitted on ${branch} \u2014 commit it yourself with a proper message, then report back`]})}function deleteRecoveryError(err,taskId){let message=errorMessage(err),coded=splitDaemonCode(message);if(coded?.code!==DIRTY_WORKTREE_CODE)return err;return new ApiError(coded.rest,DIRTY_WORKTREE_CODE,{taskId,hint:"the worktree still holds work this delete would destroy \u2014 the message names it, and a gitignored path never shows in `git status`. Send the worker back to commit it, or pass --force to delete the task AND discard that work",nextCommandArgs:["api","send","--task-id",taskId,"--prompt","your worktree still holds work that this cleanup would destroy (it may be gitignored, so `git status` will not show it \u2014 check `git status --ignored`). Commit it yourself with a proper message, then report back"]})}async function adopt(ctx){let daemon=daemonOf(ctx),{args}=ctx,input={repo:args.requireRepo("repo"),worktreePath:args.requirePath("worktree")},branch=args.str("branch");if(branch)input.branch=branch;let command=args.str("command");if(command)input.command=command,input.vendor=resolveCommandProtocol(command);let title=args.str("title");if(title)input.title=title;return daemon.request("worktree.adopt",input)}var LIFECYCLE_VERBS=[{name:"pin",group:"lifecycle",summary:"Pin (or with --pinned=false, unpin) a task to the top of the sidebar.",flags:[F.taskId(),{name:"pinned",type:"bool",default:"true",description:"true to pin, false to unpin."}],handler:(ctx)=>simpleRpc(ctx,"task.pin",{taskId:ctx.args.require("task-id"),pinned:ctx.args.bool("pinned")??!0})},{name:"land",group:"lifecycle",summary:"Merge a task's branch back into its base repo's current branch. Refuses a dirty base checkout, a branch that no longer resolves in the base repo (MISSING_REF \u2014 renamed or deleted outside Rove), and a branch with zero commits ahead of the base (EMPTY_BRANCH; EMPTY_BRANCH_DIRTY_WORKTREE with a send-back recovery path when the worktree still holds the uncommitted work). On conflict, aborts and returns the conflicted files (resolve by hand). Returns { landedOn, commit }.",flags:[F.taskId(),{name:"dry-run",type:"bool",description:"Report whether the land would proceed, and write nothing. Returns { branch, landedOn, ahead?, baseDirty?, refusal?, dirtyFiles?, baseDir } \u2014 `landedOn` is the base checkout's CURRENT branch (the merge destination), `ahead` the commits that would land, and `refusal` one of DETACHED_HEAD, UNREADABLE_BASE, UNBORN_BASE, SAME_BRANCH, MAIN_CHECKOUT_DIRTY, MISSING_REF, EMPTY_BRANCH, EMPTY_BRANCH_DIRTY_WORKTREE when the land would be refused. Ignores --strategy/--delete-branch/--remove-worktree."},{name:"strategy",type:"enum",values:["merge","squash"],default:"merge",description:"merge (--no-ff) or squash into one commit."},{name:"delete-branch",type:"bool",description:"Delete the task's branch after a successful land. Uses `git branch -D`, which drops the branch's reflog too; with --strategy squash the base's new commit does not reach the branch's own commits, so Rove first anchors the tip at refs/rove/salvage/<branch>-<stamp> and returns it as `branchAnchor`. Requires the worktree to be gone: git refuses to delete a branch a live worktree has checked out, so a land that kept the worktree keeps the branch too and says so in `branchKept`."},{name:"remove-worktree",type:"bool",default:"true",description:"Remove the task's worktree after a successful land (default; the branch always stays). Pass --remove-worktree=false to keep it. Dirty worktrees, the base checkout, and the caller's own worktree are refused \u2014 the outcome is reported in the result's `worktree` field, never thrown."}],handler:land},{name:"delete",group:"lifecycle",summary:"Remove a task and its worktree; the git branch stays unless --delete-branch. Needs --force on a dirty worktree. Returns { queued } \u2014 removal itself runs in the background; add --wait for the resolved outcome.",flags:[F.taskId(),{name:"force",type:"bool",description:"Delete even with uncommitted changes (never implies --delete-branch)."},{name:"delete-branch",type:"bool",description:"Also delete the task's git branch (default: keep it)."},{name:"wait",type:"bool",description:"Follow the background removal and report its OUTCOME (removed / failed / pending) instead of returning as soon as it is queued."}],handler:deleteTask}];var DEFAULT_SINCE_DAYS=7,DEFAULT_LIMIT=200;function summarizeTurns(turns){let byModel={},inputTokens=0,outputTokens=0,cacheReadTokens=0,cacheCreationTokens=0,durationMs=0;for(let turn of turns){inputTokens+=turn.usage?.input_tokens??0,outputTokens+=turn.usage?.output_tokens??0,cacheReadTokens+=turn.usage?.cache_read_input_tokens??0,cacheCreationTokens+=turn.usage?.cache_creation_input_tokens??0,durationMs+=Math.max(0,turn.endedAt-turn.startedAt);let model=turn.model??"unknown";byModel[model]=(byModel[model]??0)+1}return{turns:turns.length,inputTokens,outputTokens,cacheReadTokens,cacheCreationTokens,durationMs,byModel}}async function agentTurns(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.str("task-id"),repoPath=ctx.args.path("repo"),repo=repoPath?await ctx.runtime.resolveRepoRoot(repoPath):void 0,sinceDays=ctx.args.int("since-days")??DEFAULT_SINCE_DAYS,since=Date.now()-sinceDays*24*60*60*1000,limit=ctx.args.int("limit")??DEFAULT_LIMIT,{turns}=await daemon.request("agentTurn.list",{...taskId?{taskId}:{},...repo?{repoRoot:repo}:{},since,limit});return{since:new Date(since).toISOString(),totals:summarizeTurns(turns),turns}}var AGENT_TURNS_VERB={name:"agent-turns",group:"read",summary:`Per-turn agent telemetry: one record per completed engine turn (task/tab/vendor/model/timings/tokens), newest first, plus totals. Engine-produced, daemon-stored; read-only. Engines that report turns: ${vendorsWithTurnReader().join(", ")} \u2014 a task on any other engine yields an empty page because that engine has no turn reader, not because it did no work.`,flags:[{name:"task-id",type:"string",placeholder:"ID",description:"Only this task's turns."},{name:"repo",type:"string",placeholder:"PATH",description:"Only turns of tasks in this repo. Relative paths resolve against $PWD."},{name:"since-days",type:"int",default:String(DEFAULT_SINCE_DAYS),placeholder:"N",description:"Look-back window in days."},{name:"limit",type:"int",default:String(DEFAULT_LIMIT),placeholder:"N",description:"Max turns returned."}],handler:agentTurns};var DEFAULT_SINCE_DAYS2=7;function epochOf(iso){if(!iso)return null;let ms=Date.parse(iso);return Number.isNaN(ms)?null:ms}function buildDigest(repo,sinceMs,tasks,runs){let byStatus={};for(let run of runs)byStatus[run.status]=(byStatus[run.status]??0)+1;return{repo,since:new Date(sinceMs).toISOString(),tasks:{total:tasks.length},routines:{runs:runs.length,byStatus}}}async function digest(ctx){let daemon=daemonOf(ctx),{args,runtime}=ctx,repoFlag=args.requireRepo("repo"),sinceMs=Date.now()-(args.int("since-days")??DEFAULT_SINCE_DAYS2)*86400000,{tasks:allTasks}=await daemon.request("task.list"),{automations}=await daemon.request("automation.list"),filter=await repoFilter(runtime,repoFlag,[...allTasks.map((t)=>t.repo),...automations.map((a)=>a.repo)]),repo=filter.target,tasks=[];for(let task of allTasks){if((task.kind??"task")!=="task")continue;if((epochOf(task.updatedAt)??0)<sinceMs)continue;if(filter.matches(task.repo))tasks.push(task)}let runs=[];for(let automation of automations){if(!filter.matches(automation.repo))continue;let page=await daemon.request("automation.runs",{id:automation.id});for(let run of page.runs)if((epochOf(run.at)??0)>=sinceMs)runs.push(run)}return{...buildDigest(repo,sinceMs,tasks,runs),...filter.unresolvableRepos.length>0?{unresolvableRepos:filter.unresolvableRepos}:{}}}var DIGEST_VERB={name:"digest",group:"read",summary:"Aggregate a repo's recent agent work: tasks touched in the window plus routine run outcomes. Reads state Rove already persists \u2014 the measurement any workflow change has to move.",flags:[{name:"repo",type:"string",required:!0,placeholder:"PATH",description:"Repo root (git toplevel). Relative paths resolve against $PWD."},{name:"since-days",type:"int",default:String(DEFAULT_SINCE_DAYS2),placeholder:"N",description:"Look-back window in days."}],handler:digest};async function daemonSection(){let{connectIfRunning}=await import("./chunk-mb5m7ex7.js"),client=null;try{if(client=await connectIfRunning(),!client)return null;return await client.request("debug.inspect",{})}catch(err){return{error:err instanceof Error?err.message:String(err)}}finally{client?.close()}}async function sessionsSection(taskId){let[{KobeDaemonClient},{defaultPtyHostSocketPath}]=await Promise.all([import("./chunk-arf294te.js"),import("./chunk-ewdhz3dv.js")]),client=new KobeDaemonClient(defaultPtyHostSocketPath()),sessions;try{await client.connect(),sessions=(await client.request("pty.list",{})).sessions??[]}catch{return null}finally{client.close()}if(taskId)sessions=sessions.filter((s)=>s.key.startsWith(`${taskId}::`));let rows=null;try{rows=parsePsSnapshot(await psSnapshot())}catch{rows=null}return sessions.map((s)=>{let walkable=rows!==null&&typeof s.pid==="number"&&s.pid>0,found=walkable&&rows?foregroundEngineIn(rows,s.pid):null;return{key:s.key,alive:s.alive,pid:s.pid??null,title:s.title||null,exit:s.exit??null,foreground:walkable?found?{vendor:found.vendor,pid:found.pid,argv:found.argv}:null:"unknown"}})}async function sessionExitsSection(taskId){try{let{readPtyExitRecords}=await import("./chunk-5c3d1y0r.js"),records=Object.values(readPtyExitRecords()).sort((a,b)=>a.at<b.at?1:-1);return taskId?records.filter((r)=>r.key.startsWith(`${taskId}::`)):records}catch(err){return{error:err instanceof Error?err.message:String(err)}}}function tabsSection(taskId,sessions){let live=Array.isArray(sessions)?sessions.filter((s)=>typeof s?.key==="string"):[];try{let state=loadStateFile(),out={},prefix="terminalTabs.";for(let[key,value]of Object.entries(state)){if(!key.startsWith("terminalTabs."))continue;let id=key.slice(13);if(taskId&&id!==taskId)continue;let snap=value;if(!snap||!Array.isArray(snap.tabs))continue;let unregistered=unregisteredTabIds(snap,id,live);out[id]={activeId:snap.activeId,tabs:snap.tabs.map((t)=>({id:t.id,kind:t.kind,title:t.title??null,vendor:t.vendor??null,liveVendor:t.liveVendor??null,lastTitle:t.lastTitle??null,autoTitle:t.autoTitle??null})),...unregistered.length>0?{unregistered}:{}}}for(let s of live){let id=s.key.split("::")[0]??"";if(!id||out[id]!==void 0||taskId&&id!==taskId)continue;let unregistered=unregisteredTabIds(void 0,id,live);if(unregistered.length>0)out[id]={activeId:null,tabs:[],unregistered}}return out}catch(err){return{error:err instanceof Error?err.message:String(err)}}}async function inspect(ctx){let taskId=ctx.args.str("task-id"),[daemon,sessions,sessionExits]=await Promise.all([daemonSection(),sessionsSection(taskId),sessionExitsSection(taskId)]);return{daemon,sessions,sessionExits,tabs:tabsSection(taskId,sessions),at:new Date().toISOString()}}var INSPECT_VERB={name:"inspect",group:"read",summary:"Production diagnostics in one read: daemon activity registry (raw states, probe vendors, watchdogs), pty-host sessions joined with a live process-tree engine walk, durable session death records (exit code/signal/output tail), and the persisted tab snapshots the sidebar renders from. Read-only; missing daemon/host degrade to null.",flags:[F.taskId(!1)],offline:!0,handler:inspect};var DEFAULT_PAGE_MESSAGES=40,MAX_PAGE_MESSAGES=50,PAGE_BYTE_BUDGET=131072,STRING_CLIP_CHARS=16384,TERMINAL_TAIL_LINES=200,TERMINAL_TAIL_BYTES=65536;function encodeCursor(cursor){return Buffer.from(JSON.stringify(cursor),"utf8").toString("base64url")}function decodeCursor(raw,taskId){let parsed;try{parsed=JSON.parse(Buffer.from(raw,"base64url").toString("utf8"))}catch{throw new ApiError("invalid cursor (not a read-output cursor)","CURSOR_INVALID")}let c=parsed;if(!(c!==null&&typeof c==="object"&&c.v===1&&typeof c.task==="string"&&(c.src==="history"&&typeof c.sid==="string"&&typeof c.idx==="number"||c.src==="terminal"&&typeof c.off==="number"&&(c.tab===void 0||typeof c.tab==="string"))))throw new ApiError("invalid cursor (unknown version or shape)","CURSOR_INVALID");if(c.task!==taskId)throw new ApiError(`cursor belongs to task ${c.task}, not ${taskId}`,"CURSOR_TASK_MISMATCH");return c}function clipStrings(value){if(typeof value==="string"){if(value.length<=STRING_CLIP_CHARS)return value;return`${value.slice(0,STRING_CLIP_CHARS)}\u2026[+${value.length-STRING_CLIP_CHARS} chars clipped]`}if(Array.isArray(value))return value.map(clipStrings);if(value!==null&&typeof value==="object"){let out={};for(let[k,v]of Object.entries(value))out[k]=clipStrings(v);return out}return value}function buildHistoryPage(messages,startIdx,limit){let page=[],bytes=0,limited=!1,i=startIdx;for(;i<messages.length&&page.length<limit;i++){let clipped=clipStrings(messages[i]),size=JSON.stringify(clipped).length;if(page.length>0&&bytes+size>PAGE_BYTE_BUDGET){limited=!0;break}page.push(clipped),bytes+=size}return{page,nextIdx:i,limited}}function terminalLines(text){return terminalRows(text)}function boundedTail(text){let lines=terminalLines(text),start=Math.max(0,lines.length-TERMINAL_TAIL_LINES),bytes=0;for(let i=lines.length-1;i>=start;i--)if(bytes+=(lines[i]?.length??0)+1,bytes>TERMINAL_TAIL_BYTES&&i<lines.length-1){start=i+1;break}return{tail:lines.slice(start),truncated:start>0}}function sourceChanged(detail){return new ApiError(`${detail} \u2014 restart the read without the cursor`,"SOURCE_CHANGED")}async function readTaskOutput(input,deps){let limit=Math.min(Math.max(input.limit??DEFAULT_PAGE_MESSAGES,1),MAX_PAGE_MESSAGES);if(input.tab&&input.source==="history")throw new ApiError("--tab reads one terminal tab; --source history is task/worktree-scoped","BAD_FLAG");if(input.cursor){let cursor=decodeCursor(input.cursor,input.taskId);if(input.source!=="auto"&&input.source!==cursor.src)throw new ApiError(`cursor is pinned to source "${cursor.src}" but --source is "${input.source}"`,"CURSOR_INVALID");if(input.tab&&cursor.src!=="terminal")throw new ApiError(`cursor is pinned to source "${cursor.src}" but --tab reads a terminal tab`,"CURSOR_INVALID");if(cursor.src==="terminal"&&(cursor.tab??null)!==(input.tab??null))throw new ApiError(`cursor is pinned to tab ${cursor.tab??"canonical"} \u2014 pass the same --tab or restart without the cursor`,"CURSOR_INVALID");return cursor.src==="history"?continueHistory(input,deps,cursor,limit):continueTerminal(input,deps,cursor)}if(input.tab||input.source==="terminal")return firstTerminalPage(input,deps,null);let first=await tryFirstHistoryPage(input,deps,limit);if(typeof first!=="string")return first;if(input.source==="history")throw new ApiError(`structured history unavailable for ${input.taskId}: ${first}`,"HISTORY_REQUIRED");return firstTerminalPage(input,deps,first)}async function currentSessionId(history,worktree){let ids=await history.listSessionIdsForWorktree(worktree);return ids.length>0?ids[ids.length-1]??null:null}async function tryFirstHistoryPage(input,deps,limit){if(!deps.history)return"engine_unsupported";if(!input.worktree)return"history_missing";let sid;try{sid=await currentSessionId(deps.history,input.worktree)}catch{return"history_unreadable"}if(!sid)return"history_missing";let messages;try{messages=await deps.history.readHistory(sid)}catch{return"history_unreadable"}return historyEnvelope(input.taskId,sid,messages,0,limit)}async function continueHistory(input,deps,cursor,limit){if(!deps.history||!input.worktree)throw sourceChanged("the engine no longer provides structured history");let sid,messages;try{sid=await currentSessionId(deps.history,input.worktree),messages=sid===cursor.sid?await deps.history.readHistory(cursor.sid):[]}catch{throw new ApiError("history became unreadable \u2014 retry, or restart without the cursor","HISTORY_UNREADABLE")}if(sid!==cursor.sid)throw sourceChanged(`the task's engine session changed (was ${cursor.sid}, now ${sid??"none"})`);if(cursor.idx>messages.length)throw sourceChanged("the pinned transcript shrank");return historyEnvelope(input.taskId,cursor.sid,messages,cursor.idx,limit)}function historyEnvelope(taskId,sessionId,messages,startIdx,limit){let{page,nextIdx,limited}=buildHistoryPage(messages,startIdx,limit);return{taskId,source:"history",history:{sessionId,messages:page,returnedMessageCount:page.length,totalMessages:messages.length,limited},cursor:encodeCursor({v:1,task:taskId,src:"history",sid:sessionId,idx:nextIdx}),fallbackReason:null,warnings:[]}}async function firstTerminalPage(input,deps,fallbackReason){let t=await deps.peekTerminal(input.tab);if(!t||t==="host-unreachable"){let unreachable=t==="host-unreachable";return{taskId:input.taskId,source:"terminal",terminal:{tail:[],truncated:!1,live:!1,tab:input.tab},cursor:null,fallbackReason:unreachable?"pty_host_unreachable":fallbackReason,warnings:[unreachable?"the pty host could not be reached \u2014 this is 'could not look', not 'no session'":"no live terminal session for this task"]}}let{tail,truncated}=boundedTail(t.text);return{taskId:input.taskId,source:"terminal",terminal:{tail,truncated,live:t.live,exit:t.exit??null,tab:input.tab},cursor:encodeCursor({v:1,task:input.taskId,src:"terminal",pid:t.pid,off:t.offset,fr:fallbackReason,tab:input.tab}),fallbackReason,warnings:[]}}async function continueTerminal(input,deps,cursor){let t=await deps.peekTerminal(cursor.tab,cursor.off);if(t==="host-unreachable")throw sourceChanged("the pty host could not be reached");if(!t)throw sourceChanged("the terminal session is gone");if(t.pid!==cursor.pid)throw sourceChanged("the terminal session restarted (new process)");let warnings=t.sinceValid?[]:["scrollback trimmed \u2014 there is a gap before this page"],{tail,truncated}=boundedTail(t.text),fr=cursor.fr??null;return{taskId:input.taskId,source:"terminal",terminal:{tail,truncated,live:t.live,exit:t.exit??null,tab:cursor.tab},cursor:encodeCursor({v:1,task:input.taskId,src:"terminal",pid:t.pid,off:t.offset,fr,tab:cursor.tab}),fallbackReason:fr,warnings}}async function peekTaskTerminal(taskId,vendor,tab,sinceOffset){let host=await openPtyHost();if(!host)return"host-unreachable";try{let key;if(tab)key=`${taskId}::${tab}`;else{let engineBin=vendor?engineLaunchArgv({vendor})[0]:void 0,sessions=await listSessionsOrNull(host.rpc);if(sessions===null)return"host-unreachable";key=findEngineKey(sessions,taskId,engineBin)??sessions.find((s)=>s.key===`${taskId}::tab-1`)?.key}if(!key)return null;let res;try{res=await host.rpc.request("pty.peek",{key,sinceOffset})}catch{return"host-unreachable"}if(!res.exists){if(tab)throw new ApiError(`tab ${tab} has no hosted session on task ${taskId} \u2014 see \`rove api pty-list\` for live tabs`,"TAB_NOT_FOUND");return null}return{pid:res.pid,offset:res.offset,text:Buffer.from(res.data,"base64").toString("utf8"),sinceValid:res.sinceValid,live:res.alive,exit:res.exit??null}}catch(err){if(err instanceof ApiError)throw err;return null}finally{host.close()}}async function handleReadOutput(ctx){let daemon=daemonOf(ctx),taskId=ctx.args.str("task-id");if(!taskId){let active=await resolveActiveTaskId(daemon);if(!active)throw new ApiError("no --task-id given and no active task \u2014 pass --task-id","MISSING_TARGET");taskId=active}let{task}=await daemon.request("task.get",{taskId}),vendor=task.vendor,tab=ctx.args.str("tab"),deps={history:vendor&&supportsStructuredHistory(sessionProtocol(vendor))?protocolEntry(vendor).history:null,peekTerminal:(tabId,sinceOffset)=>peekTaskTerminal(taskId,vendor,tabId,sinceOffset)},envelope=await readTaskOutput({taskId,worktree:task.worktreePath??null,source:ctx.args.enumOf("source")??"auto",tab,cursor:ctx.args.str("cursor"),limit:ctx.args.int("limit")},deps),running=await ctx.runtime.isTaskRunning(taskId,taskEngineArgv(task));return{vendor:vendor??null,running,...envelope}}var READ_OUTPUT_VERB={name:"read-output",group:"read",summary:"Read a task's engine output as bounded, cursor-paged JSON: the engine's own structured history when available, else a labeled terminal tail (typed fallbackReason). --tab tab-N reads one exact terminal tab. Read-only; the cursor stays pinned to one source/session/tab (SOURCE_CHANGED when it moved).",flags:[{name:"task-id",type:"string",placeholder:"ID",description:"Target task id (defaults to the active task)."},{name:"tab",type:"string",placeholder:"TAB",description:"Read exactly this terminal tab's hosted session (e.g. tab-3) instead of the canonical engine tab. Terminal-only read; cannot combine with --source history."},{name:"source",type:"enum",values:["auto","history","terminal"],default:"auto",description:"auto = structured history else terminal fallback; history = require structured (typed error instead of fallback); terminal = bounded terminal tail."},{name:"cursor",type:"string",placeholder:"C",description:"Opaque cursor from the previous page. Pinned to that page's source and session."},{name:"limit",type:"int",placeholder:"N",default:String(DEFAULT_PAGE_MESSAGES),description:`History messages per page (default ${DEFAULT_PAGE_MESSAGES}, max ${MAX_PAGE_MESSAGES}).`}],handler:handleReadOutput};var READ_VERBS=[{name:"list",group:"read",summary:"List all tasks. Returns { tasks, activeTaskId } \u2014 `activeTaskId` is the shared focus verbs default to when --task-id is omitted (null = no active task), the audit read for any implicit-target delivery.",flags:[],handler:list},{name:"get-task",group:"read",summary:"Read one task's metadata + terminal tabs. `.running` = any hosted engine tab is live; `.tabs[]` (id/kind/vendor/liveVendor/lastTitle/alive) is the discovery read for `send --tab tab-N`; `.task.dispatcher` = the Rove session (task+tab) that created it, when one did; `.task.prStatus.checkState` (none|pending|passing|failing|unknown) is Rove's OWN CI truth for the branch's PR \u2014 `passing` is what \"CI is green\" means, never a local test run.",flags:[F.taskId()],handler:getTask},{name:"pty-list",group:"read",summary:"List hosted PTY sessions (key, alive, pid, command, live OSC window title). Returns { sessions }: `[]` = a live pty host with nothing running (the fleet IS idle); `null` = no pty host to ask, so this is `couldn't look` and says NOTHING about what is running.",flags:[],offline:!0,handler:handlePtyList},{name:"collect",group:"read",summary:"Read-only health snapshot of a parallel round: identity, branch, lineage (.dispatcher, .groupId), .running (pty-host process truth, not a cached status), .activity (daemon engine state + how long it has been in it, null when unknowable), per-tab .tabs with a dead tab's exit cause AND output tail, uncommitted .changes (non-zero = it cannot land; null = could not read git \u2014 do NOT treat as clean), and committed .base (ahead count + diffstat \u2014 ahead:0 is the `succeeded but committed nothing` tell). Select with --group (one fan-out round), --repo, or --task-ids.",flags:[{name:"task-ids",type:"csv",placeholder:"a,b,c",description:"Comma-separated task ids."},{name:"group",type:"string",placeholder:"GROUPID",description:"Every task of one fan-out round (the `groupId` that `add --count` returns)."},F.repo(!1)],handler:collect},DIGEST_VERB,AGENT_TURNS_VERB,INSPECT_VERB,READ_OUTPUT_VERB];var WORK_ITEM_STATES=["open","closed","all"],WORK_ITEM_VERBS=[{name:"workitem-list",group:"workitems",summary:"List a repo's GitHub issues through the `gh` CLI. Read-only \u2014 nothing is copied into Rove's own issue store.",flags:[F.repo(),{name:"state",type:"enum",values:WORK_ITEM_STATES,default:"open",description:"Issue state filter."},{name:"limit",type:"int",placeholder:"N",default:"20",description:"Max items (1-50, max 50)."},{name:"search",type:"string",placeholder:"Q",description:"Free-text search passed to `gh --search`."},{name:"assignee",type:"string",placeholder:"USER",description:"Only items assigned to this user; `@me` for yourself."},{name:"label",type:"string",placeholder:"L",description:"Only items carrying this label."}],handler:(ctx)=>simpleRpc(ctx,"workitem.list",{repo:ctx.args.requireRepo("repo"),...ctx.args.str("state")?{state:ctx.args.str("state")}:{},...ctx.args.int("limit")!==void 0?{limit:ctx.args.int("limit")}:{},...ctx.args.str("search")?{search:ctx.args.str("search")}:{},...ctx.args.str("assignee")?{assignee:ctx.args.str("assignee")}:{},...ctx.args.str("label")?{labels:[ctx.args.str("label")]}:{}})},{name:"workitem-start",group:"workitems",summary:"Start a task on one GitHub issue: creates a worktree + engine session whose first message carries the issue title, body, and URL. The task keeps a link back to the issue.",flags:[F.repo(),{name:"number",type:"int",required:!0,placeholder:"N",description:"Issue number."},F.vendor(),{name:"base-branch",type:"string",placeholder:"B",description:"Base ref the worktree branches from."}],handler:(ctx)=>simpleRpc(ctx,"workitem.start",{repo:ctx.args.requireRepo("repo"),number:ctx.args.int("number"),...ctx.args.vendor()?{vendor:ctx.args.vendor()}:{},...ctx.args.str("base-branch")?{baseRef:ctx.args.str("base-branch")}:{}})}];var WORKTREE_VERBS=[{name:"ensure-worktree",group:"worktree",summary:"Materialize a task's git worktree on disk now (without starting an engine). Returns { worktreePath }.",flags:[F.taskId()],handler:(ctx)=>simpleRpc(ctx,"task.ensureWorktree",{taskId:ctx.args.require("task-id")})},{name:"discover-adoptable",group:"worktree",summary:"List existing git worktrees in a repo not yet tracked as Rove tasks. Returns { worktrees }.",flags:[F.repo()],handler:(ctx)=>simpleRpc(ctx,"worktree.discoverAdoptable",{repo:ctx.args.requireRepo("repo")})},{name:"adopt",group:"worktree",summary:"Import an existing git worktree as a Rove task. Returns { task }.",flags:[F.repo(),{name:"worktree",type:"string",required:!0,placeholder:"PATH",description:"Path of the worktree to adopt."},{name:"branch",type:"string",placeholder:"B",description:"Branch override (else the worktree's own)."},F.command(),F.title()],handler:adopt}];async function handleSchema(ctx){let verbName=ctx.args.str("verb");if(verbName){let v=findVerb(verbName);if(!v)throw new ApiError(`unknown verb: ${verbName}`,"BAD_VERB");return verbSchema(v)}let group=ctx.args.str("group");if(group)return groupSchema(group);if(ctx.args.bool("all"))return fullSchema();return schemaIndex()}var VERB_ALIASES={"spawn-task":"add"},RETIRED_VERBS={"fan-out":{hint:"fan-out was folded into `add`: pass --count N (or --agents claude:2,codex:1) to spawn N parallel tasks of one prompt",nextCommandArgs:["api","add","--help"]},"set-vendor":{hint:"set-vendor was replaced by `set-command`, which takes the engine's raw launch command (an engine id from `engine-list`, or a full command line)",nextCommandArgs:["api","set-command","--help"]},archive:{hint:"archive was removed: there is no hide-without-delete anymore \u2014 use `delete` to remove a finished task and its worktree; the git branch survives (pass --delete-branch explicitly only when the history may go)",nextCommandArgs:["api","delete","--help"]}},SCHEMA_VERB={name:"schema",group:"discover",summary:"Explore the API. Default = a COMPACT index (groups + verb summaries, no flags). Drill in with --verb / --group; --all for the full spec.",flags:[{name:"verb",type:"string",placeholder:"NAME",description:"Full flag detail for ONE verb."},{name:"group",type:"string",placeholder:"G",description:"List the verbs in one group (compact)."},{name:"all",type:"bool",description:"The COMPLETE spec \u2014 every verb AND every flag (large; avoid by default)."}],offline:!0,handler:handleSchema},VERBS=[SCHEMA_VERB,ENGINE_LIST_VERB,...READ_VERBS,...CREATE_VERBS,...DRIVE_VERBS,...FEEDBACK_VERBS,...ISSUE_VERBS,...ROUTINE_VERBS,...WORK_ITEM_VERBS,...EDIT_VERBS,...LIFECYCLE_VERBS,...WORKTREE_VERBS],API_VERBS=VERBS.map((v)=>v.name),VERB_GROUPS=(()=>{let byGroup=Object.fromEntries(VERB_GROUP_IDS.map((g)=>[g,[]]));for(let v of VERBS)byGroup[v.group].push(v.name);return byGroup})();function findVerb(name){let canonical=VERB_ALIASES[name]??name;return VERBS.find((v)=>v.name===canonical)}
7
+ export{parseFlags,validateAgainstSpec,VerbArgs,parseAgentsSpec,buildCountPlan,API_SCHEMA_VERSION,verbSchema,schemaIndex,fullSchema,verbHelp,apiUsage,takeIdentityWarning,deliverPrompt,defaultApiRuntime,VERB_ALIASES,RETIRED_VERBS,VERBS,API_VERBS,VERB_GROUPS,findVerb};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{readLastActiveTaskId,writeLastActiveTaskId}from"./chunk-zb56ck5f.js";import"./chunk-gjf604bb.js";import"./chunk-6dn6h640.js";import"./chunk-cb2fpajx.js";import"./chunk-3bfm4hrs.js";import"./chunk-wqfp8d9w.js";export{writeLastActiveTaskId,readLastActiveTaskId};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ var __create=Object.create;var{getPrototypeOf:__getProtoOf,defineProperty:__defProp,getOwnPropertyNames:__getOwnPropNames}=Object;var __hasOwnProp=Object.prototype.hasOwnProperty;function __accessProp(key){return this[key]}var __toESMCache_node,__toESMCache_esm,__toESM=(mod,isNodeMode,target)=>{var canCache=mod!=null&&typeof mod==="object";if(canCache){var cache=isNodeMode?__toESMCache_node??=new WeakMap:__toESMCache_esm??=new WeakMap,cached=cache.get(mod);if(cached)return cached}target=mod!=null?__create(__getProtoOf(mod)):{};let to=isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target;for(let key of __getOwnPropNames(mod))if(!__hasOwnProp.call(to,key))__defProp(to,key,{get:__accessProp.bind(mod,key),enumerable:!0});if(canCache)cache.set(mod,to);return to};var __commonJS=(cb,mod)=>()=>(mod||cb((mod={exports:{}}).exports,mod),mod.exports);var __esm=(fn,res)=>()=>(fn&&(res=fn(fn=0)),res);var __require=import.meta.require;
3
+ export{__toESM,__commonJS,__esm,__require};