@sma1lboy/rove 0.9.253 → 0.9.256

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 (113) hide show
  1. package/dist/cli/{chunk-xmf4053m.js → chunk-09v8w5jq.js} +1 -1
  2. package/dist/cli/{chunk-sncjkw3g.js → chunk-0yyvfpcg.js} +3 -3
  3. package/dist/cli/{chunk-t6j3pmpp.js → chunk-18dpws0y.js} +1 -1
  4. package/dist/cli/{chunk-19h18xca.js → chunk-1e6jbj70.js} +1 -1
  5. package/dist/cli/{chunk-avdg2rrh.js → chunk-1s8ctwmv.js} +1 -1
  6. package/dist/cli/{chunk-97vsqj97.js → chunk-372yekdz.js} +1 -1
  7. package/dist/cli/{chunk-g3wdrhxk.js → chunk-3aecx6za.js} +2 -2
  8. package/dist/cli/{chunk-yq99x0r6.js → chunk-3r8v2gza.js} +1 -1
  9. package/dist/cli/chunk-3vq0ywzf.js +3 -0
  10. package/dist/cli/{chunk-rm2wy5hf.js → chunk-3x0hyz6y.js} +3 -3
  11. package/dist/cli/{chunk-mrtfvgb2.js → chunk-4mxn5jg6.js} +1 -1
  12. package/dist/cli/{chunk-zy20hzvh.js → chunk-5a6b4kv9.js} +1 -1
  13. package/dist/cli/{chunk-bdvqjzwh.js → chunk-5csm9ffj.js} +9 -7
  14. package/dist/cli/{chunk-3j1x55tc.js → chunk-5d0vb6v9.js} +1 -1
  15. package/dist/cli/{chunk-c3d36ryd.js → chunk-5j6z32fh.js} +1 -1
  16. package/dist/cli/{chunk-8je3dmzq.js → chunk-5s4g1bkx.js} +1 -1
  17. package/dist/cli/{chunk-34vpdm19.js → chunk-6d5csfp4.js} +1 -1
  18. package/dist/cli/{chunk-vcq9hb2a.js → chunk-73chbrb4.js} +1 -1
  19. package/dist/cli/chunk-7wga6bet.js +8 -0
  20. package/dist/cli/{chunk-nhpadsbv.js → chunk-7yb48mar.js} +1 -1
  21. package/dist/cli/{chunk-2nfz06mq.js → chunk-7zpkyrxy.js} +1 -1
  22. package/dist/cli/{chunk-ffjwgbck.js → chunk-80zzjcvp.js} +1 -1
  23. package/dist/cli/{chunk-wq6pfbek.js → chunk-8vh57pq0.js} +2 -2
  24. package/dist/cli/{chunk-3r4ekafa.js → chunk-8wf3p9er.js} +1 -1
  25. package/dist/cli/chunk-9g0vz9dk.js +209 -0
  26. package/dist/cli/{chunk-6e54q8g5.js → chunk-9qny0zke.js} +1 -1
  27. package/dist/cli/{chunk-epyw87gt.js → chunk-adkk89dp.js} +1 -1
  28. package/dist/cli/{chunk-ad5gbdcf.js → chunk-ant4k6dm.js} +1 -1
  29. package/dist/cli/{chunk-6k424rbf.js → chunk-bampejxv.js} +1 -1
  30. package/dist/cli/{chunk-76adkzyf.js → chunk-btpp4geb.js} +1 -1
  31. package/dist/cli/chunk-c9xg9dn0.js +3 -0
  32. package/dist/cli/{chunk-pcyatg4b.js → chunk-ccqs21dj.js} +1 -1
  33. package/dist/cli/{chunk-814ktvx2.js → chunk-cqdebtbr.js} +1 -1
  34. package/dist/cli/{chunk-2bmebw8q.js → chunk-d4mvprvq.js} +1 -1
  35. package/dist/cli/{chunk-dk0hffvd.js → chunk-dqprpnd6.js} +1 -1
  36. package/dist/cli/chunk-edsm15ht.js +4 -0
  37. package/dist/cli/{chunk-rpm771v5.js → chunk-ekre1aps.js} +8 -8
  38. package/dist/cli/chunk-em6vwq07.js +2 -0
  39. package/dist/cli/{chunk-vjvkkrzc.js → chunk-f5n0tgky.js} +1 -1
  40. package/dist/cli/chunk-fhrs8ja3.js +2 -0
  41. package/dist/cli/{chunk-y33j54yr.js → chunk-fnsch8kr.js} +2 -2
  42. package/dist/cli/{chunk-mw35vd6g.js → chunk-fvksjtsk.js} +1 -1
  43. package/dist/cli/{chunk-tq1hrd9n.js → chunk-ggzfard0.js} +1 -1
  44. package/dist/cli/{chunk-x2c8f2qz.js → chunk-gpcxqdmq.js} +1 -1
  45. package/dist/cli/{chunk-hbq7nqnp.js → chunk-h0h51emh.js} +1 -1
  46. package/dist/cli/{chunk-0x7zcw52.js → chunk-hkyn3kme.js} +1 -1
  47. package/dist/cli/{chunk-p8d9va1w.js → chunk-hqp8b49k.js} +2 -2
  48. package/dist/cli/{chunk-yjk0465m.js → chunk-hzzq6pdb.js} +1 -1
  49. package/dist/cli/{chunk-s21t29qn.js → chunk-j7831wcj.js} +1 -1
  50. package/dist/cli/{chunk-67q0qw7z.js → chunk-jetsx9zr.js} +1 -1
  51. package/dist/cli/{chunk-q349ydch.js → chunk-jtpjdmb6.js} +1 -1
  52. package/dist/cli/{chunk-c4e149yb.js → chunk-jwtqe209.js} +1 -1
  53. package/dist/cli/{chunk-gt1vbz3f.js → chunk-k7ke95vy.js} +1 -1
  54. package/dist/cli/{chunk-6r84zkcz.js → chunk-ksk3j88n.js} +1 -1
  55. package/dist/cli/{chunk-mwm63vz3.js → chunk-m3pn99w9.js} +8 -8
  56. package/dist/cli/{chunk-xgq5ycyn.js → chunk-mk1xzp2k.js} +1 -1
  57. package/dist/cli/{chunk-02h0488p.js → chunk-mvsvmfsf.js} +1 -1
  58. package/dist/cli/{chunk-hqzgejfj.js → chunk-p5902z3e.js} +1 -1
  59. package/dist/cli/{chunk-q1e0qqmd.js → chunk-pahcf9td.js} +1 -1
  60. package/dist/cli/{chunk-9wkf17yd.js → chunk-pfx1tg6q.js} +1 -1
  61. package/dist/cli/{chunk-8wze6dnr.js → chunk-ptaj848y.js} +1 -1
  62. package/dist/cli/chunk-pw10r9fk.js +2 -0
  63. package/dist/cli/{chunk-b1mk33gy.js → chunk-pw1twn8f.js} +1 -1
  64. package/dist/cli/{chunk-k8dm013e.js → chunk-q800jxzk.js} +1 -1
  65. package/dist/cli/{chunk-v9avfsxq.js → chunk-q9f24pkx.js} +1 -1
  66. package/dist/cli/{chunk-6ygwq4w5.js → chunk-rc2nwsdd.js} +1 -1
  67. package/dist/cli/{chunk-zmw0v18d.js → chunk-rf4z7b6g.js} +1 -1
  68. package/dist/cli/{chunk-jbqpyp76.js → chunk-rnxk3587.js} +1 -1
  69. package/dist/cli/chunk-rzd7xpx2.js +30 -0
  70. package/dist/cli/{chunk-pwybgztt.js → chunk-s96qnht3.js} +1 -1
  71. package/dist/cli/{chunk-jk9b9ztf.js → chunk-se20ckc5.js} +1 -1
  72. package/dist/cli/chunk-sma961wh.js +3 -0
  73. package/dist/cli/{chunk-b18pnmsv.js → chunk-stcvcsfm.js} +1 -1
  74. package/dist/cli/{chunk-hm7t9gkb.js → chunk-tewt7vre.js} +1 -1
  75. package/dist/cli/{chunk-b3j5s2jb.js → chunk-tq6pq1ws.js} +1 -1
  76. package/dist/cli/{chunk-3dgghmjv.js → chunk-v7prawj1.js} +1 -1
  77. package/dist/cli/chunk-vbv6v4hs.js +52 -0
  78. package/dist/cli/chunk-vfwbgx85.js +2 -0
  79. package/dist/cli/{chunk-qc06psqb.js → chunk-vvw1njx5.js} +1 -1
  80. package/dist/cli/{chunk-7hkkjx6g.js → chunk-vydwyjz2.js} +1 -1
  81. package/dist/cli/{chunk-sk3h2pzj.js → chunk-w9grtqm8.js} +2 -2
  82. package/dist/cli/{chunk-dkb8nj75.js → chunk-wy5xx1ez.js} +1 -1
  83. package/dist/cli/{chunk-65tbvcjj.js → chunk-xd4qfcm6.js} +1 -1
  84. package/dist/cli/{chunk-p98w5ffk.js → chunk-y330t2bt.js} +2 -2
  85. package/dist/cli/{chunk-bq0xsdqx.js → chunk-y4vxge6s.js} +1 -1
  86. package/dist/cli/{chunk-bk4pfjs0.js → chunk-ypzac0kj.js} +1 -1
  87. package/dist/cli/{chunk-mt8vtz20.js → chunk-z41s748k.js} +2 -2
  88. package/dist/cli/index.js +4 -4
  89. package/dist/cli/pty-host-node.mjs +5 -2
  90. package/dist/cli/rove.js +4 -4
  91. package/dist/completions/rove.bash +1 -1
  92. package/dist/completions/rove.fish +1 -1
  93. package/dist/completions/rove.zsh +1 -1
  94. package/dist/skills/rove/SKILL.md +14 -3
  95. package/dist/skills/rove/references/api-flags.md +47 -32
  96. package/dist/web/assets/{index-DmRZRttZ.js → index-BydepNFM.js} +1 -1
  97. package/dist/web/index.html +1 -1
  98. package/dist/web-ui/assets/{harness-BHiciz5A.js → harness-dYkQBfYz.js} +3 -3
  99. package/dist/web-ui/assets/{index-BkxJISAI.js → index-ByMdqeTv.js} +2 -2
  100. package/dist/web-ui/index.html +1 -1
  101. package/package.json +6 -4
  102. package/dist/cli/chunk-75zg2k73.js +0 -3
  103. package/dist/cli/chunk-9g7gneek.js +0 -30
  104. package/dist/cli/chunk-9jbcfrse.js +0 -2
  105. package/dist/cli/chunk-9r6x8mh4.js +0 -8
  106. package/dist/cli/chunk-admwmjjy.js +0 -4
  107. package/dist/cli/chunk-cgy92ks5.js +0 -2
  108. package/dist/cli/chunk-n7dtvega.js +0 -2
  109. package/dist/cli/chunk-parvvj8h.js +0 -2
  110. package/dist/cli/chunk-r2pd9tv9.js +0 -3
  111. package/dist/cli/chunk-sb2xg6py.js +0 -5
  112. package/dist/cli/chunk-vx4e787s.js +0 -3
  113. package/dist/cli/chunk-zfjqy7hp.js +0 -52
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{submitFeedback}from"./chunk-tq1hrd9n.js";import"./chunk-75zg2k73.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{readFileSync}from"fs";var CLI_NAME=ROVE_PRODUCT_NAME,FEEDBACK_USAGE=[`Usage: ${CLI_NAME} feedback --title <text> (--body <text> | --body-file <path>) [--category <slug>]`,"","Create a GitHub Discussion in the Rove repository using the GitHub CLI.","","Requires:"," gh auth login","","Options:"," --title <text> Discussion title"," --body <text> Discussion body"," --body-file <path> Read the Discussion body from a file; use - for stdin"," --category <slug> Discussion category slug (default: feedback)"," -h, --help Print this help",""].join(`
2
+ import{submitFeedback}from"./chunk-ggzfard0.js";import"./chunk-c9xg9dn0.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{readFileSync}from"fs";var CLI_NAME=ROVE_PRODUCT_NAME,FEEDBACK_USAGE=[`Usage: ${CLI_NAME} feedback --title <text> (--body <text> | --body-file <path>) [--category <slug>]`,"","Create a GitHub Discussion in the Rove repository using the GitHub CLI.","","Requires:"," gh auth login","","Options:"," --title <text> Discussion title"," --body <text> Discussion body"," --body-file <path> Read the Discussion body from a file; use - for stdin"," --category <slug> Discussion category slug (default: feedback)"," -h, --help Print this help",""].join(`
3
3
  `);function usageError(message){process.stderr.write(`${CLI_NAME} feedback: ${message}
4
4
 
5
5
  ${FEEDBACK_USAGE}
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{PLUGIN_MANIFEST_FILENAMES,currentPluginPlatform,loadPluginRegistry,parsePluginManifest,pluginCheckoutDir,pluginConfigDir,pluginManifestPath,pluginStateDir,pluginsRootDir,savePluginRegistry,supportsPlatform,upsertPluginEntry}from"./chunk-admwmjjy.js";import{CURRENT_VERSION,compareSemver}from"./chunk-75zg2k73.js";import{spawn}from"child_process";import{cpSync,existsSync,mkdirSync,readFileSync,renameSync,rmSync}from"fs";import{mkdtempSync}from"fs";import{basename,join,resolve}from"path";import{createInterface}from"readline/promises";var GITHUB_SPEC_RE=/^([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)(\/.+)?$/;class PluginCliError extends Error{}function fail(message){throw new PluginCliError(message)}function readManifestAt(root){let path=pluginManifestPath(root);if(!path)fail(`no ${PLUGIN_MANIFEST_FILENAMES.join(" or ")} found at ${root}`);let source=readFileSync(path,"utf8");return{...parsePluginManifest(source,basename(path)),manifestPath:path,manifestSource:source}}function checkVersionGate(parsed){let min=parsed.manifest.minRoveVersion;if(compareSemver(CURRENT_VERSION,min)<0)fail(`plugin requires Rove >= ${min} (this is ${CURRENT_VERSION}); update Rove first`)}function commandLines(parsed){let m=parsed.manifest,lines=[];for(let c of m.build)lines.push(`build: ${c.command.join(" ")}`);for(let c of m.startup)lines.push(`startup: ${c.command.join(" ")}`);for(let a of m.actions)lines.push(`action ${a.id}: ${a.command.join(" ")}`);for(let e of m.events)lines.push(`on ${e.on}: ${e.command.join(" ")}`);return lines}var CAPTURE_TAIL_BYTES=4096;function run(cmd,args,opts){return new Promise((resolve2)=>{let child=spawn(cmd,args,{cwd:opts.cwd,stdio:opts.inherit?["ignore","inherit","inherit"]:["ignore","pipe","pipe"]}),output="",collect=(chunk)=>{output=`${output}${String(chunk)}`.slice(-CAPTURE_TAIL_BYTES)};child.stdout?.on("data",collect),child.stderr?.on("data",collect),child.on("error",(err)=>resolve2({code:null,output:`${output}${err.message}`})),child.on("close",(code)=>resolve2({code,output}))})}function outputTail(output){let trimmed=output.trim();return trimmed?`: ${trimmed.split(`
2
+ import{PLUGIN_MANIFEST_FILENAMES,currentPluginPlatform,loadPluginRegistry,parsePluginManifest,pluginCheckoutDir,pluginConfigDir,pluginManifestPath,pluginStateDir,pluginsRootDir,savePluginRegistry,supportsPlatform,upsertPluginEntry}from"./chunk-edsm15ht.js";import{CURRENT_VERSION,compareSemver}from"./chunk-c9xg9dn0.js";import{spawn}from"child_process";import{cpSync,existsSync,mkdirSync,readFileSync,renameSync,rmSync}from"fs";import{mkdtempSync}from"fs";import{basename,join,resolve}from"path";import{createInterface}from"readline/promises";var GITHUB_SPEC_RE=/^([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)(\/.+)?$/;class PluginCliError extends Error{}function fail(message){throw new PluginCliError(message)}function readManifestAt(root){let path=pluginManifestPath(root);if(!path)fail(`no ${PLUGIN_MANIFEST_FILENAMES.join(" or ")} found at ${root}`);let source=readFileSync(path,"utf8");return{...parsePluginManifest(source,basename(path)),manifestPath:path,manifestSource:source}}function checkVersionGate(parsed){let min=parsed.manifest.minRoveVersion;if(compareSemver(CURRENT_VERSION,min)<0)fail(`plugin requires Rove >= ${min} (this is ${CURRENT_VERSION}); update Rove first`)}function commandLines(parsed){let m=parsed.manifest,lines=[];for(let c of m.build)lines.push(`build: ${c.command.join(" ")}`);for(let c of m.startup)lines.push(`startup: ${c.command.join(" ")}`);for(let a of m.actions)lines.push(`action ${a.id}: ${a.command.join(" ")}`);for(let e of m.events)lines.push(`on ${e.on}: ${e.command.join(" ")}`);return lines}var CAPTURE_TAIL_BYTES=4096;function run(cmd,args,opts){return new Promise((resolve2)=>{let child=spawn(cmd,args,{cwd:opts.cwd,stdio:opts.inherit?["ignore","inherit","inherit"]:["ignore","pipe","pipe"]}),output="",collect=(chunk)=>{output=`${output}${String(chunk)}`.slice(-CAPTURE_TAIL_BYTES)};child.stdout?.on("data",collect),child.stderr?.on("data",collect),child.on("error",(err)=>resolve2({code:null,output:`${output}${err.message}`})),child.on("close",(code)=>resolve2({code,output}))})}function outputTail(output){let trimmed=output.trim();return trimmed?`: ${trimmed.split(`
3
3
  `).slice(-3).join(" / ")}`:""}async function git(args,opts){let{code,output}=await run("git",args,opts);if(code!==0)fail(`git ${args[0]} failed${outputTail(output)}`)}async function runBuildCommands(parsed,root,opts){let platform=currentPluginPlatform();for(let[i,step]of parsed.manifest.build.entries()){if(!supportsPlatform(step,parsed.manifest,platform))continue;let[cmd,...args]=step.command;if(opts.inherit)console.log(`build[${i}]: ${step.command.join(" ")}`);let{code,output}=await run(cmd,args,{cwd:root,inherit:opts.inherit});if(code!==0)fail(`build[${i}] failed (exit ${code??"spawn error"}); plugin not registered${outputTail(output)}`)}}function makeStagingDir(){let root=pluginsRootDir();return mkdirSync(root,{recursive:!0}),mkdtempSync(join(root,".staging-"))}function movePluginTree(from,to){try{renameSync(from,to)}catch(err){if(err.code!=="EXDEV")throw err;cpSync(from,to,{recursive:!0,verbatimSymlinks:!0}),rmSync(from,{recursive:!0,force:!0})}}function register(entry){savePluginRegistry(upsertPluginEntry(loadPluginRegistry(),entry)),mkdirSync(pluginConfigDir(entry.id),{recursive:!0,mode:448}),mkdirSync(pluginStateDir(entry.id),{recursive:!0,mode:448})}async function preparePluginInstall(spec,opts={}){let match=spec.match(GITHUB_SPEC_RE);if(!match)fail(`install takes GitHub shorthand (owner/repo[/subdir]), got \`${spec}\``);let[,owner,repo,subdirRaw]=match,subdir=subdirRaw?.replace(/^\//,""),inherit=opts.inherit??!1,tmp=makeStagingDir();try{let url=`https://github.com/${owner}/${repo}.git`;if(inherit)console.log(`cloning ${url}${opts.ref?` @ ${opts.ref}`:""}`);if(opts.ref)await git(["clone","--quiet",url,tmp],{inherit}),await git(["checkout","--quiet",opts.ref],{cwd:tmp,inherit});else await git(["clone","--quiet","--depth","1",url,tmp],{inherit});let rootInClone=subdir?join(tmp,subdir):tmp,parsed=readManifestAt(rootInClone);checkVersionGate(parsed);let id=parsed.manifest.id,existing=loadPluginRegistry().plugins.find((p)=>p.id===id);if(existing?.source.kind==="link")fail(`\`${id}\` is locally linked at ${existing.root}; unlink it first`);if(!supportsPlatform({},parsed.manifest,currentPluginPlatform()))fail(`plugin declares platforms [${parsed.manifest.platforms?.join(", ")}]; this machine is unsupported`);return{preview:{id,name:parsed.manifest.name,version:parsed.manifest.version,description:parsed.manifest.description??null,source:`github.com/${owner}/${repo}${subdir?`/${subdir}`:""}`,commands:commandLines(parsed),warnings:parsed.warnings},commit:async()=>{try{await runBuildCommands(parsed,rootInClone,{inherit});let manifestPath=pluginManifestPath(rootInClone);if(!manifestPath)fail("manifest disappeared during build; aborting");if(manifestPath!==parsed.manifestPath)fail("manifest changed during build; aborting");if(readFileSync(manifestPath,"utf8")!==parsed.manifestSource)fail("manifest changed during build; aborting");let checkout=pluginCheckoutDir(id);return rmSync(checkout,{recursive:!0,force:!0}),mkdirSync(join(checkout,".."),{recursive:!0}),movePluginTree(tmp,checkout),register({id,source:{kind:"github",spec},root:subdir?join(checkout,subdir):checkout,enabled:!0,version:parsed.manifest.version,installedAt:Date.now()}),id}finally{rmSync(tmp,{recursive:!0,force:!0})}},discard:()=>rmSync(tmp,{recursive:!0,force:!0})}}catch(err){throw rmSync(tmp,{recursive:!0,force:!0}),err}}function printPreview(preview){console.log(`
4
4
  ${preview.name} (${preview.id}) v${preview.version} \u2014 ${preview.description??"no description"}`),console.log(`source: ${preview.source}`);for(let line of preview.commands)console.log(` ${line}`);for(let w of preview.warnings)console.log(` warning: ${w}`);console.log("")}async function confirmOrBail(yes){if(yes)return;if(!process.stdin.isTTY)fail("refusing to install without confirmation in a non-interactive terminal; pass --yes");let rl=createInterface({input:process.stdin,output:process.stdout}),answer=(await rl.question("Install this plugin and run its build commands? [y/N] ")).trim().toLowerCase();if(rl.close(),answer!=="y"&&answer!=="yes")fail("aborted")}async function installPlugin(spec,opts){let prepared=await preparePluginInstall(spec,{ref:opts.ref,inherit:!0});printPreview(prepared.preview);try{await confirmOrBail(opts.yes)}catch(err){throw prepared.discard(),err}let id=await prepared.commit();return console.log(`installed ${id} v${prepared.preview.version}`),id}function linkPlugin(dir){let root=resolve(dir);if(!existsSync(root))fail(`no such directory: ${root}`);let parsed=readManifestAt(root);checkVersionGate(parsed);let id=parsed.manifest.id;if(loadPluginRegistry().plugins.find((p)=>p.id===id)?.source.kind==="github")fail(`\`${id}\` is installed from GitHub; uninstall it before linking a local copy`);for(let w of parsed.warnings)console.log(`warning: ${w}`);if(!supportsPlatform({},parsed.manifest,currentPluginPlatform()))console.log(`warning: declares platforms [${parsed.manifest.platforms?.join(", ")}]; nothing will run on this machine`);register({id,source:{kind:"link"},root,enabled:!0,version:parsed.manifest.version,installedAt:Date.now()}),console.log(`linked ${id} v${parsed.manifest.version} \u2192 ${root}`)}
5
5
  export{PluginCliError,preparePluginInstall,installPlugin,linkPlugin};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{RoveDaemonClient}from"./chunk-y33j54yr.js";import{defaultDaemonSocketPath,defaultPtyHostSocketPath}from"./chunk-z7zm8h1q.js";import{spawn as spawn2}from"child_process";import{appendFileSync,closeSync,mkdirSync,openSync}from"fs";import{dirname}from"path";import{join,resolve}from"path";var PTY_JOB_ENV="ROVE_PTY_JOB",PTY_JOB_OWNER_ENV="ROVE_PTY_JOB_OWNER",PTY_JOB_LAUNCHER_CS=`
2
+ import{RoveDaemonClient}from"./chunk-fnsch8kr.js";import{defaultDaemonSocketPath,defaultPtyHostSocketPath}from"./chunk-z7zm8h1q.js";import{spawn as spawn2}from"child_process";import{appendFileSync,closeSync,mkdirSync,openSync}from"fs";import{dirname}from"path";import{join,resolve}from"path";var PTY_JOB_ENV="ROVE_PTY_JOB",PTY_JOB_OWNER_ENV="ROVE_PTY_JOB_OWNER",PTY_JOB_LAUNCHER_CS=`
3
3
  using System;
4
4
  using System.Runtime.InteropServices;
5
5
  using System.Text;
@@ -93,4 +93,4 @@ public static class RoveDetachedLaunch {
93
93
  [RoveDetachedLaunch]::Launch($cmd, $log) | Out-Null
94
94
  `;function windowsCommandLine(argv){return argv.map(quoteWindowsArg).join(" ")}function quoteWindowsArg(arg){if(arg!==""&&!/[\s"]/.test(arg))return arg;let out='"',backslashes=0;for(let ch of arg){if(ch==="\\"){backslashes++;continue}if(ch==='"'){out+=`${"\\".repeat(backslashes*2+1)}"`,backslashes=0;continue}out+="\\".repeat(backslashes)+ch,backslashes=0}return`${out}${"\\".repeat(backslashes*2)}"`}function windowsPowershellPath(env=process.env,exists=existsSync){let root=env.SystemRoot||env.windir||"C:\\Windows",absolute=join2(root,"System32","WindowsPowerShell","v1.0","powershell.exe");return exists(absolute)?absolute:"powershell.exe"}function encodePowershellCommand(script){return Buffer.from(script,"utf16le").toString("base64")}function windowsDetachedLauncherArgs(){return["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand",encodePowershellCommand(WIN_DETACHED_LAUNCHER_PS)]}function spawnWindowsDetached(command,args,env,logPath,spawnImpl=spawn){let child=spawnImpl(windowsPowershellPath(env),windowsDetachedLauncherArgs(),{windowsHide:!0,stdio:"ignore",env:{...env,[LAUNCH_CMD_ENV]:windowsCommandLine([command,...args]),[LAUNCH_LOG_ENV]:logPath}});return child.unref(),child}var ESCAPE_REQUEST_TIMEOUT_MS=5000;function appendLaunchLog(logPath,line){try{appendFileSync(logPath,`[${new Date().toISOString()}] rove: ${line}
95
95
  `)}catch{}}async function launchViaPtyHost(command,args,env,logPath,socketPath){let client=new RoveDaemonClient(socketPath),timer;try{await Promise.race([client.request("spawn.detached",{command,args:[...args],env,logPath}),new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error("timed out")),ESCAPE_REQUEST_TIMEOUT_MS)})])}finally{if(timer)clearTimeout(timer);client.close()}}function detachOptions(platform=process.platform){return platform==="win32"?{windowsHide:!0}:{detached:!0}}function spawnDetachedDaemon(command,args,env,logPath,platform=process.platform){try{mkdirSync(dirname(logPath),{recursive:!0})}catch{}if(platform==="win32"){if(ptyJobLaunch(env,defaultDaemonSocketPath())?.mode==="escape"){let free=withoutPtyJob(env);launchViaPtyHost(command,args,free,logPath,defaultPtyHostSocketPath()).catch((err)=>{appendLaunchLog(logPath,`PTY host could not launch outside the tab (${err.message}) \u2014 it ends with the tab`),launchWindowsDetached(command,args,free,logPath,platform)});return}launchWindowsDetached(command,args,env,logPath,platform);return}spawnDirect(command,args,env,logPath,platform)}function launchWindowsDetached(command,args,env,logPath,platform){let launcher=spawnWindowsDetached(command,args,env,logPath),fallback=(reason)=>{appendLaunchLog(logPath,`detached launcher ${reason} \u2014 spawning directly; this process will not outlive its parent`),spawnDirect(command,args,env,logPath,platform)};launcher.once("error",(err)=>fallback(`could not start (${err.message})`)),launcher.once("exit",(code,signal)=>{if(code!==0)fallback(`exited ${code??signal}`)})}function spawnDirect(command,args,env,logPath,platform){let stdio="ignore",logFd;try{mkdirSync(dirname(logPath),{recursive:!0}),logFd=openSync(logPath,"a"),stdio=["ignore",logFd,logFd]}catch{stdio="ignore"}if(spawn2(command,[...args],{...detachOptions(platform),stdio,env}).unref(),logFd!==void 0)try{closeSync(logFd)}catch{}}
96
- export{withoutPtyJob,windowsPowershellPath,spawnDetachedDaemon};
96
+ export{withoutPtyJob,windowsPowershellPath,detachOptions,spawnDetachedDaemon};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{REPEAT_SEP,VerbArgs,defaultApiRuntime,findVerb,parseFlags,unknownVerbError,validateAgainstSpec}from"./chunk-rpm771v5.js";import{ensurePluginEnginesLoaded}from"./chunk-c3d36ryd.js";import{ApiError,cliCaller,splitDaemonCode}from"./chunk-x7r75e3p.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{ROVE_API_VERSION}from"./chunk-278sc87n.js";function toApiError(err){if(err instanceof ApiError)return err;let message=errorMessage(err);if(/task not found/i.test(message))return new ApiError(message,"TASK_NOT_FOUND",{hint:"that task id does not exist (deleted or mistyped) \u2014 list live tasks and retry with a real id",nextCommandArgs:["api","list"]});if(/unknown daemon request:/i.test(message))return versionSkewError(message);let coded=splitDaemonCode(message);if(coded)return new ApiError(coded.rest,coded.code);return new ApiError(message,"RPC_ERROR")}function versionSkewError(message){return new ApiError(message,"DAEMON_VERSION_SKEW",{hint:"the running daemon is a different build than this CLI, so it does not serve this verb \u2014 restart the daemon to pick up this build, then retry (if the verb is gone for good, `rove api schema` lists what this daemon actually serves)",nextCommandArgs:["daemon","restart"]})}function errorBody(err){return{message:err.message,code:err.code,...err.data}}function successEnvelope(result){return{apiVersion:ROVE_API_VERSION,ok:!0,result}}function errorEnvelope(err){return{apiVersion:ROVE_API_VERSION,ok:!1,error:errorBody(toApiError(err))}}function flagText(name,spec,value){let type=spec?.type;if(typeof value==="string")return value;if(spec?.repeatable&&Array.isArray(value)&&value.every((item)=>typeof item==="string"))return value.join(REPEAT_SEP);if(typeof value==="boolean"&&(type==="bool"||type===void 0))return String(value);if(typeof value==="number"&&(type==="int"||type==="uint"||type===void 0))return String(value);if(Array.isArray(value)&&(type==="csv"||type===void 0)){if(!value.every((item)=>typeof item==="string"&&!item.includes(",")))throw new ApiError(`param ${name} must be a list of strings without ","`,"BAD_FLAG");return value.join(",")}throw new ApiError(`param ${name} does not fit its ${type} flag`,"BAD_FLAG")}function paramsToFlags(verb,params){let flags=new Map;for(let[name,value]of Object.entries(params))flags.set(name,flagText(name,verb.flags.find((f)=>f.name===name),value));return flags}function booleanFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.type==="bool").map((f)=>f.name))}function repeatableFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.repeatable).map((f)=>f.name))}async function callVerb(verb,flags,deps){let caller=deps.caller??cliCaller();try{return await verb.handler({args:new VerbArgs(verb,flags,caller),caller,client:deps.client,runtime:deps.runtime??defaultApiRuntime})}catch(err){throw toApiError(err)}}async function runVerb(verbName,input,deps){ensurePluginEnginesLoaded();let verb=findVerb(verbName);if(!verb)throw unknownVerbError(verbName);if(verb.cliOnly&&!(deps.caller??cliCaller()).cli)throw new ApiError(`"${verbName}" streams over stdout/stdin, so only \`rove api\` can run it`,"CLI_ONLY_VERB",{hint:"run it as a CLI command on the machine whose daemon you are talking to"});let flags="argv"in input?parseFlags(input.argv,booleanFlagsOf(verb),repeatableFlagsOf(verb)).flags:paramsToFlags(verb,input.params);return validateAgainstSpec(verb,flags),callVerb(verb,flags,deps)}async function executeVerb(verbName,input,deps){try{return successEnvelope(await runVerb(verbName,input,deps))}catch(err){return errorEnvelope(err)}}
2
+ import{REPEAT_SEP,VerbArgs,defaultApiRuntime,findVerb,parseFlags,unknownVerbError,validateAgainstSpec}from"./chunk-ekre1aps.js";import{ensurePluginEnginesLoaded}from"./chunk-5j6z32fh.js";import{ApiError,cliCaller,splitDaemonCode}from"./chunk-x7r75e3p.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{ROVE_API_VERSION}from"./chunk-278sc87n.js";function toApiError(err){if(err instanceof ApiError)return err;let message=errorMessage(err);if(/task not found/i.test(message))return new ApiError(message,"TASK_NOT_FOUND",{hint:"that task id does not exist (deleted or mistyped) \u2014 list live tasks and retry with a real id",nextCommandArgs:["api","list"]});if(/unknown daemon request:/i.test(message))return versionSkewError(message);let coded=splitDaemonCode(message);if(coded)return new ApiError(coded.rest,coded.code);return new ApiError(message,"RPC_ERROR")}function versionSkewError(message){return new ApiError(message,"DAEMON_VERSION_SKEW",{hint:"the running daemon is a different build than this CLI, so it does not serve this verb \u2014 restart the daemon to pick up this build, then retry (if the verb is gone for good, `rove api schema` lists what this daemon actually serves)",nextCommandArgs:["daemon","restart"]})}function errorBody(err){return{message:err.message,code:err.code,...err.data}}function successEnvelope(result){return{apiVersion:ROVE_API_VERSION,ok:!0,result}}function errorEnvelope(err){return{apiVersion:ROVE_API_VERSION,ok:!1,error:errorBody(toApiError(err))}}function flagText(name,spec,value){let type=spec?.type;if(typeof value==="string")return value;if(spec?.repeatable&&Array.isArray(value)&&value.every((item)=>typeof item==="string"))return value.join(REPEAT_SEP);if(typeof value==="boolean"&&(type==="bool"||type===void 0))return String(value);if(typeof value==="number"&&(type==="int"||type==="uint"||type===void 0))return String(value);if(Array.isArray(value)&&(type==="csv"||type===void 0)){if(!value.every((item)=>typeof item==="string"&&!item.includes(",")))throw new ApiError(`param ${name} must be a list of strings without ","`,"BAD_FLAG");return value.join(",")}throw new ApiError(`param ${name} does not fit its ${type} flag`,"BAD_FLAG")}function paramsToFlags(verb,params){let flags=new Map;for(let[name,value]of Object.entries(params))flags.set(name,flagText(name,verb.flags.find((f)=>f.name===name),value));return flags}function booleanFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.type==="bool").map((f)=>f.name))}function repeatableFlagsOf(verb){return new Set(verb.flags.filter((f)=>f.repeatable).map((f)=>f.name))}async function callVerb(verb,flags,deps){let caller=deps.caller??cliCaller();try{return await verb.handler({args:new VerbArgs(verb,flags,caller),caller,client:deps.client,runtime:deps.runtime??defaultApiRuntime})}catch(err){throw toApiError(err)}}async function runVerb(verbName,input,deps){ensurePluginEnginesLoaded();let verb=findVerb(verbName);if(!verb)throw unknownVerbError(verbName);if(verb.cliOnly&&!(deps.caller??cliCaller()).cli)throw new ApiError(`"${verbName}" streams over stdout/stdin, so only \`rove api\` can run it`,"CLI_ONLY_VERB",{hint:"run it as a CLI command on the machine whose daemon you are talking to"});let flags="argv"in input?parseFlags(input.argv,booleanFlagsOf(verb),repeatableFlagsOf(verb)).flags:paramsToFlags(verb,input.params);return validateAgainstSpec(verb,flags),callVerb(verb,flags,deps)}async function executeVerb(verbName,input,deps){try{return successEnvelope(await runVerb(verbName,input,deps))}catch(err){return errorEnvelope(err)}}
3
3
  export{toApiError,errorBody,successEnvelope,errorEnvelope,paramsToFlags,booleanFlagsOf,repeatableFlagsOf,callVerb,runVerb,executeVerb};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{defaultFixRuntime}from"./chunk-ffjwgbck.js";import{discoverMachine,roveCommand}from"./chunk-v9dcyjmj.js";import{addMachine,defaultMachineAlias,duplicateAliasOf,getMachine,isValidMachineAlias,listMachines,machineSocketDir,machineSshArgs,parseSshTarget,readMachines,removeMachine,sanitizeAlias,setMachineIdentity,sshTargetOf,updateMachine}from"./chunk-3jnw3ftt.js";import{shQuote}from"./chunk-b7xjhgts.js";import"./chunk-7kysxgbq.js";import{CURRENT_VERSION}from"./chunk-75zg2k73.js";import{loadStateFile}from"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import{homeDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-b3j5s2jb.js";import"./chunk-r2pd9tv9.js";import"./chunk-cmp64q4j.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{spawn}from"child_process";import{appendFileSync,chmodSync,existsSync,mkdirSync}from"fs";import{homedir}from"os";import{dirname}from"path";function parseSshG(stdout,home=homedir()){let values=new Map;for(let line of stdout.split(`
2
+ import{defaultFixRuntime}from"./chunk-80zzjcvp.js";import{discoverMachine,roveCommand}from"./chunk-v9dcyjmj.js";import{addMachine,defaultMachineAlias,duplicateAliasOf,getMachine,isValidMachineAlias,listMachines,machineSocketDir,machineSshArgs,parseSshTarget,readMachines,removeMachine,sanitizeAlias,setMachineIdentity,sshTargetOf,updateMachine}from"./chunk-3jnw3ftt.js";import{shQuote}from"./chunk-b7xjhgts.js";import"./chunk-7kysxgbq.js";import{CURRENT_VERSION}from"./chunk-c9xg9dn0.js";import{loadStateFile}from"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import{homeDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{spawn}from"child_process";import{appendFileSync,chmodSync,existsSync,mkdirSync}from"fs";import{homedir}from"os";import{dirname}from"path";function parseSshG(stdout,home=homedir()){let values=new Map;for(let line of stdout.split(`
3
3
  `)){let space=line.indexOf(" ");if(space>0)values.set(line.slice(0,space).toLowerCase(),line.slice(space+1).trim())}let hostname=values.get("hostname")??"",port=Number(values.get("port")??"22")||22,files=(key)=>(values.get(key)??"").split(/\s+/).filter((f)=>f&&f!=="none").map((f)=>f.startsWith("~/")?`${home}${f.slice(1)}`:f),alias=values.get("hostkeyalias"),proxy=(key)=>{let v=values.get(key);return v!==void 0&&v!=="none"};return{knownHostsName:alias&&alias!=="none"?alias:port===22?hostname:`[${hostname}]:${port}`,hostname,port,userKnownHosts:files("userknownhostsfile"),globalKnownHosts:files("globalknownhostsfile"),proxied:proxy("proxyjump")||proxy("proxycommand")}}async function checkHostKey(config){let target=sshTargetOf(config),resolved=await run(["ssh","-G",...config.port?["-p",String(config.port)]:[],target]);if(resolved.code!==0)return{state:"skipped",reason:"ssh -G failed"};let destination=parseSshG(resolved.stdout);if(!destination.hostname)return{state:"skipped",reason:"ssh -G named no host"};if(destination.proxied)return{state:"skipped",reason:"reached through a proxy"};for(let file of[...destination.userKnownHosts,...destination.globalKnownHosts]){if(!existsSync(file))continue;if((await run(["ssh-keygen","-F",destination.knownHostsName,"-f",file])).code===0)return{state:"known"}}let keys=(await run(["ssh-keyscan","-T","5","-p",String(destination.port),destination.hostname])).stdout.split(`
4
4
  `).filter((line)=>line.trim()&&!line.startsWith("#"));if(keys.length===0)return{state:"skipped",reason:"the host offered no key"};let named=keys.map((line)=>line.replace(/^\S+/,destination.knownHostsName)),fingerprints=(await run(["ssh-keygen","-l","-f","-"],`${named.join(`
5
5
  `)}
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{preferredReleaseChannel,rememberReleaseChannel}from"./chunk-avdg2rrh.js";import{updaterShell,updaterShellFailureHint}from"./chunk-814ktvx2.js";import"./chunk-vjvkkrzc.js";import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,breakingVersionsCrossed,channelOf,checkLatestVersion,fetchReleaseSummaries,recommendedGlobalInstallCommand,updateCommandFor}from"./chunk-75zg2k73.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{spawnSync}from"child_process";var CLI_NAME=ROVE_PRODUCT_NAME;function updatePlan(target){let shell=updateCommandFor(target);return{command:updaterShell(),args:["-c",shell],display:shell}}var VERSION_SHAPE=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function isReleaseChannel(value){return RELEASE_CHANNELS.includes(value)}var CHANNEL_FLAGS={"--nightly":"nightly","--stable":"latest","--latest":"latest",stable:"latest"};function parseUpdateArgs(args){let dryRun=!1,list=!1,version,channel;for(let i=0;i<args.length;i+=1){let arg=args[i];if(arg===void 0)continue;if(arg==="--help"||arg==="-h"||arg==="help")return{help:!0,dryRun,list,version,channel};if(arg==="dry-run"||arg==="--dry-run"){dryRun=!0;continue}if(arg==="list"||arg==="--list"){list=!0;continue}if(arg==="--channel"||arg.startsWith("--channel=")){let value=(arg.startsWith("--channel=")?arg.slice(10):void 0)??args[++i];if(value===void 0||!isReleaseChannel(value))process.stderr.write(`${CLI_NAME} update: --channel expects one of ${RELEASE_CHANNELS.join(", ")}${value===void 0?"":` (got "${value}")`}
2
+ import{preferredReleaseChannel,rememberReleaseChannel}from"./chunk-1s8ctwmv.js";import{updaterShell,updaterShellFailureHint}from"./chunk-cqdebtbr.js";import"./chunk-f5n0tgky.js";import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,breakingVersionsCrossed,channelOf,checkLatestVersion,fetchReleaseSummaries,recommendedGlobalInstallCommand,updateCommandFor}from"./chunk-c9xg9dn0.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{spawnSync}from"child_process";var CLI_NAME=ROVE_PRODUCT_NAME;function updatePlan(target){let shell=updateCommandFor(target);return{command:updaterShell(),args:["-c",shell],display:shell}}var VERSION_SHAPE=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function isReleaseChannel(value){return RELEASE_CHANNELS.includes(value)}var CHANNEL_FLAGS={"--nightly":"nightly","--stable":"latest","--latest":"latest",stable:"latest"};function parseUpdateArgs(args){let dryRun=!1,list=!1,version,channel;for(let i=0;i<args.length;i+=1){let arg=args[i];if(arg===void 0)continue;if(arg==="--help"||arg==="-h"||arg==="help")return{help:!0,dryRun,list,version,channel};if(arg==="dry-run"||arg==="--dry-run"){dryRun=!0;continue}if(arg==="list"||arg==="--list"){list=!0;continue}if(arg==="--channel"||arg.startsWith("--channel=")){let value=(arg.startsWith("--channel=")?arg.slice(10):void 0)??args[++i];if(value===void 0||!isReleaseChannel(value))process.stderr.write(`${CLI_NAME} update: --channel expects one of ${RELEASE_CHANNELS.join(", ")}${value===void 0?"":` (got "${value}")`}
3
3
 
4
4
  `),printUsage(process.stderr),process.exit(2);channel=value;continue}let flagged=CHANNEL_FLAGS[arg];if(channel===void 0&&flagged!==void 0){channel=flagged;continue}if(channel===void 0&&isReleaseChannel(arg)){channel=arg;continue}if(version===void 0&&VERSION_SHAPE.test(arg)){version=arg;continue}process.stderr.write(`${CLI_NAME} update: unknown argument "${arg}"
5
5
 
@@ -10,7 +10,7 @@ import{preferredReleaseChannel,rememberReleaseChannel}from"./chunk-avdg2rrh.js";
10
10
  install one with: ${CLI_NAME} update <version>
11
11
  `)}async function warnBreakingCrossings(target,channel,io){if(BREAKING_VERSIONS.length===0)return;let resolved=target??(await checkLatestVersion({force:!0,channel}))?.latest;if(!resolved)return;let crossed=breakingVersionsCrossed(CURRENT_VERSION,resolved);if(crossed.length===0)return;io.stderr.write([`warning: ${CURRENT_VERSION} -> ${resolved} crosses breaking version(s): ${crossed.join(", ")}.`,`After this update, Rove will refuse to start until you run \`${CLI_NAME} reset\``,"(worktrees are never touched; add --hard only to also wipe the task index).",""].join(`
12
12
  `))}var FOLLOW_UP_NOTE=["",`${CLI_NAME}: installed. Two background processes are still running the old build:`,` daemon \u2192 \`${CLI_NAME} daemon restart\` (safe; never touches live sessions)`,` pty host \u2192 only \`${CLI_NAME} reset\` replaces it, and that ends every live`," terminal and engine session \u2014 do it when you can afford to.",`Run \`${CLI_NAME} doctor\` to see which of the two is actually stale.`,""].join(`
13
- `);async function runUpdateSubcommand(args,deps){let io={spawn:deps?.spawn??spawnSync,stdout:deps?.stdout??process.stdout,stderr:deps?.stderr??process.stderr,exit:deps?.exit??((code)=>process.exit(code)),channel:deps?.channel??{get:()=>preferredReleaseChannel(),remember:rememberReleaseChannel}},parsed=parseUpdateArgs(args);if(parsed.help){printUsage(io.stdout);return}if(parsed.list){if(deps===void 0&&process.stdout.isTTY){let{startVersionsHost}=await import("./chunk-6k424rbf.js");await startVersionsHost();return}await printVersionList(io);return}let channel=parsed.channel??io.channel.get(),target=parsed.version??(channel===DEFAULT_RELEASE_CHANNEL?void 0:channel),plan=updatePlan(target),switching=parsed.version===void 0&&channel!==channelOf();if(io.stdout.write(`${CLI_NAME} ${CURRENT_VERSION} -> ${target??channel}
13
+ `);async function runUpdateSubcommand(args,deps){let io={spawn:deps?.spawn??spawnSync,stdout:deps?.stdout??process.stdout,stderr:deps?.stderr??process.stderr,exit:deps?.exit??((code)=>process.exit(code)),channel:deps?.channel??{get:()=>preferredReleaseChannel(),remember:rememberReleaseChannel}},parsed=parseUpdateArgs(args);if(parsed.help){printUsage(io.stdout);return}if(parsed.list){if(deps===void 0&&process.stdout.isTTY){let{startVersionsHost}=await import("./chunk-bampejxv.js");await startVersionsHost();return}await printVersionList(io);return}let channel=parsed.channel??io.channel.get(),target=parsed.version??(channel===DEFAULT_RELEASE_CHANNEL?void 0:channel),plan=updatePlan(target),switching=parsed.version===void 0&&channel!==channelOf();if(io.stdout.write(`${CLI_NAME} ${CURRENT_VERSION} -> ${target??channel}
14
14
  `),switching)io.stdout.write(`switching channel: ${channelOf()} -> ${channel}
15
15
  `);if(io.stdout.write(`running: ${plan.display}
16
16
  `),await warnBreakingCrossings(parsed.version,channel,io),parsed.dryRun)return;let result=io.spawn(plan.command,plan.args,{stdio:"inherit"});if(result.error){io.stderr.write(`${CLI_NAME} update: failed to run ${plan.command}: ${result.error.message}
package/dist/cli/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{matchPathGlob}from"./chunk-kg11c1py.js";import{failureHint}from"./chunk-3dgghmjv.js";import{switchFlag}from"./chunk-zsfyj36g.js";import{connectIfRunning}from"./chunk-ad5gbdcf.js";import"./chunk-19h18xca.js";import"./chunk-p98w5ffk.js";import{expandTilde}from"./chunk-ag3txfg2.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{BUILTIN_VENDORS,coerceVendorId,isBuiltinVendor}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-75zg2k73.js";import{rejectionReason}from"./chunk-3342qzve.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-b3j5s2jb.js";import"./chunk-r2pd9tv9.js";import"./chunk-cmp64q4j.js";import"./chunk-y33j54yr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{resolve as resolve2}from"path";function debugEnabled(env){return env.ROVE_DEBUG==="1"||env.ROVE_DEBUG==="1"}function failureSubject(cliName,argv){let verb=argv[2];return verb&&!verb.startsWith("-")&&!verb.includes("/")?`${cliName} ${verb}`:cliName}function summarizeCliGitError(message,cwd){if(!/not a git repository/i.test(message))return null;return`${cwd} is not a git repository \u2014 run this inside one, or pass a repo path.`}function formatCliFailure(err,opts){let subject=failureSubject(opts.cliName,opts.argv);if(debugEnabled(opts.env))return`${subject}: ${String(err instanceof Error?err.stack??err:err)}
3
+ import{matchPathGlob}from"./chunk-kg11c1py.js";import{failureHint}from"./chunk-v7prawj1.js";import{switchFlag}from"./chunk-zsfyj36g.js";import{connectIfRunning}from"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import{expandTilde}from"./chunk-ag3txfg2.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{BUILTIN_VENDORS,coerceVendorId,isBuiltinVendor}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-c9xg9dn0.js";import{rejectionReason}from"./chunk-3342qzve.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{resolve as resolve2}from"path";function debugEnabled(env){return env.ROVE_DEBUG==="1"||env.ROVE_DEBUG==="1"}function failureSubject(cliName,argv){let verb=argv[2];return verb&&!verb.startsWith("-")&&!verb.includes("/")?`${cliName} ${verb}`:cliName}function summarizeCliGitError(message,cwd){if(!/not a git repository/i.test(message))return null;return`${cwd} is not a git repository \u2014 run this inside one, or pass a repo path.`}function formatCliFailure(err,opts){let subject=failureSubject(opts.cliName,opts.argv);if(debugEnabled(opts.env))return`${subject}: ${String(err instanceof Error?err.stack??err:err)}
4
4
  ${failureHint(err,opts.cliName)}`;let raw=errorMessage(err),git=summarizeCliGitError(raw,opts.cwd);return`${subject}: ${git??`${raw}
5
- ${failureHint(err,opts.cliName)}`}`}var DYNAMIC_COMMANDS=new Map([["completions",async(args)=>{let{runCompletionsSubcommand}=await import("./chunk-sk3h2pzj.js");await runCompletionsSubcommand(args)}],["export",async(args)=>{let{runExportSubcommand}=await import("./chunk-wbtebbrv.js");await runExportSubcommand(args)}],["repo",async(args)=>{let{runRepoSubcommand}=await import("./chunk-j26zqrnq.js");await runRepoSubcommand(args)}],["api",async(args)=>{let{runApiSubcommand}=await import("./chunk-9jbcfrse.js");await runApiSubcommand(args)}],["update",async(args)=>{let{runUpdateSubcommand}=await import("./chunk-mt8vtz20.js");await runUpdateSubcommand(args)}],["theme",async(args)=>{let{runThemeSubcommand}=await import("./chunk-7cd1epfw.js");await runThemeSubcommand(args)}],["feedback",async(args)=>{let{runFeedbackSubcommand}=await import("./chunk-dkb8nj75.js");await runFeedbackSubcommand(args)}],["daemon",async(args)=>{let{runDaemonSubcommand}=await import("./chunk-g3wdrhxk.js");await runDaemonSubcommand(args)}],["machine",async(args)=>{let{runMachineSubcommand}=await import("./chunk-bk4pfjs0.js");await runMachineSubcommand(args)}],["weixin",async(args)=>{let{runChannelCli}=await import("./chunk-k8dm013e.js");await runChannelCli("weixin",args)}],["doctor",async(args)=>{let{runDoctorSubcommand}=await import("./chunk-hbq7nqnp.js");await runDoctorSubcommand(args)}],["config",async(args)=>{let{runConfigSubcommand}=await import("./chunk-yshp8cmx.js");await runConfigSubcommand(args)}],["reset",async(args)=>{let{runResetSubcommand}=await import("./chunk-xmf4053m.js");await runResetSubcommand(args)}],["pty-host",async(args)=>{let{runPtyHostSubcommand}=await import("./chunk-7hkkjx6g.js");await runPtyHostSubcommand(args)}],["skill",async(args)=>{let{runSkillSubcommand}=await import("./chunk-sncjkw3g.js");await runSkillSubcommand(args)}],["plugin",async(args)=>{let{runPluginSubcommand}=await import("./chunk-rm2wy5hf.js");await runPluginSubcommand(args)}],["hook",async(args)=>{let{runHookSubcommand}=await import("./chunk-34vpdm19.js");await runHookSubcommand(args)}]]);import{statSync}from"fs";import{resolve}from"path";async function openLocalOrchestrator(){let{TaskIndexStore}=await import("./chunk-v8s0s21v.js"),{GitWorktreeManager}=await import("./chunk-qb6ktr8a.js"),{Orchestrator}=await import("./chunk-cjxvb9vg.js"),store=new TaskIndexStore;return await store.load(),new Orchestrator({store,worktrees:new GitWorktreeManager})}async function withDaemonOrLocal(bridge){let client=await connectIfRunning();if(client)try{return await bridge.daemon(client)}finally{client.close()}let orch=await openLocalOrchestrator();return bridge.local(orch)}function isPathLikeArg(arg){return arg==="."||arg===".."||arg.startsWith("./")||arg.startsWith("../")||arg.startsWith(".\\")||arg.startsWith("..\\")||pathSyntax(arg).isAbsolute(arg)||arg.startsWith("/")||arg==="~"||arg.startsWith("~/")||arg.startsWith("~\\")}async function projectRootFor(dir){let{isGitRepo,resolveRepoRoot}=await import("./chunk-afwh7as3.js"),{projectRejection}=await import("./chunk-4z01vg3t.js"),top=resolveRepoRoot(dir);if(top!==dir)return null;return projectRejection(top,isGitRepo,"explicit")?null:top}async function runOpenDirectory(arg){let dir=resolve(process.cwd(),expandTilde(arg)),isDir=!1;try{isDir=statSync(dir).isDirectory()}catch{isDir=!1}if(!isDir)process.stderr.write(`${ROVE_PRODUCT_NAME}: "${arg}" is not a directory (resolved to ${dir}).
6
- `),process.exit(1);let projectRoot=await projectRootFor(dir);await withDaemonOrLocal({daemon:async(client)=>{let taskId;if(projectRoot)taskId=(await client.request("task.ensureMain",{repo:projectRoot})).task.id;else taskId=(await client.request("task.openDir",{dir})).taskId;await client.request("task.setActive",{taskId})},local:async(orch)=>{let task=projectRoot?await orch.ensureMainTask(projectRoot):await orch.openDirectoryTask({dir}),{writeLastActiveTaskId}=await import("./chunk-dx72asyw.js");writeLastActiveTaskId(String(task.id))}});let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-jk9b9ztf.js");await launchTui()}function topLevelUsage(cliName=ROVE_PRODUCT_NAME){return[`${cliName} ${CURRENT_VERSION}`,"",`Usage: ${cliName} [command] [options]`,"","Run with no command to launch PureTUI.",`Run \`${cliName} .\` (or \`${cliName} <path>\`) to open a directory as a standalone task.`,"","Commands:"," completions <shell> Generate shell completion script (bash/zsh/fish)"," add [path] Save a repo path for the new-task picker"," remove [path] Forget a saved project (inverse of add; non-destructive)"," adopt [glob] Import existing git worktrees as tasks"," export [--csv|--json] Print the task list (json/csv/table; daemon-free)"," repo <verb> Per-repo init script + first prompt (show|set|unset)",` api <verb> Scriptable RPC surface for agents (see \`${cliName} api --help\`)`," daemon <verb> Manage the daemon (start|stop|status|restart)"," machine <verb> Other computers running Rove (add|doctor|remove|list)"," weixin <verb> Talk to Rove from WeChat (login|logout|status|allow|deny)"," doctor [--report|--fix] Diagnose daemon/PTY/engines/git; --fix walks the remedies",` config [--path] Open ${cliName}'s config file (state.json) in your editor`," reset [--hard] Stop runtimes; optionally wipe task/UI state"," theme <verb> Manage user themes (list|add|remove)",` skill <verb> Install the ${cliName} agent skill (install|status|command|print)`," plugin <verb> Install and run plugins (install|link|list|action|\u2026)"," feedback Send feedback to GitHub Discussions",` update [version|channel|list] Self-update ${cliName}, switch channel, or list versions`,"","Options:"," -v, --version Print version"," -h, --help Print this help"," --skill Print the agent skill file and exit"," --web [bridge flags] Serve the browser client on 127.0.0.1 and print its link"].join(`
5
+ ${failureHint(err,opts.cliName)}`}`}var DYNAMIC_COMMANDS=new Map([["completions",async(args)=>{let{runCompletionsSubcommand}=await import("./chunk-w9grtqm8.js");await runCompletionsSubcommand(args)}],["export",async(args)=>{let{runExportSubcommand}=await import("./chunk-wbtebbrv.js");await runExportSubcommand(args)}],["repo",async(args)=>{let{runRepoSubcommand}=await import("./chunk-j26zqrnq.js");await runRepoSubcommand(args)}],["api",async(args)=>{let{runApiSubcommand}=await import("./chunk-pw10r9fk.js");await runApiSubcommand(args)}],["update",async(args)=>{let{runUpdateSubcommand}=await import("./chunk-z41s748k.js");await runUpdateSubcommand(args)}],["theme",async(args)=>{let{runThemeSubcommand}=await import("./chunk-7cd1epfw.js");await runThemeSubcommand(args)}],["feedback",async(args)=>{let{runFeedbackSubcommand}=await import("./chunk-wy5xx1ez.js");await runFeedbackSubcommand(args)}],["daemon",async(args)=>{let{runDaemonSubcommand}=await import("./chunk-3aecx6za.js");await runDaemonSubcommand(args)}],["machine",async(args)=>{let{runMachineSubcommand}=await import("./chunk-ypzac0kj.js");await runMachineSubcommand(args)}],["weixin",async(args)=>{let{runChannelCli}=await import("./chunk-q800jxzk.js");await runChannelCli("weixin",args)}],["doctor",async(args)=>{let{runDoctorSubcommand}=await import("./chunk-h0h51emh.js");await runDoctorSubcommand(args)}],["config",async(args)=>{let{runConfigSubcommand}=await import("./chunk-yshp8cmx.js");await runConfigSubcommand(args)}],["reset",async(args)=>{let{runResetSubcommand}=await import("./chunk-09v8w5jq.js");await runResetSubcommand(args)}],["pty-host",async(args)=>{let{runPtyHostSubcommand}=await import("./chunk-vydwyjz2.js");await runPtyHostSubcommand(args)}],["skill",async(args)=>{let{runSkillSubcommand}=await import("./chunk-0yyvfpcg.js");await runSkillSubcommand(args)}],["plugin",async(args)=>{let{runPluginSubcommand}=await import("./chunk-3x0hyz6y.js");await runPluginSubcommand(args)}],["hook",async(args)=>{let{runHookSubcommand}=await import("./chunk-6d5csfp4.js");await runHookSubcommand(args)}]]);import{statSync}from"fs";import{resolve}from"path";async function openLocalOrchestrator(){let{TaskIndexStore}=await import("./chunk-v8s0s21v.js"),{GitWorktreeManager}=await import("./chunk-qb6ktr8a.js"),{Orchestrator}=await import("./chunk-cjxvb9vg.js"),store=new TaskIndexStore;return await store.load(),new Orchestrator({store,worktrees:new GitWorktreeManager})}async function withDaemonOrLocal(bridge){let client=await connectIfRunning();if(client)try{return await bridge.daemon(client)}finally{client.close()}let orch=await openLocalOrchestrator();return bridge.local(orch)}function isPathLikeArg(arg){return arg==="."||arg===".."||arg.startsWith("./")||arg.startsWith("../")||arg.startsWith(".\\")||arg.startsWith("..\\")||pathSyntax(arg).isAbsolute(arg)||arg.startsWith("/")||arg==="~"||arg.startsWith("~/")||arg.startsWith("~\\")}async function projectRootFor(dir){let{isGitRepo,resolveRepoRoot}=await import("./chunk-afwh7as3.js"),{projectRejection}=await import("./chunk-4z01vg3t.js"),top=resolveRepoRoot(dir);if(top!==dir)return null;return projectRejection(top,isGitRepo,"explicit")?null:top}async function runOpenDirectory(arg){let dir=resolve(process.cwd(),expandTilde(arg)),isDir=!1;try{isDir=statSync(dir).isDirectory()}catch{isDir=!1}if(!isDir)process.stderr.write(`${ROVE_PRODUCT_NAME}: "${arg}" is not a directory (resolved to ${dir}).
6
+ `),process.exit(1);let projectRoot=await projectRootFor(dir);await withDaemonOrLocal({daemon:async(client)=>{let taskId;if(projectRoot)taskId=(await client.request("task.ensureMain",{repo:projectRoot})).task.id;else taskId=(await client.request("task.openDir",{dir})).taskId;await client.request("task.setActive",{taskId})},local:async(orch)=>{let task=projectRoot?await orch.ensureMainTask(projectRoot):await orch.openDirectoryTask({dir}),{writeLastActiveTaskId}=await import("./chunk-dx72asyw.js");writeLastActiveTaskId(String(task.id))}});let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-se20ckc5.js");await launchTui()}function topLevelUsage(cliName=ROVE_PRODUCT_NAME){return[`${cliName} ${CURRENT_VERSION}`,"",`Usage: ${cliName} [command] [options]`,"","Run with no command to launch PureTUI.",`Run \`${cliName} .\` (or \`${cliName} <path>\`) to open a directory as a standalone task.`,"","Commands:"," completions <shell> Generate shell completion script (bash/zsh/fish)"," add [path] Save a repo path for the new-task picker"," remove [path] Forget a saved project (inverse of add; non-destructive)"," adopt [glob] Import existing git worktrees as tasks"," export [--csv|--json] Print the task list (json/csv/table; daemon-free)"," repo <verb> Per-repo init script + first prompt (show|set|unset)",` api <verb> Scriptable RPC surface for agents (see \`${cliName} api --help\`)`," daemon <verb> Manage the daemon (start|stop|status|restart)"," machine <verb> Other computers running Rove (add|doctor|remove|list)"," weixin <verb> Talk to Rove from WeChat (login|logout|status|allow|deny)"," doctor [--report|--fix] Diagnose daemon/PTY/engines/git; --fix walks the remedies",` config [--path] Open ${cliName}'s config file (state.json) in your editor`," reset [--hard] Stop runtimes; optionally wipe task/UI state"," theme <verb> Manage user themes (list|add|remove)",` skill <verb> Install the ${cliName} agent skill (install|status|command|print)`," plugin <verb> Install and run plugins (install|link|list|action|\u2026)"," feedback Send feedback to GitHub Discussions",` update [version|channel|list] Self-update ${cliName}, switch channel, or list versions`,"","Options:"," -v, --version Print version"," -h, --help Print this help"," --skill Print the agent skill file and exit"," --web [bridge flags] Serve the browser client on 127.0.0.1 and print its link"].join(`
7
7
  `)}var CLI_NAME=ROVE_PRODUCT_NAME,ADD_USAGE=[`Usage: ${CLI_NAME} add [path]`,` ${CLI_NAME} add --remote --host <host> --user <user> --path <basePath> [--port N] [--key [path] | --password]`,"","Save a repo for the new-task picker. With --remote, register an SSH-backed","project whose worktrees live on <host>; engine launch over SSH is pending.",""].join(`
8
8
  `);async function runAddSubcommand(rest){let arg=rest[0];if(arg==="--help"||arg==="-h"||arg==="help"){process.stdout.write(ADD_USAGE);return}if(arg==="--remote"){let{runAddRemote}=await import("./chunk-xpxxgzw9.js");await runAddRemote(rest.slice(1));return}if(arg?.startsWith("-"))process.stderr.write(`${CLI_NAME} add: unknown flag "${arg}"
9
9
 
@@ -26,4 +26,4 @@ ${ADOPT_USAGE}
26
26
  pass a path glob to adopt, e.g. ${CLI_NAME} adopt '${repo}/*' --yes`);return}let matched=worktrees.filter(isMatch);if(matched.length===0){console.log(`
27
27
  no worktrees match glob ${JSON.stringify(glob)}`);return}if(!yes){console.log(`
28
28
  ${matched.length} worktree(s) match \u2014 re-run with --yes to adopt them`);return}await adoptWorktreesInto(orch,repo,matched,vendor),console.log(`done \u2014 adopted ${matched.length} worktree(s)`)}function printTopLevelUsage(out){out.write(`${topLevelUsage()}
29
- `)}var COMMANDS=new Map([["add",runAddSubcommand],["remove",runRemoveSubcommand],["adopt",runAdoptSubcommand],...DYNAMIC_COMMANDS]);async function main(){let[,,...rawArgs]=process.argv,[subcommand,...rest]=rawArgs;if(subcommand==="--version"||subcommand==="-v"||subcommand==="version"){let{CURRENT_VERSION:CURRENT_VERSION2}=await import("./chunk-8wze6dnr.js");console.log(`${CLI_NAME} ${CURRENT_VERSION2}`);return}if(subcommand==="--help"||subcommand==="-h"||subcommand==="help"){printTopLevelUsage(process.stdout);return}if(subcommand==="--skill"){let{runSkillSubcommand}=await import("./chunk-sncjkw3g.js");await runSkillSubcommand(["print"]);return}if(subcommand==="--web"){let{runWebSubcommand}=await import("./chunk-76adkzyf.js");await runWebSubcommand(rest);return}let handler=subcommand!==void 0?COMMANDS.get(subcommand):void 0;if(handler){await handler(rest);return}if(subcommand!==void 0&&isPathLikeArg(subcommand)){await runOpenDirectory(subcommand);return}if(subcommand!==void 0)console.error(`${CLI_NAME}: unknown command '${subcommand}'`),printTopLevelUsage(process.stderr),process.exit(2);let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-jk9b9ztf.js");await launchTui()}main().catch((err)=>{console.error(formatCliFailure(err,{cliName:CLI_NAME,argv:process.argv,cwd:process.cwd(),env:process.env})),process.exit(1)});
29
+ `)}var COMMANDS=new Map([["add",runAddSubcommand],["remove",runRemoveSubcommand],["adopt",runAdoptSubcommand],...DYNAMIC_COMMANDS]);async function main(){let[,,...rawArgs]=process.argv,[subcommand,...rest]=rawArgs;if(subcommand==="--version"||subcommand==="-v"||subcommand==="version"){let{CURRENT_VERSION:CURRENT_VERSION2}=await import("./chunk-ptaj848y.js");console.log(`${CLI_NAME} ${CURRENT_VERSION2}`);return}if(subcommand==="--help"||subcommand==="-h"||subcommand==="help"){printTopLevelUsage(process.stdout);return}if(subcommand==="--skill"){let{runSkillSubcommand}=await import("./chunk-0yyvfpcg.js");await runSkillSubcommand(["print"]);return}if(subcommand==="--web"){let{runWebSubcommand}=await import("./chunk-btpp4geb.js");await runWebSubcommand(rest);return}let handler=subcommand!==void 0?COMMANDS.get(subcommand):void 0;if(handler){await handler(rest);return}if(subcommand!==void 0&&isPathLikeArg(subcommand)){await runOpenDirectory(subcommand);return}if(subcommand!==void 0)console.error(`${CLI_NAME}: unknown command '${subcommand}'`),printTopLevelUsage(process.stderr),process.exit(2);let{publishRoveTerminalTitle}=await import("./chunk-g8wrdp7n.js");publishRoveTerminalTitle();let{launchTui}=await import("./chunk-se20ckc5.js");await launchTui()}main().catch((err)=>{console.error(formatCliFailure(err,{cliName:CLI_NAME,argv:process.argv,cwd:process.cwd(),env:process.env})),process.exit(1)});
@@ -1609,8 +1609,11 @@ function resolveLoginShell(options = {}) {
1609
1609
  }
1610
1610
  function resolveUncached(fallback, platform, env, exists) {
1611
1611
  const explicit = env.SHELL?.trim();
1612
- if (platform !== "win32")
1613
- return explicit || fallback;
1612
+ if (platform !== "win32") {
1613
+ if (explicit)
1614
+ return explicit;
1615
+ return [fallback, "/bin/bash", "/bin/sh"].find((candidate) => exists(candidate)) ?? fallback;
1616
+ }
1614
1617
  if (explicit && isPosixShell(explicit) && isWindowsSpawnable(explicit) && exists(explicit))
1615
1618
  return explicit;
1616
1619
  for (const candidate of windowsBashCandidates(env)) {
package/dist/cli/rove.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{join as n}from"node:path";import{createInterface as a}from"node:readline/promises";import{pathToFileURL as ss}from"node:url";import{spawnSync as Q}from"node:child_process";import{constants as V,accessSync as R}from"node:fs";import{homedir as e}from"node:os";import{basename as I,delimiter as U,dirname as o,join as y}from"node:path";import{fileURLToPath as T}from"node:url";var H={$schema:"https://json.schemastore.org/package.json",name:"@sma1lboy/rove",version:"0.9.253",description:"Rove — the agent multiplexer for your terminal. Run coding agents on parallel tasks with isolated worktrees and persistent sessions.",keywords:["terminal","tui","cli","multiplexer","ai-agents","coding-agent","ai-coding-assistant","agentic-ai","parallel-agents","git-worktree","claude-code","codex","llm","developer-tools"],type:"module",packageManager:"bun@1.3.13",bin:{rove:"dist/cli/rove.js"},files:["dist/cli","dist/completions","dist/web","dist/web-ui","dist/skills","dist/*.wav","README.md","LICENSE"],publishConfig:{access:"public"},repository:{type:"git",url:"git+https://github.com/Sma1lboy/rove.git"},homepage:"https://rove.run",bugs:{url:"https://github.com/Sma1lboy/rove/issues"},engines:{bun:">=1.3.11"},scripts:{dev:"ROVE_DEV=1 bun --conditions=browser ./src/cli/rove.ts","dev:mock":"ROVE_DEV=1 bun ./src/tui-react/mock/host.tsx","dev:sandbox":"bun run scripts/dev-sandbox.ts run","dev:sandbox:reset":"bun run scripts/dev-sandbox.ts reset",build:"bun run scripts/build.ts","build:rubric":"bun run scripts/build-tier-rubric.ts",compile:"bun run scripts/compile.ts",typecheck:"tsc --noEmit","check-i18n":"bun run scripts/check-i18n.ts",test:"bun run test:fast && bun run test:socket","perf:golden":"bun scripts/perf-golden.ts","pty:soak":"bun scripts/pty-soak.ts","test:fast":"vitest run --passWithNoTests --minWorkers=1 --maxWorkers=8","test:socket":"ROVE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:socket:coverage":"ROVE_INCLUDE_SOCKET=1 ROVE_COVERAGE_DAEMON=1 vitest run test/daemon --coverage --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:behavior":"ROVE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --retry=2 --passWithNoTests","test:render":"bun scripts/render-track.mjs",coverage:"vitest run --coverage --passWithNoTests",bench:"vitest bench --run",lint:"biome check .",knip:"knip-bun",postinstall:"bun run scripts/check-preview-deps.ts || true",prepublishOnly:"bun run typecheck && bun run build","plugin-sandbox":"bun run scripts/plugin-sandbox.ts"},"//":"biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",dependencies:{"@ansi-tools/parser":"^1.0.15","@opentui/core":"0.4.3","@opentui/react":"0.4.3","@xterm/addon-serialize":"^0.14.0","@xterm/addon-unicode11":"^0.9.0","@xterm/headless":"^6.0.0","node-pty":"^1.1.0",react:"^19.2.8","smol-toml":"^1.7.1",ws:"^8.21.3"},devDependencies:{"@biomejs/biome":"1.9.4","@sma1lboy/rove-daemon":"0.7.22","@tsconfig/bun":"1.0.11","@types/bun":"1.3.14","@types/node":"25.6.2","@types/react":"^19.2.0","@vitest/coverage-v8":"2.1.9",knip:"^6.14.2","react-devtools-core":"^7.0.1","rove-bridge":"0.0.14","rove-weixin":"0.0.14",typescript:"5.8.2",vitest:"2.1.9"},trustedDependencies:[]};var q="rove";var k=q;var z="ROVE_BUN",_="ROVE_NO_BUN_BOOTSTRAP",F="ROVE_SKIP_BUN_CHECK",$=H.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0",L=(s)=>{try{return R(s,V.X_OK),!0}catch{return!1}},B=(s)=>s==="win32"?"bun.exe":"bun",f=(s)=>{let t=Q(s,["--version"],{encoding:"utf8",timeout:5000});if(t.error||t.status!==0)return null;return t.stdout?.split(`
3
- `)[0]?.trim()??""};function J(s){return s.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function X(s,t=$){let c=J(s),W=J(t);if(!c||!W)return!0;let b=c.split(".").map(Number),g=W.split(".").map(Number);for(let x=0;x<3;x++){let i=b[x]??0,r=g[x]??0;if(i!==r)return i>r}return!0}function Y(s=process.env){return s[F]==="1"}function A(s={}){let t=s.env??process.env,c=s.platform??process.platform,W=s.home??t.HOME??t.USERPROFILE??e(),b=B(c),g=[],x=t[z]?.trim();if(x)g.push(x);for(let G of(t.PATH??t.Path??"").split(U))if(G)g.push(y(G,b));let i=t.BUN_INSTALL?.trim();if(i)g.push(y(i,"bin",b));if(W)g.push(y(W,".bun","bin",b));let r=s.launcherDir;if(r)g.push(y(r,"..","..","node_modules","bun","bin",b)),g.push(y(r,"..","..","..","bun","bin",b));return g}function N(s={}){let t=s.isExecutable??L;return A(s).find((c)=>t(c))??null}function Z(s={}){let t=s.isExecutable??L,c=s.bunVersionOf??f;if(Y(s.env??process.env))return{bun:N(s),stale:null,unusable:null};let W=null,b=null;for(let g of A(s)){if(!t(g))continue;let x=c(g);if(x===null){b??=g;continue}if(X(x))return{bun:g,stale:W,unusable:b};W??={path:g,version:J(x)??x}}return{bun:null,stale:W,unusable:b}}function h(s){return o(T(s))}function C(s){return I(T(s)).replace(/\.[cm]?[jt]s$/,"")}function u(s=process.platform){return s==="win32"?["powershell","-NoProfile","-Command","irm bun.sh/install.ps1 | iex"]:["bash","-c","curl -fsSL https://bun.sh/install | bash"]}function D(s=k,t=process.platform){let c=t==="win32"?'powershell -c "irm bun.sh/install.ps1 | iex"':"curl -fsSL https://bun.sh/install | bash";return[`${s}: Rove runs on the Bun runtime, and no Bun was found on this machine.`,"","Install Bun, then run this command again:",` ${c}`," npm install -g bun # any platform, if you already have npm","","Or install Bun and Rove together in one step:"," curl -fsSL https://rove.run/install.sh | sh","",`Already have Bun somewhere unusual? Point Rove at it: ${z}=/path/to/bun`,""].join(`
2
+ import{join as l}from"node:path";import{createInterface as a}from"node:readline/promises";import{pathToFileURL as ss}from"node:url";import{spawnSync as Q}from"node:child_process";import{constants as e,accessSync as V}from"node:fs";import{homedir as R}from"node:os";import{basename as o,delimiter as I,dirname as U,join as y}from"node:path";import{fileURLToPath as T}from"node:url";var H={$schema:"https://json.schemastore.org/package.json",name:"@sma1lboy/rove",version:"0.9.256",description:"Rove — the agent multiplexer for your terminal. Run coding agents on parallel tasks with isolated worktrees and persistent sessions.",keywords:["terminal","tui","cli","multiplexer","ai-agents","coding-agent","ai-coding-assistant","agentic-ai","parallel-agents","git-worktree","claude-code","codex","llm","developer-tools"],type:"module",packageManager:"bun@1.3.13",bin:{rove:"dist/cli/rove.js"},files:["dist/cli","dist/completions","dist/web","dist/web-ui","dist/skills","dist/*.wav","README.md","LICENSE"],publishConfig:{access:"public"},repository:{type:"git",url:"git+https://github.com/Sma1lboy/rove.git"},homepage:"https://rove.run",bugs:{url:"https://github.com/Sma1lboy/rove/issues"},engines:{bun:">=1.3.11"},scripts:{dev:"ROVE_DEV=1 bun --conditions=browser ./src/cli/rove.ts","dev:mock":"ROVE_DEV=1 bun ./src/tui-react/mock/host.tsx","dev:sandbox":"bun run scripts/dev-sandbox.ts run","dev:sandbox:reset":"bun run scripts/dev-sandbox.ts reset",build:"bun run scripts/build.ts","build:rubric":"bun run scripts/build-tier-rubric.ts",compile:"bun run scripts/compile.ts",typecheck:"tsc --noEmit","check-i18n":"bun run scripts/check-i18n.ts",test:"bun run test:fast && bun run test:socket","perf:golden":"bun scripts/perf-golden.ts","pty:soak":"bun scripts/pty-soak.ts","test:fast":"vitest run --passWithNoTests --minWorkers=1 --maxWorkers=8","test:socket":"ROVE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:socket:coverage":"ROVE_INCLUDE_SOCKET=1 ROVE_COVERAGE_DAEMON=1 vitest run test/daemon --coverage --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:behavior":"ROVE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --retry=2 --passWithNoTests","test:render":"bun scripts/render-track.mjs",coverage:"vitest run --coverage --passWithNoTests",bench:"vitest bench --run",lint:"biome check .",knip:"knip-bun",postinstall:"bun run scripts/check-preview-deps.ts || true",prepublishOnly:"bun run typecheck && bun run build","plugin-sandbox":"bun run scripts/plugin-sandbox.ts"},"//":"biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",dependencies:{"@ansi-tools/parser":"^1.0.15","@opentui/core":"0.4.3","@opentui/react":"0.4.3","@xterm/addon-serialize":"^0.14.0","@xterm/addon-unicode11":"^0.9.0","@xterm/headless":"^6.0.0",react:"^19.2.8","smol-toml":"^1.7.1",ws:"^8.21.3"},optionalDependencies:{"node-pty":"^1.1.0"},devDependencies:{"@biomejs/biome":"1.9.4","@sma1lboy/rove-daemon":"0.7.22","@tsconfig/bun":"1.0.11","@types/bun":"1.3.14","@types/node":"25.6.2","@types/react":"^19.2.0","@vitest/coverage-v8":"2.1.9",knip:"^6.14.2","react-devtools-core":"^7.0.1","rove-bridge":"0.0.17","rove-weixin":"0.0.17",typescript:"5.8.2",vitest:"2.1.9"},trustedDependencies:[]};var q="rove";var k=q;var z="ROVE_BUN",_="ROVE_NO_BUN_BOOTSTRAP",F="ROVE_SKIP_BUN_CHECK",$=H.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0",L=(s)=>{try{return V(s,e.X_OK),!0}catch{return!1}},B=(s)=>s==="win32"?"bun.exe":"bun",f=(s)=>{let t=Q(s,["--version"],{encoding:"utf8",timeout:5000});if(t.error||t.status!==0)return null;return t.stdout?.split(`
3
+ `)[0]?.trim()??""};function J(s){return s.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function X(s,t=$){let c=J(s),W=J(t);if(!c||!W)return!0;let b=c.split(".").map(Number),g=W.split(".").map(Number);for(let p=0;p<3;p++){let r=b[p]??0,i=g[p]??0;if(r!==i)return r>i}return!0}function Y(s=process.env){return s[F]==="1"}function A(s={}){let t=s.env??process.env,c=s.platform??process.platform,W=s.home??t.HOME??t.USERPROFILE??R(),b=B(c),g=[],p=t[z]?.trim();if(p)g.push(p);for(let G of(t.PATH??t.Path??"").split(I))if(G)g.push(y(G,b));let r=t.BUN_INSTALL?.trim();if(r)g.push(y(r,"bin",b));if(W)g.push(y(W,".bun","bin",b));let i=s.launcherDir;if(i)g.push(y(i,"..","..","node_modules","bun","bin",b)),g.push(y(i,"..","..","..","bun","bin",b));return g}function N(s={}){let t=s.isExecutable??L;return A(s).find((c)=>t(c))??null}function Z(s={}){let t=s.isExecutable??L,c=s.bunVersionOf??f;if(Y(s.env??process.env))return{bun:N(s),stale:null,unusable:null};let W=null,b=null;for(let g of A(s)){if(!t(g))continue;let p=c(g);if(p===null){b??=g;continue}if(X(p))return{bun:g,stale:W,unusable:b};W??={path:g,version:J(p)??p}}return{bun:null,stale:W,unusable:b}}function h(s){return U(T(s))}function C(s){return o(T(s)).replace(/\.[cm]?[jt]s$/,"")}function n(s=process.platform){return s==="win32"?["powershell","-NoProfile","-Command","irm bun.sh/install.ps1 | iex"]:["bash","-c","curl -fsSL https://bun.sh/install | bash"]}function D(s=k,t=process.platform){let c=t==="win32"?'powershell -c "irm bun.sh/install.ps1 | iex"':"curl -fsSL https://bun.sh/install | bash";return[`${s}: Rove runs on the Bun runtime, and no Bun was found on this machine.`,"","Install Bun, then run this command again:",` ${c}`," npm install -g bun # any platform, if you already have npm","","Or install Bun and Rove together in one step:"," curl -fsSL https://rove.run/install.sh | sh","",`Already have Bun somewhere unusual? Point Rove at it: ${z}=/path/to/bun`,""].join(`
4
4
  `)}function j(s,t,c=k){return[`${c}: this machine's Bun is too old for Rove — found ${t} at ${s}, need ${$} or newer.`,"","Rove's terminals are built on Bun's PTY API. An older Bun ignores it silently,","so every terminal and engine tab would open empty and stay empty.","","Upgrade Bun, then run this command again:"," bun upgrade # Bun installed itself (~/.bun)"," brew upgrade bun # Homebrew"," npm install -g bun@latest # npm-managed Bun","",`Have a newer Bun elsewhere? Point Rove at it: ${z}=/path/to/bun`,`Run on this one anyway (unsupported, terminals will be blank): ${F}=1`,""].join(`
5
5
  `)}function v(s,t=k){return[`${t}: the Bun at ${s} could not be run — \`${s} --version\` failed or never returned.`,"","Rove skipped it rather than starting through it, which would hang or fail","with a message from that binary instead of this one.","","Reinstall Bun, then run this command again:"," curl -fsSL https://bun.sh/install | bash","",`Have a working Bun elsewhere? Point Rove at it: ${z}=/path/to/bun`,""].join(`
6
- `)}function w(s=process.env,t=process.stdin,c=process.stdout){if(s[_]==="1")return!1;if(s.CI==="true"||s.CI==="1")return!1;return Boolean(t.isTTY&&c.isTTY)}function M(s={},t=Q){let c=s.platform??process.platform,[W,...b]=u(c);if(!W)return null;let g=t(W,b,{stdio:"inherit"});if(g.error||g.status!==0)return null;return Z(s).bun}function l(s){if(typeof s.status==="number")return s.status;if(s.signal)return 128+(Object.hasOwn(K,s.signal)?K[s.signal]:0);return 1}var K={SIGINT:2,SIGQUIT:3,SIGKILL:9,SIGTERM:15};function S(s,t,c,W=Q){let b=W(s,[t,...c],{stdio:"inherit"});if(b.error)return process.stderr.write(`rove: could not start Bun at ${s}: ${b.error.message}. Check that this path is executable, or reinstall Bun from https://bun.sh.
7
- `),1;return l(b)}var E=h(import.meta.url),p=C(import.meta.url),m=n(E,`${p}-run.js`);async function ts(s){let t=a({input:process.stdin,output:process.stdout});try{let c=(await t.question(s)).trim().toLowerCase();return c===""||c==="y"||c==="yes"}finally{t.close()}}async function cs(){let s={launcherDir:E},t=Z(s);if(t.bun||!w())return t;let c=t.stale?`${p}: Bun ${t.stale.version} is too old for Rove (needs ${$}+). Install the latest Bun now? [Y/n] `:`${p}: Rove runs on the Bun runtime, and none is installed. Install Bun now? [Y/n] `;if(!await ts(c))return t;return{bun:M(s),stale:t.stale,unusable:t.unusable}}var O=globalThis.Bun;if(O){let s=O.version;if(s&&!Y()&&!X(s))process.stderr.write(j(process.execPath,s,p)),process.exit(1);await import(ss(m).href)}else{let{bun:s,stale:t,unusable:c}=await cs();if(!s){let W=t?j(t.path,t.version,p):c?v(c,p):D(p);process.stderr.write(W),process.exit(1)}process.exit(S(s,m,process.argv.slice(2)))}
6
+ `)}function w(s=process.env,t=process.stdin,c=process.stdout){if(s[_]==="1")return!1;if(s.CI==="true"||s.CI==="1")return!1;return Boolean(t.isTTY&&c.isTTY)}function M(s={},t=Q){let c=s.platform??process.platform,[W,...b]=n(c);if(!W)return null;let g=t(W,b,{stdio:"inherit"});if(g.error||g.status!==0)return null;return Z(s).bun}function u(s){if(typeof s.status==="number")return s.status;if(s.signal)return 128+(Object.hasOwn(K,s.signal)?K[s.signal]:0);return 1}var K={SIGINT:2,SIGQUIT:3,SIGKILL:9,SIGTERM:15};function S(s,t,c,W=Q){let b=W(s,[t,...c],{stdio:"inherit"});if(b.error)return process.stderr.write(`rove: could not start Bun at ${s}: ${b.error.message}. Check that this path is executable, or reinstall Bun from https://bun.sh.
7
+ `),1;return u(b)}var d=h(import.meta.url),x=C(import.meta.url),m=l(d,`${x}-run.js`);async function ts(s){let t=a({input:process.stdin,output:process.stdout});try{let c=(await t.question(s)).trim().toLowerCase();return c===""||c==="y"||c==="yes"}finally{t.close()}}async function cs(){let s={launcherDir:d},t=Z(s);if(t.bun||!w())return t;let c=t.stale?`${x}: Bun ${t.stale.version} is too old for Rove (needs ${$}+). Install the latest Bun now? [Y/n] `:`${x}: Rove runs on the Bun runtime, and none is installed. Install Bun now? [Y/n] `;if(!await ts(c))return t;return{bun:M(s),stale:t.stale,unusable:t.unusable}}var O=globalThis.Bun;if(O){let s=O.version;if(s&&!Y()&&!X(s))process.stderr.write(j(process.execPath,s,x)),process.exit(1);await import(ss(m).href)}else{let{bun:s,stale:t,unusable:c}=await cs();if(!s){let W=t?j(t.path,t.version,x):c?v(c,x):D(x);process.stderr.write(W),process.exit(1)}process.exit(S(s,m,process.argv.slice(2)))}
@@ -12,7 +12,7 @@ _rove() {
12
12
  fi
13
13
  if [[ ${COMP_CWORD} -eq 2 ]]; then
14
14
  case "${prev}" in
15
- api) COMPREPLY=( $(compgen -W "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch deliveries add send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split notify prompt engine-report row-token set-active report gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land delete ensure-worktree remove-worktree discover-adoptable adopt" -- "${cur}") ) ;;
15
+ api) COMPREPLY=( $(compgen -W "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch deliveries should-run add send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split notify prompt engine-report row-token set-active report attention-dismiss gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land delete ensure-worktree remove-worktree discover-adoptable adopt" -- "${cur}") ) ;;
16
16
  daemon) COMPREPLY=( $(compgen -W "status start stop restart" -- "${cur}") ) ;;
17
17
  machine) COMPREPLY=( $(compgen -W "add doctor remove list" -- "${cur}") ) ;;
18
18
  plugin) COMPREPLY=( $(compgen -W "install link list search outdated update enable disable unlink uninstall config-dir log action pane" -- "${cur}") ) ;;
@@ -19,7 +19,7 @@ complete -c rove -f -n __fish_use_subcommand -a skill
19
19
  complete -c rove -f -n __fish_use_subcommand -a plugin
20
20
  complete -c rove -f -n __fish_use_subcommand -a feedback
21
21
  complete -c rove -f -n __fish_use_subcommand -a update
22
- complete -c rove -f -n "__fish_seen_subcommand_from api" -a "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch deliveries add send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split notify prompt engine-report row-token set-active report gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land delete ensure-worktree remove-worktree discover-adoptable adopt"
22
+ complete -c rove -f -n "__fish_seen_subcommand_from api" -a "schema engine-list list get-task pty-list collect context digest agent-turns inspect read-output watch deliveries should-run add send dispatch interrupt note note-list note-delete pane-open pane-close pane-graphics tab-close tab-new tab-split notify prompt engine-report row-token set-active report attention-dismiss gate-raise gate-answer gate-withdraw gates feedback issue-list issue-create issue-update issue-delete routine-list routine-create routine-update routine-delete routine-run-now routine-respond routine-runs workitem-list workitem-start update land delete ensure-worktree remove-worktree discover-adoptable adopt"
23
23
  complete -c rove -f -n "__fish_seen_subcommand_from daemon" -a "status start stop restart"
24
24
  complete -c rove -f -n "__fish_seen_subcommand_from machine" -a "add doctor remove list"
25
25
  complete -c rove -f -n "__fish_seen_subcommand_from plugin" -a "install link list search outdated update enable disable unlink uninstall config-dir log action pane"
@@ -13,7 +13,7 @@ _rove() {
13
13
 
14
14
  verbs=()
15
15
  case "${words[2]}" in
16
- api) verbs=("schema" "engine-list" "list" "get-task" "pty-list" "collect" "context" "digest" "agent-turns" "inspect" "read-output" "watch" "deliveries" "add" "send" "dispatch" "interrupt" "note" "note-list" "note-delete" "pane-open" "pane-close" "pane-graphics" "tab-close" "tab-new" "tab-split" "notify" "prompt" "engine-report" "row-token" "set-active" "report" "gate-raise" "gate-answer" "gate-withdraw" "gates" "feedback" "issue-list" "issue-create" "issue-update" "issue-delete" "routine-list" "routine-create" "routine-update" "routine-delete" "routine-run-now" "routine-respond" "routine-runs" "workitem-list" "workitem-start" "update" "land" "delete" "ensure-worktree" "remove-worktree" "discover-adoptable" "adopt") ;;
16
+ api) verbs=("schema" "engine-list" "list" "get-task" "pty-list" "collect" "context" "digest" "agent-turns" "inspect" "read-output" "watch" "deliveries" "should-run" "add" "send" "dispatch" "interrupt" "note" "note-list" "note-delete" "pane-open" "pane-close" "pane-graphics" "tab-close" "tab-new" "tab-split" "notify" "prompt" "engine-report" "row-token" "set-active" "report" "attention-dismiss" "gate-raise" "gate-answer" "gate-withdraw" "gates" "feedback" "issue-list" "issue-create" "issue-update" "issue-delete" "routine-list" "routine-create" "routine-update" "routine-delete" "routine-run-now" "routine-respond" "routine-runs" "workitem-list" "workitem-start" "update" "land" "delete" "ensure-worktree" "remove-worktree" "discover-adoptable" "adopt") ;;
17
17
  daemon) verbs=("status" "start" "stop" "restart") ;;
18
18
  machine) verbs=("add" "doctor" "remove" "list") ;;
19
19
  plugin) verbs=("install" "link" "list" "search" "outdated" "update" "enable" "disable" "unlink" "uninstall" "config-dir" "log" "action" "pane") ;;
@@ -3,7 +3,7 @@ name: rove
3
3
  description: Use when controlling Rove tasks, parallel coding attempts, hosted agent sessions, task lifecycle, or the daemon-owned issue tracker from a shell. Also the ONLY channel for messaging another agent session on this machine — `rove api send`, never a peer/MCP side channel.
4
4
  ---
5
5
 
6
- <!-- rove-skill-version: 63 — bump in lockstep with ROVE_SKILL_VERSION (src/lib/skill-install.ts). -->
6
+ <!-- rove-skill-version: 64 — bump in lockstep with ROVE_SKILL_VERSION (src/lib/skill-install.ts). -->
7
7
 
8
8
  # Rove shell control
9
9
 
@@ -273,7 +273,7 @@ turn either. These five carry almost all traffic:
273
273
 
274
274
  ```text
275
275
  add --repo(REQ) --prompt --title --command --count --agents --activate
276
- send --prompt|--prompt-file(REQ) --task-id --tab --command --plain --expect-report
276
+ send --prompt|--prompt-file(REQ) --task-id --tab --command --plain --expect-report --if-runnable
277
277
  get-task --task-id(REQ) list (no flags)
278
278
  collect --group <groupId> | --task-ids <csv> | --repo
279
279
  ```
@@ -294,7 +294,7 @@ Find your section by what you were ASKED, not by Rove's noun for it:
294
294
  | track work in Rove's own backlog, move a kanban card | issues |
295
295
  | remember this for the repo, leave a field note / 记一笔 | drive |
296
296
  | what did the agent do, per-turn cost, read its screen | read |
297
- | a prompt ends `[ROVE] … report with: rove api report`; tick a task unattended and want proof it reported | drive (`report`, `send --expect-report`) / read (`deliveries`) |
297
+ | a prompt ends `[ROVE] … report with: rove api report`; tick a task unattended and want proof it reported, or skip a tick the task can't use | drive (`report`, `send --expect-report`, `send --if-runnable`) / read (`deliveries`, `should-run`) |
298
298
  | make / rename / retarget / land / close a task | create / edit / lifecycle |
299
299
  | materialize or adopt a checkout | worktree |
300
300
  | which engines exist, send Rove product feedback | discover / feedback |
@@ -657,6 +657,17 @@ or why it went missing) instead of a growing notes file. Silence still never
657
657
  proves a worker is dead — `missing` says the turn ended or the engine died
658
658
  without a report; look before you act on it.
659
659
 
660
+ **Don't tick a task that can't use it.** `rove api should-run --task-id <id>`
661
+ prints `{run:true}` (exit 0) or `{run:false, reason, detail?, until?,
662
+ gateIds?}` (exit 3, verdict still on stdout). `reason`, first match wins:
663
+ `awaiting-owner` (an open gate), `report-missing` (the last tick ended
664
+ unreported), `quota` (clears itself at `until`), `engine-error`,
665
+ `engine-dead`, `awaiting-permission`, `busy`. Check and send in one step with
666
+ `rove api send --if-runnable --expect-report --task-id <id> --prompt "…"`:
667
+ a refused tick pastes nothing, replies `{skipped: {…}}` with exit 3 and is
668
+ recorded as a `skipped` delivery. Drop the tick; never queue it for later.
669
+ `get-task` and `collect` carry the same verdict as `runnability`.
670
+
660
671
  ### A task is finished when it is GONE
661
672
 
662
673
  The dispatcher closes tasks. A worker cannot delete itself, so when it reports
@@ -82,6 +82,7 @@ inspect --task-id
82
82
  read-output --task-id --tab --source{auto|history|terminal}(auto) --cursor --limit
83
83
  watch --task-ids <a,b,c> --group --until(REQ) <STATE[,STATE]> --timeout
84
84
  deliveries --task-id(REQ) --limit
85
+ should-run --task-id(REQ) --tab
85
86
  gates --repo --task-id --status{open|answered|withdrawn|all}(open)
86
87
  ```
87
88
  <!-- generated:end -->
@@ -92,10 +93,14 @@ gates --repo --task-id --status{open|answered|withdrawn|all}(open)
92
93
  the wider diagnostic — daemon activity, a pty walk, and every tab snapshot.
93
94
 
94
95
  `deliveries --task-id` is the task's report log, newest first: for each armed
95
- prompt its `status` (`awaiting`, `reported`, `missing`), the report
96
+ prompt its `status` (`awaiting`, `reported`, `missing`, `skipped`), the report
96
97
  (`outcome`, `summary`, `evidence`, `next`) or why it went missing (`cause`,
97
- `note`). A loop that wakes a task reads `deliveries --limit 5` for what the
98
- last ticks did, instead of a growing notes file.
98
+ `note`; a `missing` row also carries `dismissedAt` once the owner dismissed it).
99
+ A `skipped` row is a tick `send --if-runnable` refused: `skipped` is
100
+ the verdict (`reason`, `detail`, `until`, `gateIds`) and `repeats` counts the
101
+ identical refusals (same tab, same reason) that one row stands for. A loop that
102
+ wakes a task reads `deliveries --limit 5` for what the last ticks did, instead
103
+ of a growing notes file.
99
104
 
100
105
  `pty-list` is what `NO_ENGINE_TAB`'s hint points at: when `.tabs[]` and reality
101
106
  disagree, it is the ground truth for what is actually alive — key, alive, pid,
@@ -119,35 +124,36 @@ needs numbers, not when you want to know what a task is doing.
119
124
 
120
125
  <!-- generated:begin drive -->
121
126
  ```text
122
- send --task-id --prompt|--prompt-file(REQ) --tab --command --respawn --plain
123
- --allow-empty --expect-report
124
- dispatch --task-id(REQ) --prompt|--prompt-file(REQ) --tab
125
- interrupt --task-id(REQ) --tab
126
- note --task-id(REQ) --text(REQ)
127
- note-list --repo(REQ)
128
- note-delete --repo(REQ) --id(REQ)
129
- pane-open --task-id --tab --command --direction{right|down}(right)
130
- --placement{split|tab}(split) --title
131
- pane-close --task-id --title(REQ) --tab
132
- pane-graphics --task-id --tab(REQ) --image-id
133
- tab-close --task-id(REQ) --tab(REQ)
134
- tab-new --task-id(REQ) --kind{shell|engine|editor|pane|viewport}(REQ) --command
135
- --file --line --pane --tab --view-task
136
- tab-split --task-id(REQ) --tab(REQ) --action{open|close|rename|reset}(REQ)
137
- --direction{right|down}(right) --leaf --title --cols --rows
138
- notify --title(REQ) --body --kind(done) --task-id --source
139
- prompt --title(REQ) --placeholder --initial --timeout
140
- engine-report --task-id
141
- --kind{session-start|turn-start|turn-complete|turn-failed|turn-interrupted|awaiting-input|session-end|tool-pre|tool-post|tool-failed|pre-compact|post-compact|subagent-start|subagent-stop}(REQ)
142
- --engine --tab --detail
143
- row-token --task-id(REQ) --text --ttl(60) --key(default)
144
- --tone{info|success|warning|error|muted} --clear
145
- set-active --task-id --none
146
- report --delivery --outcome{done|partial|blocked|failed} --summary --evidence
147
- --next --json
148
- gate-raise --task-id(REQ) --tab --question(REQ) --option --recommend --context
149
- gate-answer --gate(REQ) --option --text
150
- gate-withdraw --gate(REQ)
127
+ send --task-id --prompt|--prompt-file(REQ) --tab --command --respawn --plain
128
+ --allow-empty --expect-report --if-runnable
129
+ dispatch --task-id(REQ) --prompt|--prompt-file(REQ) --tab
130
+ interrupt --task-id(REQ) --tab
131
+ note --task-id(REQ) --text(REQ)
132
+ note-list --repo(REQ)
133
+ note-delete --repo(REQ) --id(REQ)
134
+ pane-open --task-id --tab --command --direction{right|down}(right)
135
+ --placement{split|tab}(split) --title
136
+ pane-close --task-id --title(REQ) --tab
137
+ pane-graphics --task-id --tab(REQ) --image-id
138
+ tab-close --task-id(REQ) --tab(REQ)
139
+ tab-new --task-id(REQ) --kind{shell|engine|editor|pane|viewport}(REQ) --command
140
+ --file --line --pane --tab --view-task
141
+ tab-split --task-id(REQ) --tab(REQ) --action{open|close|rename|reset}(REQ)
142
+ --direction{right|down}(right) --leaf --title --cols --rows
143
+ notify --title(REQ) --body --kind(done) --task-id --source
144
+ prompt --title(REQ) --placeholder --initial --timeout
145
+ engine-report --task-id
146
+ --kind{session-start|turn-start|turn-complete|turn-failed|turn-interrupted|awaiting-input|session-end|tool-pre|tool-post|tool-failed|pre-compact|post-compact|subagent-start|subagent-stop}(REQ)
147
+ --engine --tab --detail
148
+ row-token --task-id(REQ) --text --ttl(60) --key(default)
149
+ --tone{info|success|warning|error|muted} --clear
150
+ set-active --task-id --none
151
+ report --delivery --outcome{done|partial|blocked|failed} --summary --evidence
152
+ --next --json
153
+ attention-dismiss --delivery(REQ)
154
+ gate-raise --task-id(REQ) --tab --question(REQ) --option --recommend --context
155
+ gate-answer --gate(REQ) --option --text
156
+ gate-withdraw --gate(REQ)
151
157
  ```
152
158
  <!-- generated:end -->
153
159
 
@@ -179,6 +185,15 @@ last step, before you stop. A turn that ends without it raises a
179
185
  report; a late one clears the flag. `outcome` is `blocked` when you need the
180
186
  owner, `failed` when you tried and could not. Summary max 32,000 characters.
181
187
 
188
+ **`send --if-runnable` skips a tick the task can't use.** The daemon checks
189
+ `should-run`, claims the tab and arms the delivery in one step, so two ticks
190
+ arriving together cannot both pass. A refusal pastes nothing and replies
191
+ `{skipped: {run:false, reason, detail?, until?, gateIds?}}` with exit 3
192
+ (a `skipped` delivery is recorded when the send would have owed a report).
193
+ `reason` is `awaiting-owner`, `report-missing`, `quota`, `engine-error`,
194
+ `engine-dead`, `awaiting-permission` or `busy`; `should-run` asks the same
195
+ question without sending. A skip is dropped, never queued.
196
+
182
197
  `notify --kind` styles the toast: `done`, `needs_input`, `error` get severity
183
198
  treatment and an unread mark, anything else renders neutrally.
184
199