@sma1lboy/rove 0.9.238 → 0.9.240
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.
- package/README.md +2 -2
- package/dist/cli/chunk-0mj358p5.js +3 -0
- package/dist/cli/{chunk-nntg9d85.js → chunk-0y4qgnby.js} +1 -1
- package/dist/cli/chunk-1brfxbvg.js +4 -0
- package/dist/cli/chunk-21fm5tks.js +2 -0
- package/dist/cli/{chunk-bkbmqec2.js → chunk-28zg6p31.js} +1 -1
- package/dist/cli/chunk-2andp9gs.js +2 -0
- package/dist/cli/{chunk-r2pztgm6.js → chunk-2bgmby1d.js} +1 -1
- package/dist/cli/chunk-2vgc5cyj.js +2 -0
- package/dist/cli/chunk-2x9yc5nx.js +2 -0
- package/dist/cli/chunk-30kjq5m7.js +2 -0
- package/dist/cli/{chunk-b503z32q.js → chunk-31y92jm8.js} +3 -3
- package/dist/cli/{chunk-mkksdcsw.js → chunk-3342qzve.js} +1 -1
- package/dist/cli/{chunk-0wgheepg.js → chunk-34261s0m.js} +3 -3
- package/dist/cli/chunk-3aqhytyy.js +3 -0
- package/dist/cli/chunk-3c58659p.js +2 -0
- package/dist/cli/chunk-3d1hjrq0.js +2 -0
- package/dist/cli/{chunk-pzbrj3gd.js → chunk-3vyk7bzb.js} +1 -1
- package/dist/cli/chunk-41x1hrqe.js +3 -0
- package/dist/cli/chunk-4dv0mgan.js +2 -0
- package/dist/cli/{chunk-mhg40es1.js → chunk-4hestsjp.js} +1 -1
- package/dist/cli/{chunk-77w83wgh.js → chunk-4tx2kayk.js} +1 -1
- package/dist/cli/{chunk-aj7fkrha.js → chunk-4xj52nx1.js} +1 -1
- package/dist/cli/{chunk-s1p650mm.js → chunk-53cyns0f.js} +1 -1
- package/dist/cli/chunk-576r5nht.js +5 -0
- package/dist/cli/{chunk-1bm6sryf.js → chunk-5nsvp4h7.js} +1 -1
- package/dist/cli/{chunk-zyd6k6ak.js → chunk-7q43tysm.js} +1 -1
- package/dist/cli/chunk-801h8bze.js +4 -0
- package/dist/cli/chunk-80ydqr3s.js +3 -0
- package/dist/cli/{chunk-cv1kaq8a.js → chunk-85zr7kph.js} +5 -5
- package/dist/cli/{chunk-zwgns5ce.js → chunk-8tadwbx9.js} +1 -1
- package/dist/cli/chunk-96cjpssp.js +3 -0
- package/dist/cli/{chunk-bs7v83x0.js → chunk-9aydqazr.js} +1 -1
- package/dist/cli/{chunk-7txq91d9.js → chunk-9jsm6h5y.js} +1 -1
- package/dist/cli/{chunk-pz907kve.js → chunk-9w5g7frx.js} +1 -1
- package/dist/cli/chunk-a0ave3g6.js +3 -0
- package/dist/cli/{chunk-vnpqemb0.js → chunk-a7kq09ke.js} +1 -1
- package/dist/cli/chunk-abyk3w2j.js +2 -0
- package/dist/cli/chunk-adkva3ep.js +6 -0
- package/dist/cli/{chunk-nvqdmx7q.js → chunk-ag3txfg2.js} +1 -1
- package/dist/cli/{chunk-2rg6aw59.js → chunk-atmaakwp.js} +1 -1
- package/dist/cli/chunk-axsm0gn5.js +3 -0
- package/dist/cli/chunk-b0ssvpht.js +3 -0
- package/dist/cli/chunk-b39sdtt0.js +3 -0
- package/dist/cli/{chunk-grt1e2s8.js → chunk-bb5zqmea.js} +2 -2
- package/dist/cli/{chunk-g56km26e.js → chunk-bn73kd6z.js} +1 -1
- package/dist/cli/chunk-c2m55b72.js +4 -0
- package/dist/cli/{chunk-ch0ehc06.js → chunk-cbc1h345.js} +1 -1
- package/dist/cli/{chunk-dnbh8hsm.js → chunk-cbjh43yc.js} +1 -1
- package/dist/cli/chunk-cst86kt3.js +2 -0
- package/dist/cli/chunk-des00203.js +2 -0
- package/dist/cli/chunk-dgs0m8r4.js +3 -0
- package/dist/cli/chunk-dkgxz9hf.js +5 -0
- package/dist/cli/{chunk-mb1e73jm.js → chunk-ekd274v3.js} +2 -2
- package/dist/cli/{chunk-gqphk56d.js → chunk-ekg3h5z3.js} +7 -7
- package/dist/cli/chunk-f1nf99hj.js +2 -0
- package/dist/cli/chunk-fert44x2.js +2 -0
- package/dist/cli/chunk-ff1h2qxd.js +2 -0
- package/dist/cli/{chunk-cv6r89jp.js → chunk-ffs26p85.js} +1 -1
- package/dist/cli/{chunk-hp32tbes.js → chunk-fg62y5fm.js} +1 -1
- package/dist/cli/{chunk-s9a974w9.js → chunk-fvmee0tr.js} +1 -1
- package/dist/cli/{chunk-8h0khkap.js → chunk-fwqd5ta2.js} +2 -2
- package/dist/cli/{chunk-acw32w13.js → chunk-g0eygcnq.js} +6 -5
- package/dist/cli/chunk-g4c4ffqp.js +20 -0
- package/dist/cli/{chunk-k7g060qr.js → chunk-g4j41mky.js} +2 -2
- package/dist/cli/{chunk-j6kataan.js → chunk-g5c5jytr.js} +1 -1
- package/dist/cli/{chunk-dqndd97s.js → chunk-gkdfct7t.js} +2 -2
- package/dist/cli/chunk-gvrtrvnz.js +96 -0
- package/dist/cli/chunk-h2m7em1e.js +3 -0
- package/dist/cli/chunk-h33m6cc6.js +8 -0
- package/dist/cli/{chunk-ynbfqbn9.js → chunk-h3fckxn3.js} +1 -1
- package/dist/cli/chunk-h7y9v8k1.js +4 -0
- package/dist/cli/{chunk-32mv1yy6.js → chunk-h9cg0j3a.js} +1 -1
- package/dist/cli/{chunk-h4qv19v8.js → chunk-hbn1t161.js} +1 -1
- package/dist/cli/{chunk-3es6spp4.js → chunk-hq9tbzqa.js} +2 -2
- package/dist/cli/{chunk-snjte1rd.js → chunk-hr2xky15.js} +1 -1
- package/dist/cli/chunk-jxqs4y9j.js +10 -0
- package/dist/cli/{chunk-s0snbj93.js → chunk-kb6xw0fw.js} +1 -1
- package/dist/cli/{chunk-vyb5a2rh.js → chunk-kg9kmwt9.js} +1 -1
- package/dist/cli/chunk-ktzbg4jg.js +2 -0
- package/dist/cli/chunk-kz4txj2e.js +8 -0
- package/dist/cli/{chunk-bhqracn8.js → chunk-m19q29wk.js} +1 -1
- package/dist/cli/chunk-m1ekbqyz.js +2 -0
- package/dist/cli/chunk-m4f5h05c.js +10 -0
- package/dist/cli/chunk-m5gx53yr.js +3 -0
- package/dist/cli/chunk-m6q44xc0.js +2 -0
- package/dist/cli/{chunk-hq2n27y1.js → chunk-mw0ysrpd.js} +1 -1
- package/dist/cli/{chunk-2cwb64fj.js → chunk-n5dea05t.js} +1 -1
- package/dist/cli/chunk-nc46gddg.js +3 -0
- package/dist/cli/{chunk-jhbc9nx2.js → chunk-nckmga7j.js} +1 -1
- package/dist/cli/chunk-nem2x959.js +2 -0
- package/dist/cli/chunk-ng12dprz.js +2 -0
- package/dist/cli/chunk-npwzmmv9.js +3 -0
- package/dist/cli/chunk-nzh30nty.js +4 -0
- package/dist/cli/{chunk-gqrh2gp1.js → chunk-p024scd4.js} +1 -1
- package/dist/cli/chunk-p18ehv9y.js +7 -0
- package/dist/cli/{chunk-3sg5v7cg.js → chunk-p83wf4zw.js} +2 -2
- package/dist/cli/{chunk-s278gjet.js → chunk-pdvyfz3n.js} +1 -1
- package/dist/cli/chunk-pe6wnsww.js +2 -0
- package/dist/cli/chunk-prqeddy7.js +3 -0
- package/dist/cli/chunk-pvm3fc44.js +2 -0
- package/dist/cli/{chunk-2yd128k1.js → chunk-q5eee34y.js} +6 -6
- package/dist/cli/{chunk-z5fnv7nk.js → chunk-q7zzycy7.js} +25 -23
- package/dist/cli/chunk-qvfdqrbf.js +3 -0
- package/dist/cli/{chunk-2xsem13q.js → chunk-qzsahxd9.js} +1 -1
- package/dist/cli/chunk-rqyaf0dw.js +4 -0
- package/dist/cli/chunk-s57vy3k3.js +2 -0
- package/dist/cli/{chunk-g2advs8t.js → chunk-sak1tphp.js} +1 -1
- package/dist/cli/{chunk-vdcn6h48.js → chunk-snhcvjdb.js} +1 -1
- package/dist/cli/{chunk-bav389wj.js → chunk-svm809bx.js} +1 -1
- package/dist/cli/chunk-szrfp0jq.js +2 -0
- package/dist/cli/chunk-t8v9rpdx.js +2 -0
- package/dist/cli/{chunk-j4xfsn9g.js → chunk-tbrkck2f.js} +1 -1
- package/dist/cli/chunk-tdhk4dz8.js +4 -0
- package/dist/cli/chunk-thsmzarw.js +3 -0
- package/dist/cli/{chunk-t3wwsz29.js → chunk-tvk46rfs.js} +1 -1
- package/dist/cli/{chunk-4d54zgc7.js → chunk-twyda1p8.js} +2 -2
- package/dist/cli/{chunk-s1e0e9hc.js → chunk-vej49hnt.js} +2 -2
- package/dist/cli/chunk-w2sp489k.js +3 -0
- package/dist/cli/{chunk-0wh2ff77.js → chunk-whbm14qr.js} +2 -2
- package/dist/cli/chunk-wp9bygh4.js +2 -0
- package/dist/cli/{chunk-ww4fbx8h.js → chunk-wtb8cycx.js} +2 -2
- package/dist/cli/chunk-x9s14ppv.js +4 -0
- package/dist/cli/{chunk-hk8mc43r.js → chunk-xckpezd9.js} +5 -5
- package/dist/cli/{chunk-dq2mh3b3.js → chunk-xv8fhxxm.js} +1 -1
- package/dist/cli/{chunk-85pk7c74.js → chunk-xx166cdv.js} +1 -1
- package/dist/cli/{chunk-ssfj9td0.js → chunk-y8tysjc2.js} +15 -13
- package/dist/cli/{chunk-y3k1accw.js → chunk-yexx8nec.js} +1 -1
- package/dist/cli/{chunk-9prawccj.js → chunk-yt15y085.js} +1 -1
- package/dist/cli/{chunk-n0mg67wn.js → chunk-ytxk6zqc.js} +1 -1
- package/dist/cli/chunk-yvjgwnb9.js +2 -0
- package/dist/cli/{chunk-n24dmy2r.js → chunk-yxt1y0n3.js} +9 -9
- package/dist/cli/chunk-ze0htjr5.js +2 -0
- package/dist/cli/chunk-zeb0sttx.js +2 -0
- package/dist/cli/chunk-znjcesdr.js +5 -0
- package/dist/cli/chunk-zsfyj36g.js +3 -0
- package/dist/cli/chunk-zt49bbg9.js +3 -0
- package/dist/cli/chunk-zt7wfnva.js +3 -0
- package/dist/cli/{chunk-6rvzzrd7.js → chunk-zx2n95qe.js} +1 -1
- package/dist/cli/index.js +12 -10
- package/dist/cli/pty-host-node.mjs +880 -169
- package/dist/cli/rove-run.js +1 -1
- package/dist/cli/rove.js +6 -6
- package/dist/skills/rove/SKILL.md +1 -1
- package/dist/skills/rove/references/api-flags.md +5 -5
- package/dist/web-ui/assets/{harness-CQ-1WI4q.js → harness-BHiciz5A.js} +2 -2
- package/dist/web-ui/assets/{index-_ttAn6pY.js → index-BkxJISAI.js} +2 -2
- package/dist/web-ui/assets/{index-C4dLQEqf.css → index-nMJ42IGm.css} +1 -1
- package/dist/web-ui/index.html +2 -2
- package/dist/web-ui/origin-policy.mjs +1 -1
- package/dist/web-ui/pty-server.mjs +24 -10
- package/dist/web-ui/pty-session-lifecycle.mjs +9 -13
- package/package.json +6 -8
- package/dist/cli/chunk-05jej26n.js +0 -3
- package/dist/cli/chunk-079cyb2e.js +0 -4
- package/dist/cli/chunk-1265fmzk.js +0 -3
- package/dist/cli/chunk-1da3bqjc.js +0 -4
- package/dist/cli/chunk-1tx1s5v6.js +0 -2
- package/dist/cli/chunk-507zn8rs.js +0 -3
- package/dist/cli/chunk-53afdybj.js +0 -3
- package/dist/cli/chunk-544bwsss.js +0 -4
- package/dist/cli/chunk-67ev8k22.js +0 -2
- package/dist/cli/chunk-6s5mev2n.js +0 -4
- package/dist/cli/chunk-6x5vzzpx.js +0 -3
- package/dist/cli/chunk-7s1kx9ez.js +0 -2
- package/dist/cli/chunk-85mk6j0s.js +0 -3
- package/dist/cli/chunk-8hbtca1g.js +0 -3
- package/dist/cli/chunk-8pa1ph1s.js +0 -7
- package/dist/cli/chunk-9ek7xm94.js +0 -8
- package/dist/cli/chunk-9k47xr7m.js +0 -2
- package/dist/cli/chunk-9mtxxsb3.js +0 -2
- package/dist/cli/chunk-a1k2z44b.js +0 -2
- package/dist/cli/chunk-a3yyrabb.js +0 -2
- package/dist/cli/chunk-ahnk6zx3.js +0 -2
- package/dist/cli/chunk-bc18gat5.js +0 -4
- package/dist/cli/chunk-be757xzj.js +0 -3
- package/dist/cli/chunk-bw73t1zw.js +0 -5
- package/dist/cli/chunk-c2tevj9f.js +0 -2
- package/dist/cli/chunk-d1agt94n.js +0 -8
- package/dist/cli/chunk-dc31fzrm.js +0 -2
- package/dist/cli/chunk-dw853n4w.js +0 -3
- package/dist/cli/chunk-eawt4n4g.js +0 -2
- package/dist/cli/chunk-egx76ah2.js +0 -5
- package/dist/cli/chunk-ex1a2djb.js +0 -2
- package/dist/cli/chunk-f5e3dcxe.js +0 -2
- package/dist/cli/chunk-fwb72vzc.js +0 -2
- package/dist/cli/chunk-fxs82dsx.js +0 -4
- package/dist/cli/chunk-g3r1dwy9.js +0 -3
- package/dist/cli/chunk-ge30p4s5.js +0 -3
- package/dist/cli/chunk-gmgsxdrf.js +0 -10
- package/dist/cli/chunk-he76emdm.js +0 -2
- package/dist/cli/chunk-hgx20x94.js +0 -2
- package/dist/cli/chunk-hnfp317x.js +0 -2
- package/dist/cli/chunk-hq4b3fca.js +0 -3
- package/dist/cli/chunk-janvvpv5.js +0 -8
- package/dist/cli/chunk-jvxh93nz.js +0 -3
- package/dist/cli/chunk-k6nxyxyh.js +0 -3
- package/dist/cli/chunk-k8tspshr.js +0 -2
- package/dist/cli/chunk-md8k7c5w.js +0 -2
- package/dist/cli/chunk-mppda1wq.js +0 -3
- package/dist/cli/chunk-ppwpjm39.js +0 -3
- package/dist/cli/chunk-pvw2y93q.js +0 -25
- package/dist/cli/chunk-pw6yfj10.js +0 -3
- package/dist/cli/chunk-q4nehkj9.js +0 -2
- package/dist/cli/chunk-qagek3wg.js +0 -5
- package/dist/cli/chunk-r8xqvg26.js +0 -2
- package/dist/cli/chunk-rz8zs7j8.js +0 -3
- package/dist/cli/chunk-sb0ns0c7.js +0 -3
- package/dist/cli/chunk-tcj36cqg.js +0 -2
- package/dist/cli/chunk-vejgnmcp.js +0 -2
- package/dist/cli/chunk-vzqrj32e.js +0 -2
- package/dist/cli/chunk-wpsbyq4s.js +0 -2
- package/dist/cli/chunk-wtqxv2rv.js +0 -39
- package/dist/cli/chunk-y05d79az.js +0 -2
- package/dist/cli/chunk-y4048296.js +0 -4
- package/dist/cli/chunk-y8fqdnh3.js +0 -3
- package/dist/cli/chunk-ygd572ee.js +0 -4
- package/dist/cli/chunk-yj5pjxx2.js +0 -2
- package/dist/cli/chunk-zja9chrd.js +0 -4
- package/dist/cli/chunk-zwej00t0.js +0 -5
- package/dist/cli/kobe-run.js +0 -3
- package/dist/cli/kobe.js +0 -7
- package/dist/completions/kobe.bash +0 -25
- package/dist/completions/kobe.fish +0 -27
- package/dist/completions/kobe.zsh +0 -35
- /package/dist/cli/{chunk-hqbmzz4t.js → chunk-0422g8ac.js} +0 -0
- /package/dist/cli/{chunk-khj0apc7.js → chunk-2tfnscxk.js} +0 -0
- /package/dist/cli/{chunk-7q67dz4c.js → chunk-4250pw6r.js} +0 -0
- /package/dist/cli/{chunk-xsrcwcjc.js → chunk-5mhvjytn.js} +0 -0
- /package/dist/cli/{chunk-ef39ym8f.js → chunk-7bgbansg.js} +0 -0
- /package/dist/cli/{chunk-nh4wvyz3.js → chunk-atn06ncf.js} +0 -0
- /package/dist/cli/{chunk-h4n8ffqy.js → chunk-dmzg9zdc.js} +0 -0
- /package/dist/cli/{chunk-w25b7xxz.js → chunk-etfh8f4z.js} +0 -0
- /package/dist/cli/{chunk-hape5vwj.js → chunk-hpay0ax8.js} +0 -0
- /package/dist/cli/{chunk-f10n6063.js → chunk-pn5ygqn6.js} +0 -0
- /package/dist/cli/{chunk-rcyasw34.js → chunk-rxqdw6s5.js} +0 -0
- /package/dist/cli/{chunk-exfyqsdd.js → chunk-vjvkkrzc.js} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{getPersistedString,setPersistedString}from"./chunk-h9cg0j3a.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{__require}from"./chunk-wqfp8d9w.js";import{accessSync,existsSync,constants as fsConstants,readFileSync,statSync}from"fs";import{homedir}from"os";import{delimiter,dirname,join,resolve}from"path";import{fileURLToPath}from"url";var ROVE_SKILL_VERSION=54,ROVE_SKILL_REL_PATHS=[".agents/skills/rove/SKILL.md",".claude/skills/rove/SKILL.md"],SKILL_REL_PATHS=ROVE_SKILL_REL_PATHS;function skillInstallCommand(env=process.env){return`${ROVE_PRODUCT_NAME} skill install`}var SKILL_SOURCE_SLUG="Sma1lboy/rove";function bundledSkillDir(){let here=fileURLToPath(import.meta.url);return[resolve(here,"../../../../../.agents/skills/rove"),resolve(here,"../../skills/rove")].find((dir)=>existsSync(join(dir,"SKILL.md")))??null}function npxSkillsArgv(opts={}){let source=opts.source!==void 0?opts.source:bundledSkillDir(),agents=opts.agent===void 0?[]:typeof opts.agent==="string"?[opts.agent]:opts.agent;return["skills","add",source??SKILL_SOURCE_SLUG,"--skill",ROVE_PRODUCT_NAME,...opts.global===!1?[]:["--global"],...agents.flatMap((a)=>["--agent",a])]}function npxSkillsCommand(opts={}){return`npx ${npxSkillsArgv(opts).join(" ")}`}var NPX_MISSING_EXIT=127;function isNpxMissing(){let parts=(process.env.PATH??"").split(delimiter).filter(Boolean);if(parts.length===0)return!1;return!parts.some((dir)=>{try{return accessSync(join(dir,"npx"),fsConstants.X_OK),!0}catch{return!1}})}function npxMissingMessage(){return`${ROVE_PRODUCT_NAME} skill install needs \`npx\` (part of Node.js), which isn't on your PATH.
|
|
3
|
+
Install Node.js (https://nodejs.org) and run it again \u2014 the Rove installer only installs Bun and Rove.`}async function runNpxSkillsInstall(agent,global){if(isNpxMissing())return process.stderr.write(`
|
|
4
|
+
${npxMissingMessage()}
|
|
5
|
+
|
|
6
|
+
`),NPX_MISSING_EXIT;return await Bun.spawn(["npx",...npxSkillsArgv({...agent===void 0?{}:{agent},global})],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited}var HINT_SEEN_KEY="skillHintSeen";function markSkillHintSeen(){setPersistedString(HINT_SEEN_KEY,"1")}function roveSkillPaths(opts={}){let home=opts.home??homedir(),cwd=opts.cwd??process.cwd();return[home,cwd].flatMap((root)=>SKILL_REL_PATHS.map((rel)=>join(root,rel)))}function parseSkillVersion(content){let m=content.match(/rove-skill-version:\s*(\d+)/);return m?Number.parseInt(m[1],10):null}function skillVersionAt(path){try{return parseSkillVersion(readFileSync(path,"utf8"))}catch{return null}}function inodeKey(path){try{let stat=statSync(path);return`${stat.dev}:${stat.ino}`}catch{return path}}function distinctSkillFiles(roots,rels){let seen=new Set,out=[];for(let root of roots)for(let rel of rels){let path=join(root,rel);if(!existsSync(path))continue;let key=inodeKey(path);if(seen.has(key))continue;seen.add(key),out.push(path)}return out}function installedSkillDirs(home=homedir()){return distinctSkillFiles([home],SKILL_REL_PATHS).map((path)=>dirname(path))}function roveSkillState(opts={}){let roots=[opts.home??homedir(),opts.cwd??process.cwd()],best=[...distinctSkillFiles(roots,ROVE_SKILL_REL_PATHS).map((path)=>({path,version:skillVersionAt(path)}))].sort((a,b)=>(b.version??-1)-(a.version??-1))[0];if(!best)return{installed:!1,installedVersion:null,currentVersion:ROVE_SKILL_VERSION,stale:!1,path:null};let stale=best.version===null||best.version<ROVE_SKILL_VERSION;return{installed:!0,installedVersion:best.version,currentVersion:ROVE_SKILL_VERSION,stale,path:best.path}}function installedSkillDiffersFromBundled(installedPath){let bundled=bundledSkillDir();if(!bundled)return!1;try{return readFileSync(join(bundled,"SKILL.md"),"utf8")!==readFileSync(installedPath,"utf8")}catch{return!1}}function promptLine(){return new Promise((resolve2)=>{process.stdin.resume(),process.stdin.once("data",(d)=>{process.stdin.pause(),resolve2(String(d))})})}async function maybeHintSkillInstall(io={}){let{isRovePluginEnabled}=await import("./chunk-c2m55b72.js");if(isRovePluginEnabled())return;let cliName=ROVE_PRODUCT_NAME,installCommand=skillInstallCommand(),state=roveSkillState();if(!state.installed){if(getPersistedString(HINT_SEEN_KEY)==="1")return;setPersistedString(HINT_SEEN_KEY,"1"),process.stderr.write(`
|
|
7
|
+
${cliName}: the Rove agent skill isn't installed \u2014 install it so your coding agent can drive Rove via \`${cliName} api\`:
|
|
8
|
+
${installCommand}
|
|
9
|
+
(wraps \`${npxSkillsCommand()}\`; check anytime with \`${cliName} doctor\`)
|
|
10
|
+
|
|
11
|
+
`);return}let key=`${HINT_SEEN_KEY}:v${state.currentVersion}`;if(!state.stale)return;if(getPersistedString(key)==="1")return;let was=state.installedVersion===null?"an older version":`v${state.installedVersion}`;if(!(io.ask!==void 0||Boolean(process.stdin.isTTY&&process.stderr.isTTY))){setPersistedString(key,"1"),process.stderr.write(`
|
|
12
|
+
${cliName}: your Rove agent skill is out of date (${was}; this Rove wants v${state.currentVersion}) \u2014 refresh it so \`${cliName} api\` guidance matches:
|
|
13
|
+
${installCommand}
|
|
14
|
+
|
|
15
|
+
`);return}process.stderr.write(`
|
|
16
|
+
${cliName}: a new version of the Rove agent skill is available (${was} \u2192 v${state.currentVersion}).
|
|
17
|
+
Update now? [y]es / [n]o / [d]on't notify for this version: `);let answer=(await(io.ask??promptLine)()).trim().toLowerCase();if(answer==="y"||answer==="yes"){let code=await(io.install??runNpxSkillsInstall)();if(code===0)process.stderr.write(`${cliName}: skill updated.
|
|
18
|
+
`);else process.stderr.write(`${cliName}: skill update failed (exit ${code}) \u2014 run \`${installCommand}\` manually.
|
|
19
|
+
`)}else if(answer.startsWith("d"))setPersistedString(key,"1")}
|
|
20
|
+
export{skillInstallCommand,bundledSkillDir,npxSkillsArgv,npxSkillsCommand,NPX_MISSING_EXIT,isNpxMissing,runNpxSkillsInstall,markSkillHintSeen,roveSkillPaths,installedSkillDirs,roveSkillState,installedSkillDiffersFromBundled,maybeHintSkillInstall};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{BUNDLED_THEME_JSONS,userThemesDir,validateTheme}from"./chunk-
|
|
2
|
+
import{BUNDLED_THEME_JSONS,userThemesDir,validateTheme}from"./chunk-pdvyfz3n.js";import{expandTilde}from"./chunk-ag3txfg2.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{SUBCOMMAND_VERBS}from"./chunk-fhch0x0q.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-pn5ygqn6.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-wqfp8d9w.js";import{existsSync,mkdirSync,readFileSync,readdirSync,unlinkSync,writeFileSync}from"fs";import{basename,join,resolve}from"path";var CLI_NAME=ROVE_PRODUCT_NAME,THEME_VERB_ALIASES={ls:"list",rm:"remove"},BUNDLED_NAMES=Object.keys(BUNDLED_THEME_JSONS);function fail(message){process.stderr.write(`${CLI_NAME} theme: ${message}
|
|
3
3
|
`),process.exit(1)}function failUsage(message){process.stderr.write(`${CLI_NAME} theme: ${message}
|
|
4
4
|
|
|
5
5
|
`),printUsage(),process.exit(2)}function listThemes(){let lines=[];lines.push("bundled:");for(let name of[...BUNDLED_NAMES].sort())lines.push(` ${name} [built-in]`);let dir=userThemesDir(),userFiles=[];try{userFiles=readdirSync(dir).filter((f)=>f.endsWith(".json")).sort()}catch{}if(lines.push(""),lines.push(`user (${dir}):`),userFiles.length===0)lines.push(" (none)");else for(let f of userFiles){let name=f.slice(0,-5),path=join(dir,f),overridesBundled=BUNDLED_NAMES.includes(name)?" (overrides built-in)":"";lines.push(` ${name}${overridesBundled} ${path}`)}process.stdout.write(`${lines.join(`
|
|
@@ -7,5 +7,5 @@ import{BUNDLED_THEME_JSONS,userThemesDir,validateTheme}from"./chunk-s278gjet.js"
|
|
|
7
7
|
`)}async function readSource(source){if(/^https?:\/\//i.test(source)){let res;try{res=await fetch(source)}catch(err){fail(`failed to fetch ${source}: ${errorMessage(err)}`)}if(!res.ok)fail(`failed to fetch ${source}: HTTP ${res.status} ${res.statusText}`);let text2=await res.text(),cleanPath=source.split(/[?#]/)[0]??source,file2=basename(cleanPath)||"theme.json",defaultName2=file2.endsWith(".json")?file2.slice(0,-5):file2;return{text:text2,defaultName:defaultName2}}let abs=resolve(process.cwd(),expandTilde(source)),text;try{text=readFileSync(abs,"utf8")}catch(err){fail(`failed to read ${abs}: ${errorMessage(err)}`)}let file=basename(abs),defaultName=file.endsWith(".json")?file.slice(0,-5):file;return{text,defaultName}}function parseAddArgs(args){let source=null,name,force=!1;for(let i=0;i<args.length;i+=1){let a=args[i];if(a==="--force"||a==="-f"){force=!0;continue}if(a==="--name"||a==="-n"){let next=args[i+1];if(next===void 0)failUsage("--name requires a value");name=next,i+=1;continue}if(a.startsWith("--name=")){name=a.slice(7);continue}if(a.startsWith("--"))failUsage(`unknown flag: ${a}`);if(source===null){source=a;continue}failUsage(`unexpected positional argument: ${a}`)}if(source===null)failUsage("missing <source> (URL or path to theme JSON)");return{source,opts:{name,force}}}async function addTheme(args){let{source,opts}=parseAddArgs(args),{text,defaultName}=await readSource(source),parsed;try{parsed=JSON.parse(text)}catch(err){fail(`source is not valid JSON: ${errorMessage(err)}`)}let result=validateTheme(parsed);if(!result.ok)fail(`source is not a valid Rove theme: ${result.reason}`);let name=requireThemeName(opts.name??defaultName),dir=userThemesDir();mkdirSync(dir,{recursive:!0});let dest=join(dir,`${name}.json`);if(existsSync(dest)&&!opts.force)fail(`${dest} already exists (pass --force to overwrite)`);writeFileSync(dest,`${JSON.stringify(result.theme,null,2)}
|
|
8
8
|
`,"utf8"),process.stdout.write(`installed theme "${name}" -> ${dest}
|
|
9
9
|
`)}function requireThemeName(name){if(!name||!/^[a-zA-Z0-9._-]+$/.test(name)||name==="."||name==="..")fail(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);return name}function removeTheme(args){if(!args[0])failUsage("missing <name>");if(args.length>1)failUsage(`unexpected extra arguments after "${args[0]}"`);let name=requireThemeName(args[0]);if(BUNDLED_NAMES.includes(name))fail(`"${name}" is a built-in theme and cannot be removed`);let dest=join(userThemesDir(),`${name}.json`);if(!existsSync(dest))fail(`no user theme named "${name}" (looked for ${dest})`);unlinkSync(dest),process.stdout.write(`removed theme "${name}" (${dest})
|
|
10
|
-
`)}function printUsage(out=process.stderr){out.write([`Usage: ${CLI_NAME} theme <command> [args]`,"","Commands:"," list List bundled and user-installed themes"," add <source> [--name <name>] Install a theme from a URL or local path"," Pass --force to overwrite an existing user theme"," remove <name> Remove a user-installed theme","",`User themes live under: ${userThemesDir()}`,"Schema: https://raw.githubusercontent.com/sma1lboy/rove/main/packages/
|
|
10
|
+
`)}function printUsage(out=process.stderr){out.write([`Usage: ${CLI_NAME} theme <command> [args]`,"","Commands:"," list List bundled and user-installed themes"," add <source> [--name <name>] Install a theme from a URL or local path"," Pass --force to overwrite an existing user theme"," remove <name> Remove a user-installed theme","",`User themes live under: ${userThemesDir()}`,"Schema: https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/theme.schema.json",""].join(`
|
|
11
11
|
`))}async function runThemeSubcommand(args){let[action,...rest]=args;if(!action||action==="--help"||action==="-h"||action==="help"){if(printUsage(action?process.stdout:process.stderr),!action)process.exit(2);return}let verb=THEME_VERB_ALIASES[action]??action;if(!SUBCOMMAND_VERBS.theme.includes(verb))failUsage(`unknown action "${action}" (try ${SUBCOMMAND_VERBS.theme.map((v)=>`"${v}"`).join(", ")})`);if(verb==="list"){if(rest.length>0)failUsage(`"${action}" takes no arguments`);listThemes();return}if(verb==="add"){await addTheme(rest);return}if(verb==="remove"){removeTheme(rest);return}failUsage(`unknown action "${action}"`)}export{runThemeSubcommand};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{engineEntry,identifiableEngineIds}from"./chunk-
|
|
2
|
+
import{engineEntry,identifiableEngineIds}from"./chunk-y8tysjc2.js";import{recordSpawn}from"./chunk-40dgv828.js";import{loadStateFile}from"./chunk-7q43tysm.js";import{PsProbeUnavailableError}from"./chunk-pxk2c2vj.js";import{__require}from"./chunk-wqfp8d9w.js";import{basename}from"path";var WRAPPERS=new Set(["node","bun","npx","deno","sh","zsh","bash","env","script"]);function binaryName(token){return basename(token).replace(/\.(exe|js|mjs|cjs)$/,"")}function executableNameFromArgv(argv){for(let token of argv.slice(0,8)){if(/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))continue;let name=binaryName(token);if(WRAPPERS.has(name))continue;return name||null}return null}function vendorFromArgv(commandLine){let name=executableNameFromArgv(commandLine.trim().split(/\s+/));if(!name)return null;return identifiableEngineIds().find((v)=>{let entry=engineEntry(v);return entry.defaultCommand[0]===name||entry.processNames?.includes(name)===!0})??null}function parsePsSnapshot(text){let rows=[];for(let line of text.split(`
|
|
3
3
|
`)){let m=/^\s*(\d+)\s+(\d+)\s+(\S.*)$/.exec(line);if(m)rows.push({pid:Number(m[1]),ppid:Number(m[2]),args:m[3]})}return rows}function childrenIndex(rows){let kids=new Map;for(let row of rows){let list=kids.get(row.ppid);if(list)list.push(row);else kids.set(row.ppid,[row])}return kids}function hasAncestor(rows,pid,ancestorPid){let parents=new Map(rows.map((r)=>[r.pid,r.ppid])),cur=pid;for(let hops=0;hops<=rows.length;hops++){if(cur===ancestorPid)return!0;let next=parents.get(cur);if(next===void 0||next<=1)return!1;cur=next}return!1}function customEngineBinaries(){let state=loadStateFile(),ids=state.customEngineIds,out=new Map;if(!Array.isArray(ids))return out;for(let id of ids){if(typeof id!=="string"||id.trim().length===0)continue;let command=state[`engineCommand.${id}`],argv=typeof command==="string"&&command.trim().length>0?command.trim().split(/\s+/):[id],name=executableNameFromArgv(argv);if(name)out.set(name,id)}return out}function findDescendant(rows,rootPid,pick){let kids=childrenIndex(rows),queue=[...kids.get(rootPid)??[]],visited=new Set;while(queue.length>0){let row=queue.shift();if(!row)break;if(visited.has(row.pid))continue;visited.add(row.pid);let hit=pick(row);if(hit!==null)return hit;queue.push(...kids.get(row.pid)??[])}return null}function walkDescendants(rows,rootPid,identify){return findDescendant(rows,rootPid,(row)=>{let vendor=identify(row.args);return vendor?{vendor,argv:row.args,pid:row.pid}:null})}function foregroundEngineIn(rows,rootPid){let builtin=walkDescendants(rows,rootPid,vendorFromArgv);if(builtin)return builtin;let custom=customEngineBinaries();if(custom.size===0)return null;return walkDescendants(rows,rootPid,(args)=>custom.get(executableNameFromArgv(args.trim().split(/\s+/))??"")??null)}function engineProcessIn(rows,rootPid,extraLaunch){if(foregroundEngineIn(rows,rootPid))return!0;let expected=extraLaunch?executableNameFromArgv(typeof extraLaunch==="string"?[extraLaunch]:extraLaunch):null;if(!expected)return!1;return findDescendant(rows,rootPid,(row)=>executableNameFromArgv(row.args.trim().split(/\s+/))===expected?!0:null)===!0}var PS_PROBE_TIMEOUT_MS=5000,bunPsSpawn=()=>{recordSpawn("engine.foregroundWalk",["ps","-A","-o","pid=,ppid=,args="]);let proc=Bun.spawn(["ps","-A","-o","pid=,ppid=,args="],{stdout:"pipe",stderr:"ignore"});return{text:new Response(proc.stdout).text(),kill:()=>proc.kill()}};async function psSnapshotWith(spawn,timeoutMs=PS_PROBE_TIMEOUT_MS){let proc=spawn(),timer;try{let text=await Promise.race([proc.text,new Promise((_resolve,reject)=>{timer=setTimeout(()=>{try{proc.kill()}catch{}reject(new PsProbeUnavailableError(`ps did not answer within ${timeoutMs}ms`))},timeoutMs)})]);if(parsePsSnapshot(text).length===0)throw new PsProbeUnavailableError("ps returned no usable rows");return text}finally{if(timer)clearTimeout(timer)}}var psSnapshot=async(anchors)=>{if(process.platform!=="win32")return psSnapshotWith(bunPsSpawn);let{defaultWinProcessProbe,winProcessSnapshot}=await import("./chunk-kxaqge09.js");return winProcessSnapshot(anchors??[],defaultWinProcessProbe())};async function foregroundEngine(rootPid,snapshot=psSnapshot){try{return foregroundEngineIn(parsePsSnapshot(await snapshot([rootPid])),rootPid)}catch{return null}}
|
|
4
4
|
export{vendorFromArgv,parsePsSnapshot,hasAncestor,foregroundEngineIn,engineProcessIn,PS_PROBE_TIMEOUT_MS,psSnapshotWith,psSnapshot,foregroundEngine};
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
async function run(argv){try{let proc=Bun.spawn([...argv],{stdin:"ignore",stdout:"pipe",stderr:"pipe"}),[stdout,stderr,code]=await Promise.all([new Response(proc.stdout).text().catch(()=>""),new Response(proc.stderr).text().catch(()=>""),proc.exited]);return{code,stdout,stderr,missing:!1}}catch(error){let systemError=error;return{code:127,stdout:"",stderr:error instanceof Error?error.message:String(error),missing:systemError.code==="ENOENT"}}}async function runTmux(socket,args){return run(["tmux","-L",socket,...args])}function commandFailure(label,result){return`${label} failed: ${result.stderr.trim()||`exit ${result.code}`}`}function isMissingServer(result){return/no server running|error connecting to|failed to connect to server|no sessions/i.test(`${result.stdout}
|
|
3
3
|
${result.stderr}`)}function parsePositiveInts(output){return output.split(`
|
|
4
4
|
`).map((line)=>Number.parseInt(line.trim(),10)).filter((value)=>Number.isFinite(value)&&value>1)}function parseLegacyPsRows(output){let rows=[];for(let line of output.split(`
|
|
5
|
-
`).slice(1)){let parts=line.trim().split(/\s+/);if(parts.length<4)continue;let pid=Number.parseInt(parts[0]??"",10),pgid=Number.parseInt(parts[1]??"",10),rssKb=Number.parseInt(parts[2]??"",10);if(!Number.isFinite(pid)||!Number.isFinite(pgid)||!Number.isFinite(rssKb))continue;rows.push({pid,pgid,rssKb,command:parts.slice(3).join(" ")})}return rows}function legacyPaneProcesses(rows,panePids){let groups=new Set(panePids);return rows.filter((row)=>groups.has(row.pgid))}function emptyReport(available,version,error=null){return{available,version,sessions:[],panePids:[],processes:[],error}}async function inspectLegacyTmux(socket="
|
|
6
|
-
`).map((line)=>line.trim()).filter(Boolean);if(sessions.length===0)return emptyReport(!0,version);let panesResult=await runTmux(socket,["list-panes","-a","-F","#{pane_pid}"]);if(panesResult.code!==0)return{...emptyReport(!0,version,commandFailure("tmux list-panes",panesResult)),sessions};let panePids=parsePositiveInts(panesResult.stdout);if(panePids.length===0)return{...emptyReport(!0,version,"tmux reported live sessions but no pane process groups"),sessions};let psResult=await run(["ps","-axo","pid,pgid,rss,comm"]);if(psResult.code!==0)return{available:!0,version,sessions,panePids,processes:[],error:commandFailure("ps",psResult)};return{available:!0,version,sessions,panePids,processes:legacyPaneProcesses(parseLegacyPsRows(psResult.stdout),panePids),error:null}}function legacyTmuxDoctorLines(report,socket="
|
|
5
|
+
`).slice(1)){let parts=line.trim().split(/\s+/);if(parts.length<4)continue;let pid=Number.parseInt(parts[0]??"",10),pgid=Number.parseInt(parts[1]??"",10),rssKb=Number.parseInt(parts[2]??"",10);if(!Number.isFinite(pid)||!Number.isFinite(pgid)||!Number.isFinite(rssKb))continue;rows.push({pid,pgid,rssKb,command:parts.slice(3).join(" ")})}return rows}function legacyPaneProcesses(rows,panePids){let groups=new Set(panePids);return rows.filter((row)=>groups.has(row.pgid))}function emptyReport(available,version,error=null){return{available,version,sessions:[],panePids:[],processes:[],error}}async function inspectLegacyTmux(socket="rove"){let versionResult=await run(["tmux","-V"]);if(versionResult.missing)return emptyReport(!1,null);if(versionResult.code!==0)return emptyReport(!0,null,commandFailure("tmux -V",versionResult));let version=versionResult.stdout.trim()||null,sessionsResult=await runTmux(socket,["list-sessions","-F","#{session_name}"]);if(sessionsResult.code!==0){if(isMissingServer(sessionsResult))return emptyReport(!0,version);return emptyReport(!0,version,commandFailure("tmux list-sessions",sessionsResult))}let sessions=sessionsResult.stdout.split(`
|
|
6
|
+
`).map((line)=>line.trim()).filter(Boolean);if(sessions.length===0)return emptyReport(!0,version);let panesResult=await runTmux(socket,["list-panes","-a","-F","#{pane_pid}"]);if(panesResult.code!==0)return{...emptyReport(!0,version,commandFailure("tmux list-panes",panesResult)),sessions};let panePids=parsePositiveInts(panesResult.stdout);if(panePids.length===0)return{...emptyReport(!0,version,"tmux reported live sessions but no pane process groups"),sessions};let psResult=await run(["ps","-axo","pid,pgid,rss,comm"]);if(psResult.code!==0)return{available:!0,version,sessions,panePids,processes:[],error:commandFailure("ps",psResult)};return{available:!0,version,sessions,panePids,processes:legacyPaneProcesses(parseLegacyPsRows(psResult.stdout),panePids),error:null}}function legacyTmuxDoctorLines(report,socket="rove"){if(report.error)return[`legacy tmux: \u2717 inspection failed \u2014 ${report.error}`];if(!report.available)return["legacy tmux: not installed \u2014 no pre-v0.8 sessions to inspect"];let version=report.version??"tmux (version unknown)";if(report.sessions.length===0)return[`legacy tmux: ${version} \u2014 no sessions on \`${socket}\``];let totalMb=report.processes.reduce((sum,row)=>sum+row.rssKb,0)/1024,grouped=new Map;for(let row of report.processes){let item=grouped.get(row.command)??{count:0,rssKb:0};item.count++,item.rssKb+=row.rssKb,grouped.set(row.command,item)}let lines=[`legacy tmux: \u26A0 ${version} \u2014 ${report.sessions.length} pre-v0.8 session(s) on \`${socket}\``,` ${report.processes.length} process(es) across ${report.panePids.length} pane(s), ${totalMb.toFixed(1)} MB RSS total`];for(let[command,item]of[...grouped].sort((a,b)=>b[1].rssKb-a[1].rssKb))lines.push(` ${command}: ${item.count} proc, ${(item.rssKb/1024).toFixed(1)} MB`);return lines.push(" \u2192 run `rove reset` to stop this retired runtime safely"),lines}var PROCESS_GROUP_EXIT_GRACE_MS=6000,PROCESS_GROUP_POLL_MS=100;async function ownProcessGroup(){let result=await run(["ps","-o","pgid=","-p",String(process.pid)]);if(result.code!==0)return{pgid:null,error:commandFailure("ps current process group",result)};let pgid=Number.parseInt(result.stdout.trim(),10);if(!Number.isFinite(pgid)||pgid<=1)return{pgid:null,error:"unable to determine current process group"};return{pgid,error:null}}function processGroupAlive(pgid){try{return process.kill(-pgid,0),!0}catch(error){return error.code!=="ESRCH"}}async function waitForProcessGroupsExit(pgids){let deadline=Date.now()+PROCESS_GROUP_EXIT_GRACE_MS,survivors=pgids.filter(processGroupAlive);while(survivors.length>0&&Date.now()<deadline)await new Promise((resolve)=>setTimeout(resolve,PROCESS_GROUP_POLL_MS)),survivors=survivors.filter(processGroupAlive);return survivors}async function stopLegacyTmux(socket="rove"){let report=await inspectLegacyTmux(socket);if(report.error)return{status:"failed",sessions:report.sessions.length,signalledGroups:0,error:report.error};if(!report.available||report.sessions.length===0)return{status:"absent",sessions:0,signalledGroups:0};let ownGroup=await ownProcessGroup();if(ownGroup.error)return{status:"failed",sessions:report.sessions.length,signalledGroups:0,error:ownGroup.error};let signalledPaneGroups=[];for(let pgid of new Set(report.panePids)){if(pgid===ownGroup.pgid)continue;try{process.kill(-pgid,"SIGTERM"),signalledPaneGroups.push(pgid)}catch(error){if(error.code==="ESRCH")continue;return{status:"failed",sessions:report.sessions.length,signalledGroups:signalledPaneGroups.length,error:`failed to SIGTERM pane group ${pgid}: ${error instanceof Error?error.message:String(error)}`}}}let result=await runTmux(socket,["kill-server"]);if(result.code!==0){let remaining=await inspectLegacyTmux(socket);if(remaining.error||remaining.sessions.length>0)return{status:"failed",sessions:report.sessions.length,signalledGroups:signalledPaneGroups.length,error:remaining.error??(result.stderr.trim()||`tmux exited ${result.code}`)}}let survivors=await waitForProcessGroupsExit(signalledPaneGroups);if(survivors.length>0)return{status:"failed",sessions:report.sessions.length,signalledGroups:signalledPaneGroups.length,error:`pane process groups still alive after ${PROCESS_GROUP_EXIT_GRACE_MS}ms: ${survivors.join(", ")}`};return{status:"stopped",sessions:report.sessions.length,signalledGroups:signalledPaneGroups.length}}
|
|
7
7
|
export{inspectLegacyTmux,legacyTmuxDoctorLines,stopLegacyTmux};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{RoveDaemonClient}from"./chunk-adkva3ep.js";import{defaultDaemonSocketPath,defaultPtyHostSocketPath}from"./chunk-npwzmmv9.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
|
+
using System;
|
|
4
|
+
using System.Runtime.InteropServices;
|
|
5
|
+
using System.Text;
|
|
6
|
+
static class RovePtyJob {
|
|
7
|
+
[StructLayout(LayoutKind.Sequential)] struct BASIC { public long PerProcessUserTimeLimit, PerJobUserTimeLimit; public uint LimitFlags; public UIntPtr MinimumWorkingSetSize, MaximumWorkingSetSize; public uint ActiveProcessLimit; public UIntPtr Affinity; public uint PriorityClass, SchedulingClass; }
|
|
8
|
+
[StructLayout(LayoutKind.Sequential)] struct IO { public ulong R, W, O, RB, WB, OB; }
|
|
9
|
+
[StructLayout(LayoutKind.Sequential)] struct EXT { public BASIC Basic; public IO Io; public UIntPtr ProcessMemoryLimit, JobMemoryLimit, PeakProcessMemoryUsed, PeakJobMemoryUsed; }
|
|
10
|
+
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] struct SI { public int cb; public string lpReserved, lpDesktop, lpTitle; public int dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags; public short wShowWindow, cbReserved2; public IntPtr lpReserved2, hStdInput, hStdOutput, hStdError; }
|
|
11
|
+
[StructLayout(LayoutKind.Sequential)] struct PI { public IntPtr hProcess, hThread; public int dwProcessId, dwThreadId; }
|
|
12
|
+
delegate bool CtrlHandler(uint type);
|
|
13
|
+
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateJobObject(IntPtr sa, string name);
|
|
14
|
+
[DllImport("kernel32.dll", SetLastError=true)] static extern bool SetInformationJobObject(IntPtr job, int cls, ref EXT info, int len);
|
|
15
|
+
[DllImport("kernel32.dll", SetLastError=true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr proc);
|
|
16
|
+
[DllImport("kernel32.dll")] static extern IntPtr GetCurrentProcess();
|
|
17
|
+
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] static extern IntPtr GetCommandLine();
|
|
18
|
+
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool CreateProcess(string app, StringBuilder cmd, IntPtr pa, IntPtr ta, bool inherit, uint flags, IntPtr env, string cwd, ref SI si, out PI pi);
|
|
19
|
+
[DllImport("kernel32.dll")] static extern uint WaitForSingleObject(IntPtr h, uint ms);
|
|
20
|
+
[DllImport("kernel32.dll")] static extern bool GetExitCodeProcess(IntPtr h, out uint code);
|
|
21
|
+
[DllImport("kernel32.dll")] static extern bool SetConsoleCtrlHandler(CtrlHandler h, bool add);
|
|
22
|
+
[DllImport("kernel32.dll")] static extern bool SetProcessWorkingSetSize(IntPtr p, IntPtr min, IntPtr max);
|
|
23
|
+
static CtrlHandler keep;
|
|
24
|
+
static int SkipToken(string s, int i) {
|
|
25
|
+
if (i < s.Length && s[i] == '"') { int end = s.IndexOf('"', i + 1); return end < 0 ? s.Length : end + 1; }
|
|
26
|
+
while (i < s.Length && s[i] != ' ' && s[i] != '\\t') i++;
|
|
27
|
+
return i;
|
|
28
|
+
}
|
|
29
|
+
static int SkipSpace(string s, int i) { while (i < s.Length && (s[i] == ' ' || s[i] == '\\t')) i++; return i; }
|
|
30
|
+
static int Main() {
|
|
31
|
+
string raw = Marshal.PtrToStringUni(GetCommandLine());
|
|
32
|
+
string rest = raw.Substring(SkipSpace(raw, SkipToken(raw, 0)));
|
|
33
|
+
if (rest.Length == 0) { Console.Error.WriteLine("rove-pty-job: usage: <command line> (job name in ${PTY_JOB_ENV})"); return 2; }
|
|
34
|
+
string name = Environment.GetEnvironmentVariable("${PTY_JOB_ENV}");
|
|
35
|
+
IntPtr job = String.IsNullOrEmpty(name) ? IntPtr.Zero : CreateJobObject(IntPtr.Zero, name);
|
|
36
|
+
if (job != IntPtr.Zero) {
|
|
37
|
+
EXT info = new EXT();
|
|
38
|
+
info.Basic.LimitFlags = 0x2000; // KILL_ON_JOB_CLOSE; no BREAKAWAY_OK (see module doc)
|
|
39
|
+
SetInformationJobObject(job, 9, ref info, Marshal.SizeOf(typeof(EXT)));
|
|
40
|
+
AssignProcessToJobObject(job, GetCurrentProcess());
|
|
41
|
+
}
|
|
42
|
+
keep = delegate (uint type) { return type <= 1; }; // CTRL_C, CTRL_BREAK: the shell's to handle
|
|
43
|
+
SetConsoleCtrlHandler(keep, true);
|
|
44
|
+
SI si = new SI(); si.cb = Marshal.SizeOf(typeof(SI));
|
|
45
|
+
PI pi;
|
|
46
|
+
if (!CreateProcess(null, new StringBuilder(rest), IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi)) {
|
|
47
|
+
Console.Error.WriteLine("rove-pty-job: could not start " + rest + " (error " + Marshal.GetLastWin32Error() + ")");
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
// From here it only waits: hand its pages back (14 MB resident \u2192 2 MB per tab, measured).
|
|
51
|
+
SetProcessWorkingSetSize(GetCurrentProcess(), (IntPtr)(-1), (IntPtr)(-1));
|
|
52
|
+
WaitForSingleObject(pi.hProcess, 0xFFFFFFFF);
|
|
53
|
+
uint code;
|
|
54
|
+
GetExitCodeProcess(pi.hProcess, out code);
|
|
55
|
+
GC.KeepAlive(keep);
|
|
56
|
+
return (int)code;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;function sameSocket(a,b){let norm=(p)=>resolve(p.replace(/\//g,"\\")).toLowerCase();return norm(a)===norm(b)}function withoutPtyJob(env){let out={...env};return delete out[PTY_JOB_ENV],delete out[PTY_JOB_OWNER_ENV],out}function ptyJobLaunch(env,ownDaemonSocket){let job=env[PTY_JOB_ENV],owner=env[PTY_JOB_OWNER_ENV];if(!job||!owner)return null;return{job,mode:sameSocket(owner,ownDaemonSocket)?"escape":"join"}}import{spawn}from"child_process";import{existsSync}from"fs";import{join as join2}from"path";var LAUNCH_CMD_ENV="ROVE_LAUNCH_CMD",LAUNCH_LOG_ENV="ROVE_LAUNCH_LOG",WIN_DETACHED_LAUNCHER_PS=`
|
|
60
|
+
$ErrorActionPreference = 'Stop'
|
|
61
|
+
$cmd = $env:${LAUNCH_CMD_ENV}
|
|
62
|
+
$log = $env:${LAUNCH_LOG_ENV}
|
|
63
|
+
if (-not $cmd -or -not $log) { [Console]::Error.WriteLine('rove launcher: ${LAUNCH_CMD_ENV}/${LAUNCH_LOG_ENV} not set'); exit 1 }
|
|
64
|
+
Remove-Item Env:${LAUNCH_CMD_ENV}, Env:${LAUNCH_LOG_ENV}
|
|
65
|
+
Add-Type -TypeDefinition @'
|
|
66
|
+
using System;
|
|
67
|
+
using System.Runtime.InteropServices;
|
|
68
|
+
public static class RoveDetachedLaunch {
|
|
69
|
+
[StructLayout(LayoutKind.Sequential)] public struct SA { public int nLength; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; }
|
|
70
|
+
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] public struct SI { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags; public short wShowWindow, cbReserved2; public IntPtr lpReserved2, hStdInput, hStdOutput, hStdError; }
|
|
71
|
+
[StructLayout(LayoutKind.Sequential)] public struct PI { public IntPtr hProcess, hThread; public int dwProcessId, dwThreadId; }
|
|
72
|
+
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateFile(string name, uint access, uint share, ref SA sa, uint disposition, uint flags, IntPtr template);
|
|
73
|
+
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool CreateProcess(string app, string cmd, IntPtr pa, IntPtr ta, bool inherit, uint flags, IntPtr env, string cwd, ref SI si, out PI pi);
|
|
74
|
+
[DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);
|
|
75
|
+
public static int Launch(string cmd, string log) {
|
|
76
|
+
SA sa = new SA(); sa.nLength = Marshal.SizeOf(sa); sa.bInheritHandle = true;
|
|
77
|
+
// FILE_APPEND_DATA | GENERIC_READ, shared read/write, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL
|
|
78
|
+
IntPtr h = CreateFile(log, 0x0004 | 0x80000000, 0x1 | 0x2, ref sa, 4, 0x80, IntPtr.Zero);
|
|
79
|
+
if (h == (IntPtr)(-1)) throw new Exception("CreateFile(" + log + ") failed: " + Marshal.GetLastWin32Error());
|
|
80
|
+
IntPtr nul = CreateFile("NUL", 0x80000000, 0x1 | 0x2, ref sa, 3, 0x80, IntPtr.Zero);
|
|
81
|
+
SI si = new SI(); si.cb = Marshal.SizeOf(si);
|
|
82
|
+
si.dwFlags = 0x100 | 0x1; // STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
|
|
83
|
+
si.wShowWindow = 0; // SW_HIDE
|
|
84
|
+
si.hStdInput = nul; si.hStdOutput = h; si.hStdError = h;
|
|
85
|
+
PI pi;
|
|
86
|
+
uint flags = 0x00000010 | 0x01000000 | 0x00000200; // CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_PROCESS_GROUP
|
|
87
|
+
if (!CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero, true, flags, IntPtr.Zero, null, ref si, out pi)) throw new Exception("CreateProcess failed: " + Marshal.GetLastWin32Error());
|
|
88
|
+
CloseHandle(h); CloseHandle(nul); CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
|
|
89
|
+
return pi.dwProcessId;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
'@
|
|
93
|
+
[RoveDetachedLaunch]::Launch($cmd, $log) | Out-Null
|
|
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
|
+
`)}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};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var BUILTIN_VENDORS=["claude","codex","copilot","kimi","pi","omp","bob"],DEFAULT_VENDOR=BUILTIN_VENDORS[0],ALL_VENDORS=[...BUILTIN_VENDORS];function isBuiltinVendor(id){return id!==void 0&&BUILTIN_VENDORS.includes(id)}function nextVendorWithin(list,current){if(list.length===0)return current;let i=list.indexOf(current);return list[(i+1)%list.length]??list[0]??current}function prevVendorWithin(list,current){if(list.length===0)return current;let i=list.indexOf(current);if(i<0)return list[list.length-1]??current;return list[(i-1+list.length)%list.length]??current}function coerceVendorId(value){let v=value?.trim();return v&&v.length>0?v:"claude"}
|
|
3
|
+
export{BUILTIN_VENDORS,DEFAULT_VENDOR,ALL_VENDORS,isBuiltinVendor,nextVendorWithin,prevVendorWithin,coerceVendorId};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{isEngineActivityKind}from"./chunk-17e2tgyg.js";import{loadPluginRegistry,readPluginManifest}from"./chunk-rqyaf0dw.js";import{activityHookAdapters}from"./chunk-y8tysjc2.js";import{flagValue}from"./chunk-zsfyj36g.js";import{connectIfRunning}from"./chunk-96cjpssp.js";import{getPersistedString,setPersistedString}from"./chunk-h9cg0j3a.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{readRoveEnv}from"./chunk-nc46gddg.js";import{__require}from"./chunk-wqfp8d9w.js";import{join,resolve}from"path";var STDIN_READ_TIMEOUT_MS=500;async function readTextWithTimeout(read,timeoutMs=STDIN_READ_TIMEOUT_MS){let raceTimer;try{return await Promise.race([read(),new Promise((resolve2)=>{raceTimer=setTimeout(()=>resolve2(""),timeoutMs)})])}finally{if(raceTimer!==void 0)clearTimeout(raceTimer)}}async function readStdinText(){let bun=globalThis.Bun;if(bun)return bun.stdin.text();if(process.stdin.isTTY)return"";let chunks=[];try{for await(let chunk of process.stdin)chunks.push(chunk)}finally{process.stdin.destroy()}return Buffer.concat(chunks).toString("utf8")}async function readStdinPayload(){try{let text=await readTextWithTimeout(readStdinText);if(!text.trim())return{};let parsed=JSON.parse(text);return parsed&&typeof parsed==="object"&&!Array.isArray(parsed)?parsed:{}}catch{return{}}}async function runHookSubcommand(argv){let[verb,...rest]=argv;if(verb==="setup"){await runHookSetup(rest);return}if(verb==="cleanup"){await runHookCleanup();return}try{if(!verb||!isEngineActivityKind(verb))return;let payload=await readStdinPayload(),payloadFlag=flagValue(rest,"--payload");if(payloadFlag)try{let extra=JSON.parse(payloadFlag);if(extra&&typeof extra==="object"&&!Array.isArray(extra))Object.assign(payload,extra)}catch{}let taskId=flagValue(rest,"--task-id"),envTaskId=process.env.ROVE_TASK_ID,envTabId=process.env.ROVE_TAB_ID,engine=flagValue(rest,"--engine"),adapters=activityHookAdapters().filter((a)=>!engine||a.vendor===engine);if(!taskId&&adapters.some((a)=>a.isUnattendedSession?.(process.env)===!0))return;let cwd=typeof payload.cwd==="string"&&payload.cwd?payload.cwd:void 0;for(let adapter of adapters){if(cwd)break;cwd=adapter.cwdFromPayload?.(payload)}cwd||=process.cwd();let detail;for(let adapter of adapters)if(detail=adapter.activityDetailFromPayload(verb,payload),detail)break;let session;for(let adapter of adapters)if(session=adapter.sessionFromPayload(payload),session)break;let client=await connectIfRunning();if(!client)return;try{let effectiveTaskId=taskId??envTaskId;await client.request("engine.reportEvent",{...effectiveTaskId?{taskId:effectiveTaskId}:{cwd},kind:verb,...engine?{engine}:{},...envTabId?{tabId:envTabId}:{},...detail?{detail}:{},...session?{sessionId:session.sessionId}:{},...session?.transcriptPath?{transcriptPath:session.transcriptPath}:{}})}finally{client.close()}}catch(err){if(readRoveEnv("HOOK_DEBUG"))console.error(`[rove hook] ${verb} failed:`,err instanceof Error?err.message:String(err))}}var SYNC_SETTING_KEY="externalWorktreeSync";function worktreeSyncAdapters(){return activityHookAdapters().filter((a)=>a.supportsWorktreeSync())}function globalSettingsPath(){return worktreeSyncAdapters()[0]?.globalSettingsPath()}function persistedSyncPath(stored){if(!stored||stored==="off")return;if(stored==="global")return globalSettingsPath();if(stored.startsWith("repo:"))return join(resolve(stored.slice(5)),".claude","settings.json");return stored}async function ensureGlobalRoveHooks(opts={}){try{let{isRovePluginEnabled,detectLegacyInstalls,migrationHint}=await import("./chunk-c2m55b72.js"),pluginMode=isRovePluginEnabled();if(pluginMode){let hint=migrationHint(detectLegacyInstalls(),ROVE_PRODUCT_NAME);if(hint)process.stderr.write(`
|
|
3
|
+
${hint}
|
|
4
|
+
`)}let toolEvents=pluginsWantToolEvents();for(let a of activityHookAdapters()){if(pluginMode&&a.vendor==="claude")continue;let enginePath=a.globalSettingsPath();if(!enginePath)continue;await a.installActivityHooks(enginePath,{toolEvents,quiet:opts.quiet}),await a.removeWorktreeWatchHook(enginePath)}await cleanupWorktreeSyncHook()}catch{}}function pluginsWantToolEvents(){try{for(let entry of loadPluginRegistry().plugins){if(!entry.enabled)continue;try{if(readPluginManifest(entry.root).manifest.events.some((e)=>e.on.startsWith("tool.")))return!0}catch{}}}catch{}return!1}async function cleanupWorktreeSyncHook(){let adapters=worktreeSyncAdapters();if(adapters.length===0)return;let stored=getPersistedString(SYNC_SETTING_KEY),paths=new Set;for(let adapter of adapters){let file=adapter.globalSettingsPath();if(file)paths.add(file)}let prev=persistedSyncPath(stored);if(prev)paths.add(prev);for(let a of adapters)for(let p of paths)await a.removeWorktreeSyncHook(p);if(stored!=="off")setPersistedString(SYNC_SETTING_KEY,"off")}async function runHookCleanup(){let claude=activityHookAdapters().find((a)=>a.vendor==="claude"),path=claude?.globalSettingsPath();if(!claude||!path){process.stdout.write(`rove hook cleanup: no Claude hook adapter \u2014 nothing to do.
|
|
5
|
+
`);return}await claude.removeActivityHooks(path),await claude.removeWorktreeWatchHook(path),process.stdout.write([`rove hook cleanup: removed Rove's settings-managed hooks from ${path}.`,"Only Rove's own entries were touched; your other hooks are intact.","The Claude Code plugin's hooks.json now carries these events (if the plugin","is not installed, the next Rove launch reinstalls the settings-managed set).",""].join(`
|
|
6
|
+
`))}async function runHookSetup(_argv){await cleanupWorktreeSyncHook(),process.stdout.write(["rove hook setup is deprecated and now a no-op (cleanup only).","","The old external-worktree sync used a global WorktreeCreate hook, which is","a VCS provider hook \u2014 its presence broke `claude --worktree` / EnterWorktree","in every repo. Any hook Rove previously installed has been removed.","","Sync is now automatic: a `claude --worktree` (or any session) started in a","worktree under a repo Rove already tracks is adopted as a task on launch.","To adopt existing worktrees on demand, use the New Task dialog or `rove adopt`.",""].join(`
|
|
7
|
+
`))}
|
|
8
|
+
export{readTextWithTimeout,readStdinText,runHookSubcommand,ensureGlobalRoveHooks};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{loadPluginRegistry,readPluginManifest}from"./chunk-
|
|
2
|
+
import{loadPluginRegistry,readPluginManifest}from"./chunk-rqyaf0dw.js";import{CONTRIB_ENGINE_IDS,clearPluginEngines,registerPluginEngine,resetAvailableVendorsCache}from"./chunk-576r5nht.js";import{getCustomEngineIds}from"./chunk-h9cg0j3a.js";function loadPluginEngines(homeDir){let registered=[];try{for(let entry of loadPluginRegistry(homeDir).plugins){if(!entry.enabled)continue;try{let{manifest}=readPluginManifest(entry.root);for(let engine of manifest.engines){let identity={shortName:engine.identity?.shortName??engine.name};if(registerPluginEngine(engine.id,{displayName:engine.name,defaultCommand:engine.command,...engine.processNames?{processNames:engine.processNames}:{},screenManifest:{rules:engine.rules},identity,...engine.firstMessageDelivery?{firstMessageDelivery:engine.firstMessageDelivery}:{}}))registered.push(engine.id);else console.warn(`[rove] plugin ${entry.id}: engine id \`${engine.id}\` shadows a built-in or shipped engine \u2014 skipped`)}}catch{}}}catch{}return registered}function reloadPluginEngines(homeDir){clearPluginEngines();let registered=loadPluginEngines(homeDir);return loadedOnce=registered,resetAvailableVendorsCache(),registered}var loadedOnce;function ensurePluginEnginesLoaded(){return loadedOnce??=loadPluginEngines(),loadedOnce}function registeredEngineIds(){return[...new Set([...CONTRIB_ENGINE_IDS,...getCustomEngineIds(),...ensurePluginEnginesLoaded()])]}
|
|
3
3
|
export{loadPluginEngines,reloadPluginEngines,ensurePluginEnginesLoaded,registeredEngineIds};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{ulid}from"./chunk-qkehg2c1.js";import{acquireWithRetry,mergeTasksWithDisk,readDiskIndex,recoverIndexFromDisk}from"./chunk-qzsahxd9.js";import{DEFAULT_TASK_VENDOR,toTaskId}from"./chunk-xy8be792.js";import{release}from"./chunk-mw0ysrpd.js";import{ROVE_STATE_DIR_BASENAME}from"./chunk-zt7wfnva.js";import{preRenameStateDir}from"./chunk-b39sdtt0.js";import{readRoveHomeDirEnv}from"./chunk-nc46gddg.js";import{mkdir,open,rename,unlink}from"fs/promises";import{homedir}from"os";import{dirname,join}from"path";var CURRENT_VERSION=3;class TaskIndexStore{homeDir;roveDir;path;legacyPath;lockPath;cache={version:CURRENT_VERSION,tasks:[]};loaded=!1;listeners=new Set;saveChain=Promise.resolve();dirtyIds=new Set;removedIds=new Map;constructor(options={}){this.homeDir=options.homeDir??readRoveHomeDirEnv()??homedir(),this.roveDir=join(this.homeDir,ROVE_STATE_DIR_BASENAME),this.path=join(this.roveDir,"tasks.json"),this.legacyPath=join(preRenameStateDir(this.homeDir),"tasks.json"),this.lockPath=`${this.path}.lock`}subscribe(listener){if(this.listeners.add(listener),this.loaded)try{listener(this.cache.tasks.slice())}catch(err){console.error("[rove TaskIndexStore] listener threw on subscribe:",err)}return()=>{this.listeners.delete(listener)}}get filePath(){return this.path}get stateDir(){return this.roveDir}async load(){return this.dirtyIds.clear(),this.removedIds.clear(),this.cache=await recoverIndexFromDisk(this.path,this.legacyPath),this.loaded=!0,this.notifyListeners(),this.snapshot()}async save(){this.assertLoaded();let next=this.saveChain.then(()=>this.doSave());return this.saveChain=next.catch(()=>{}),next}async saveOrRollback(id,undo){try{await this.save()}catch(err){if(undo())this.dirtyIds.delete(id),this.notifyListeners();throw err}}async doSave(){await mkdir(dirname(this.path),{recursive:!0});let dirty=new Set(this.dirtyIds),removed=new Map(this.removedIds),lockToken=await acquireWithRetry(this.lockPath),cacheChanged=!1;try{let disk=await readDiskIndex(this.path,this.legacyPath),merged=mergeTasksWithDisk(this.cache.tasks,disk.tasks,dirty,removed,disk.removed),payload={version:CURRENT_VERSION,tasks:merged.tasks,...merged.removed.length>0?{removed:merged.removed}:{}},json=`${JSON.stringify(payload)}
|
|
3
|
+
`,tmpPath=`${this.path}.${process.pid}.${ulid()}.tmp`;try{let handle=await open(tmpPath,"w",384);try{await handle.writeFile(json,"utf8"),await handle.sync()}finally{await handle.close()}await rename(tmpPath,this.path)}catch(err){throw await unlink(tmpPath).catch(()=>{}),err}for(let id of dirty)this.dirtyIds.delete(id);for(let id of removed.keys())this.removedIds.delete(id);let present=new Set(this.cache.tasks.map((t)=>t.id));for(let task of merged.tasks){if(present.has(task.id))continue;this.cache.tasks.push(task),cacheChanged=!0}let tombstoned=new Set(merged.removed.map((t)=>t.id));if(tombstoned.size>0)for(let i=this.cache.tasks.length-1;i>=0;i--){let entry=this.cache.tasks[i];if(!entry||!tombstoned.has(entry.id))continue;this.cache.tasks.splice(i,1),cacheChanged=!0}}finally{await release(this.lockPath,lockToken).catch((err)=>{console.error("[rove TaskIndexStore] index lock release failed:",err)})}if(cacheChanged)this.notifyListeners()}get(id){return this.assertLoaded(),this.cache.tasks.find((t)=>t.id===id)}list(){return this.assertLoaded(),this.cache.tasks.slice()}async create(partial){this.assertLoaded();let now=new Date().toISOString(),task={vendor:partial.vendor??DEFAULT_TASK_VENDOR,...partial,id:toTaskId(ulid()),createdAt:now,updatedAt:now};return this.cache.tasks.push(task),this.dirtyIds.add(task.id),await this.saveOrRollback(task.id,()=>{let at=this.cache.tasks.indexOf(task);if(at<0)return!1;return this.cache.tasks.splice(at,1),!0}),this.notifyListeners(),task}async update(id,patch){this.assertLoaded();let idx=this.cache.tasks.findIndex((t)=>t.id===id);if(idx<0)throw Error(`task not found: ${id}`);let existing=this.cache.tasks[idx];if(!existing)throw Error(`task not found: ${id}`);let{id:_id,createdAt:_createdAt,...mutable}=patch,next={...existing,...mutable,id:existing.id,createdAt:existing.createdAt,updatedAt:new Date().toISOString()};return this.cache.tasks[idx]=next,this.dirtyIds.add(String(id)),await this.saveOrRollback(String(id),()=>{let at=this.cache.tasks.indexOf(next);if(at<0)return!1;return this.cache.tasks[at]=existing,!0}),this.notifyListeners(),next}touchRecency(id){this.assertLoaded();let idx=this.cache.tasks.findIndex((t)=>t.id===id);if(idx<0)return;let existing=this.cache.tasks[idx];if(!existing)return;this.cache.tasks[idx]={...existing,updatedAt:new Date().toISOString()},this.dirtyIds.add(String(id)),this.notifyListeners()}async move(id,delta,withinIds){this.assertLoaded();let task=this.cache.tasks.find((t)=>t.id===id);if(!task)throw Error(`task not found: ${id}`);let ids=withinIds?.length?withinIds:this.cache.tasks.map((t)=>t.id),pos=ids.indexOf(String(id));if(pos<0)throw Error(`task not movable in current group: ${id}`);let targetId=ids[pos+delta];if(!targetId)return task;let fromIdx=this.cache.tasks.findIndex((t)=>t.id===id),toIdx=this.cache.tasks.findIndex((t)=>t.id===targetId);if(fromIdx<0||toIdx<0||fromIdx===toIdx)return task;let[moved]=this.cache.tasks.splice(fromIdx,1);if(!moved)return task;let adjustedToIdx=fromIdx<toIdx?toIdx-1:toIdx,insertAt=delta>0?adjustedToIdx+1:adjustedToIdx,next={...moved,updatedAt:new Date().toISOString()};return this.cache.tasks.splice(insertAt,0,next),this.dirtyIds.add(String(id)),await this.saveOrRollback(String(id),()=>{let at=this.cache.tasks.indexOf(next);if(at<0)return!1;return this.cache.tasks.splice(at,1),this.cache.tasks.splice(Math.min(fromIdx,this.cache.tasks.length),0,moved),!0}),this.notifyListeners(),next}async moveToFront(id,withinIds){this.assertLoaded();let task=this.cache.tasks.find((t)=>t.id===id);if(!task)throw Error(`task not found: ${id}`);if(!withinIds.includes(String(id)))throw Error(`task not movable in current group: ${id}`);if(withinIds[0]===String(id))return task;let fromIdx=this.cache.tasks.indexOf(task),toIdx=this.cache.tasks.findIndex((t)=>t.id===withinIds[0]),next={...task,updatedAt:new Date().toISOString()};return this.cache.tasks.splice(fromIdx,1),this.cache.tasks.splice(toIdx,0,next),this.dirtyIds.add(String(id)),await this.saveOrRollback(String(id),()=>{let at=this.cache.tasks.indexOf(next);if(at<0)return!1;return this.cache.tasks.splice(at,1),this.cache.tasks.splice(Math.min(fromIdx,this.cache.tasks.length),0,task),!0}),this.notifyListeners(),next}async remove(id){this.assertLoaded();let idx=this.cache.tasks.findIndex((t)=>t.id===id),removed=this.cache.tasks[idx];if(idx<0||!removed)return!1;return this.cache.tasks.splice(idx,1),this.dirtyIds.delete(String(id)),this.removedIds.set(String(id),new Date().toISOString()),await this.saveOrRollback(String(id),()=>{if(this.cache.tasks.some((t)=>t.id===removed.id))return!1;return this.removedIds.delete(String(id)),this.cache.tasks.splice(Math.min(idx,this.cache.tasks.length),0,removed),!0}),this.notifyListeners(),!0}async _unlinkForTests(){try{await unlink(this.path)}catch(err){if(err.code!=="ENOENT")throw err}this.cache={version:CURRENT_VERSION,tasks:[]},this.loaded=!1,this.dirtyIds.clear(),this.removedIds.clear()}assertLoaded(){if(!this.loaded)throw Error("TaskIndexStore: call load() before any other method")}snapshot(){return{version:CURRENT_VERSION,tasks:this.cache.tasks.slice()}}notifyListeners(){if(this.listeners.size===0)return;let snapshot=this.cache.tasks.slice();for(let listener of this.listeners)try{listener(snapshot)}catch(err){console.error("[rove TaskIndexStore] listener threw on notify:",err)}}}
|
|
4
|
+
export{TaskIndexStore};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{recordSpawn}from"./chunk-40dgv828.js";import{projectRejection}from"./chunk-
|
|
2
|
+
import{recordSpawn}from"./chunk-40dgv828.js";import{projectRejection}from"./chunk-3342qzve.js";import{getPersistedBool,loadStateFile,patchStateFile,readSavedRepos,updateStateFile}from"./chunk-7q43tysm.js";import{kvStatePath}from"./chunk-prqeddy7.js";import{pathIdentity,samePath}from"./chunk-pn5ygqn6.js";import{spawnSync}from"child_process";import{realpathSync}from"fs";function isRemoteRepoKey(key){return key.startsWith("ssh://")}function isRemoteProjectsEnabled(){return getPersistedBool("experimental.remoteProjects",!1)}function remoteRepoKey(host,user,port,basePath){let authority=port?`${user}@${host}:${port}`:`${user}@${host}`,base=(basePath??"").trim().replace(/\/+$/,"");if(!base)return`ssh://${authority}`;return`ssh://${authority}${base.startsWith("/")?base:`/${base}`}`}function readRemoteRepos(state){let raw=state.remoteRepos;if(!raw||typeof raw!=="object"||Array.isArray(raw))return{};return raw}function getRemoteRepoConfig(key){return readRemoteRepos(loadStateFile())[key]??null}function getRemoteRepos(){return readRemoteRepos(loadStateFile())}function addRemoteRepo(config){let key=remoteRepoKey(config.host,config.user,config.port,config.basePath),added=!1;return updateStateFile((state)=>{let repos={...readRemoteRepos(state)},legacyKey=remoteRepoKey(config.host,config.user,config.port);if(key!==legacyKey&&repos[legacyKey]?.basePath===config.basePath)key=legacyKey;repos[key]=config,state.remoteRepos=repos;let saved=readSavedRepos(state);if(added=!saved.includes(key),added)state.savedRepos=[...saved,key];return}),{key,added}}function resolveRepoRoot(absPath){if(isRemoteRepoKey(absPath))return absPath;recordSpawn("repos.resolveRepoRoot",["git","rev-parse","--show-toplevel"],absPath);let r=spawnSync("git",["rev-parse","--show-toplevel"],{cwd:absPath,encoding:"utf8",shell:!1});if(r.status!==0)return absPath;let top=(r.stdout??"").trim();if(!top)return absPath;try{if(realpathSync(absPath)===realpathSync(top))return absPath}catch{}return top}function isGitRepo(absPath){if(isRemoteRepoKey(absPath))return!1;recordSpawn("repos.isGitRepo",["git","rev-parse","--is-inside-work-tree"],absPath);let r=spawnSync("git",["rev-parse","--is-inside-work-tree"],{cwd:absPath,encoding:"utf8",shell:!1});return r.status===0&&(r.stdout??"").trim()==="true"}function isValidBranchName(branch){return spawnSync("git",["check-ref-format","--branch",branch],{encoding:"utf8",shell:!1}).status===0}function resolveMainRepoRoot(absPath){if(isRemoteRepoKey(absPath))return absPath;recordSpawn("repos.resolveMainRepoRoot",["git","worktree","list","--porcelain"],absPath);let r=spawnSync("git",["worktree","list","--porcelain"],{cwd:absPath,encoding:"utf8",shell:!1});if(r.status!==0)return resolveRepoRoot(absPath);return(r.stdout??"").split(/\r?\n/).find((line)=>line.startsWith("worktree "))?.slice(9).trim()||resolveRepoRoot(absPath)}function statePath(){return kvStatePath()}function getSavedRepos(){return readSavedRepos(loadStateFile())}function getPersistedString(key){let value=loadStateFile()[key];return typeof value==="string"?value:void 0}function getPersistedValue(key){return loadStateFile()[key]}function setPersistedString(key,value){patchStateFile({[key]:value})}function getCustomEngineIds(){let raw=loadStateFile().customEngineIds;if(!Array.isArray(raw))return[];return raw.filter((s)=>typeof s==="string"&&s.trim().length>0)}function getDisabledEngineIds(){let raw=loadStateFile().disabledEngineIds;if(!Array.isArray(raw))return[];return raw.filter((s)=>typeof s==="string"&&s.trim().length>0)}function addSavedRepo(absPath,opts={}){let normalized=resolveMainRepoRoot(absPath),rejected=opts.skipGate?null:projectRejection(normalized,isGitRepo,opts.intent??"explicit");if(rejected)return{added:!1,path:normalized,total:getSavedRepos().length,rejected};let result={added:!1,path:normalized,total:0};return updateStateFile((state)=>{let cur=readSavedRepos(state);if(cur.some((repo)=>samePath(repo,normalized)))return result={added:!1,path:normalized,total:cur.length},!1;state.savedRepos=[...cur,normalized],result={added:!0,path:normalized,total:cur.length+1};return}),result}function backfillSavedReposFromProjects(mainRepos){let added=[];for(let repo of mainRepos)if(addSavedRepo(repo,{intent:"explicit"}).added)added.push(repo);return added}function normalizeSavedRepos(){let cur=getSavedRepos(),seen=new Set,next=[],changed=!1;for(let p of cur){let top=resolveMainRepoRoot(p);if(top!==p)changed=!0;if(seen.has(pathIdentity(top))){changed=!0;continue}seen.add(pathIdentity(top)),next.push(top)}if(!changed)return;patchStateFile({savedRepos:next})}function readRepoConfigs(state){let raw=state.repoConfigs;if(!raw||typeof raw!=="object"||Array.isArray(raw))return{};return raw}function samePathResolved(left,right){if(samePath(left,right))return!0;try{return realpathSync(left)===realpathSync(right)}catch{return!1}}function coerceOverride(entry){if(!entry||typeof entry!=="object")return{};let e=entry;return{initScript:typeof e.initScript==="string"&&e.initScript.length>0?e.initScript:void 0,initPrompt:typeof e.initPrompt==="string"&&e.initPrompt.length>0?e.initPrompt:void 0}}function getRepoInitOverride(repoRoot){let configs=readRepoConfigs(loadStateFile()),keys=Object.keys(configs);if(keys.length===0)return{};let direct=keys.find((key2)=>samePathResolved(key2,repoRoot));if(direct)return coerceOverride(configs[direct]);let normalized=resolveRepoRoot(repoRoot),key=keys.find((key2)=>samePathResolved(key2,normalized))??normalized;return coerceOverride(configs[key])}function setRepoInitOverride(repoRoot,patch){let resolved=resolveRepoRoot(repoRoot),next={};return updateStateFile((state)=>{let configs={...readRepoConfigs(state)},normalized=Object.keys(configs).find((key)=>samePathResolved(key,resolved))??resolved,cur=coerceOverride(configs[normalized]),nextScript=patch.initScript===void 0?cur.initScript:patch.initScript||void 0,nextPrompt=patch.initPrompt===void 0?cur.initPrompt:patch.initPrompt||void 0;if(next={...nextScript?{initScript:nextScript}:{},...nextPrompt?{initPrompt:nextPrompt}:{}},!next.initScript&&!next.initPrompt){let{[normalized]:_dropped,...rest}=configs;state.repoConfigs=rest}else configs[normalized]=next,state.repoConfigs=configs;return}),next}function removeSavedRepo(absPath){let result={removed:!1,path:absPath,total:0};return updateStateFile((state)=>{let cur=readSavedRepos(state);if(!cur.some((repo)=>samePath(repo,absPath)))return result={removed:!1,path:absPath,total:cur.length},!1;let remaining=cur.filter((p)=>!samePath(p,absPath));if(state.savedRepos=remaining,isRemoteRepoKey(absPath)){let remotes=readRemoteRepos(state);if(absPath in remotes){let next={...remotes};delete next[absPath],state.remoteRepos=next}}result={removed:!0,path:absPath,total:remaining.length};return}),result}
|
|
3
3
|
export{isRemoteRepoKey,isRemoteProjectsEnabled,remoteRepoKey,getRemoteRepoConfig,getRemoteRepos,addRemoteRepo,resolveRepoRoot,isGitRepo,isValidBranchName,resolveMainRepoRoot,statePath,getSavedRepos,getPersistedString,getPersistedValue,setPersistedString,getCustomEngineIds,getDisabledEngineIds,addSavedRepo,backfillSavedReposFromProjects,normalizeSavedRepos,getRepoInitOverride,setRepoInitOverride,removeSavedRepo};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{quoteShellArg}from"./chunk-7kysxgbq.js";import{getPersistedString}from"./chunk-
|
|
2
|
+
import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{quoteShellArg}from"./chunk-7kysxgbq.js";import{getPersistedString}from"./chunk-h9cg0j3a.js";import{recordSpawn}from"./chunk-40dgv828.js";import{pathSyntax,pathWithin}from"./chunk-pn5ygqn6.js";var EDITOR_KINDS=["auto","vim","nvim","nano","emacs","custom"],AUTO_EDITOR_CANDIDATES=["nvim","vim","emacs","nano"],EDITOR_KIND_KEY="editor.kind",EDITOR_CUSTOM_KEY="editor.customCommand",DEFAULT_EDITOR_KIND="auto";function normalizeEditorKind(value){return EDITOR_KINDS.includes(value)?value:"auto"}var FILE_PLACEHOLDER="{file}";function firstToken(cmd){return cmd.trim().split(/\s+/)[0]??""}function buildEditorCommand(kind,customCommand,absPath,envEditor){let file=quoteShellArg(absPath);if(kind==="vim")return{bin:"vim",command:`vim ${file}`};if(kind==="nvim")return{bin:"nvim",command:`nvim ${file}`};if(kind==="nano")return{bin:"nano",command:`nano ${file}`};if(kind==="emacs")return{bin:"emacs",command:`emacs ${file}`};let tmpl=(customCommand.trim()||(envEditor??"").trim()).trim();if(!tmpl)return null;let bin=firstToken(tmpl);if(!bin)return null;let command=tmpl.includes(FILE_PLACEHOLDER)?tmpl.split(FILE_PLACEHOLDER).join(file):`${tmpl} ${file}`;return{bin,command}}function buildNvimDiffCommand(bin,absPath,relPath){let file=quoteShellArg(absPath);return["f=$(mktemp 2>/dev/null)",`if [ -n "$f" ] && git show ${quoteShellArg(`HEAD:./${relPath}`)} > "$f" 2>/dev/null; then`,` ${bin} -d "$f" ${file} -c 'setlocal nomodifiable' -c 'wincmd l'; r=$?`,"else",` ${bin} ${file}; r=$?`,"fi",'rm -f "$f" 2>/dev/null; exit $r'].join(`
|
|
3
3
|
`)}function relativeToWorktree(worktree,absPath){return pathWithin(worktree,absPath)||null}async function resolveEditorCommand(absPath){let kind=normalizeEditorKind(getPersistedString(EDITOR_KIND_KEY)),custom=getPersistedString(EDITOR_CUSTOM_KEY)??"",env=(process.env.VISUAL??process.env.EDITOR??"").trim();if(kind!=="auto")return buildEditorCommand(kind,custom,absPath,env);if(env)return buildEditorCommand("custom","",absPath,env);let file=quoteShellArg(absPath);for(let bin of AUTO_EDITOR_CANDIDATES)if(await binaryAvailable(bin))return{bin,command:`${bin} ${file}`};return null}async function binaryAvailable(bin){try{return await Bun.spawn(["sh","-c",`command -v ${quoteShellArg(bin)} >/dev/null 2>&1`],{stdin:"ignore",stdout:"ignore",stderr:"ignore"}).exited===0}catch{return!1}}async function fileHasDiff(worktree,relPath){try{return recordSpawn("tui.fileHasDiff",["git","diff","--quiet","HEAD","--",relPath],worktree),await Bun.spawn(["git","diff","--quiet","HEAD","--",relPath],{cwd:worktree,stdin:"ignore",stdout:"ignore",stderr:"ignore",env:readOnlyGitProcessEnv()}).exited===1}catch{return!1}}async function resolveEditorLaunch(worktree,absPath){let resolved=await resolveEditorCommand(absPath);if(!resolved)return null;if(!await binaryAvailable(resolved.bin))return null;return{command:await maybeDiffCommand(resolved,worktree,absPath),label:editorWindowLabel(absPath)}}async function maybeDiffCommand(resolved,worktree,absPath){let{bin,command}=resolved;if(bin!=="nvim"&&bin!=="vim")return command;if(command!==`${bin} ${quoteShellArg(absPath)}`)return command;let rel=relativeToWorktree(worktree,absPath);if(!rel)return command;if(!await fileHasDiff(worktree,rel))return command;return buildNvimDiffCommand(bin,absPath,rel)}function editorWindowLabel(absPath){let base=pathSyntax(absPath).basename(absPath).trim();return base.length>0?base:"edit"}
|
|
4
4
|
export{EDITOR_KINDS,EDITOR_KIND_KEY,EDITOR_CUSTOM_KEY,DEFAULT_EDITOR_KIND,normalizeEditorKind,resolveEditorCommand,binaryAvailable,resolveEditorLaunch};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{defaultClientLogPath}from"./chunk-npwzmmv9.js";import{existsSync,renameSync,statSync}from"fs";var DEFAULT_LOG_ROTATE_CAP_BYTES=10485760;function shouldRotateLog(sizeBytes,capBytes=DEFAULT_LOG_ROTATE_CAP_BYTES){return sizeBytes>capBytes}function rotateLogIfNeeded(path,capBytes=DEFAULT_LOG_ROTATE_CAP_BYTES){try{if(!existsSync(path))return;let{size}=statSync(path);if(!shouldRotateLog(size,capBytes))return;renameSync(path,`${path}.old`)}catch{}}import{appendFile,mkdir,rename,stat}from"fs/promises";import{dirname}from"path";var context="client";function setClientLogContext(ctx){context=ctx}function coerceError(err){if(err instanceof Error)return{message:err.message,stack:err.stack};let text;try{text=typeof err==="string"?err:JSON.stringify(err)}catch{text=String(err)}return{message:text}}function formatClientEntry(subsystem,message,now=new Date){return`[${now.toISOString()}] client ${context} [${subsystem}] pid=${process.pid}: ${message}
|
|
3
3
|
`}var warnedOnce=!1;function warnOnce(){if(warnedOnce)return;warnedOnce=!0;try{process.stderr.write(`[rove] client log write failed; continuing without it
|
|
4
4
|
`)}catch{}}var writeChain=Promise.resolve();async function rotateClientLogIfNeeded(path){try{let{size}=await stat(path);if(!shouldRotateLog(size,DEFAULT_LOG_ROTATE_CAP_BYTES))return;await rename(path,`${path}.old`)}catch{}}function append(line,logPath){let path=logPath??defaultClientLogPath();writeChain=writeChain.then(async()=>{await rotateClientLogIfNeeded(path);try{await appendFile(path,line)}catch(err){if(err?.code==="ENOENT")await mkdir(dirname(path),{recursive:!0}),await appendFile(path,line);else throw err}}).catch(()=>warnOnce())}function logClient(subsystem,message,logPath){append(formatClientEntry(subsystem,message),logPath)}function logClientError(subsystem,err){let e=coerceError(err);append(formatClientEntry(subsystem,e.stack??e.message))}var onRejection,onException;function installClientCrashHandlers(){if(onRejection||onException)return;onRejection=(reason)=>logClientError("crash-net",reason),onException=(err)=>logClientError("crash-net",err),process.on("unhandledRejection",onRejection),process.on("uncaughtException",onException)}
|
|
5
|
-
export{setClientLogContext,logClient,logClientError,installClientCrashHandlers};
|
|
5
|
+
export{rotateLogIfNeeded,setClientLogContext,logClient,logClientError,installClientCrashHandlers};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{connectIfRunning,connectOrStartDaemon}from"./chunk-
|
|
2
|
+
import{connectIfRunning,connectOrStartDaemon}from"./chunk-96cjpssp.js";async function openDaemonSession(opts={}){let client=opts.mode==="require-running"?await connectIfRunning():await connectOrStartDaemon();if(!client)return null;return{client,close:()=>client.close()}}async function resolveActiveTaskId(client){let activeId=null,off=client.onChannel("active-task",(payload)=>{activeId=payload.taskId});try{await client.subscribe()}finally{off()}return activeId}
|
|
3
3
|
export{openDaemonSession,resolveActiveTaskId};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{LAST_RUN_VERSION_KEY}from"./chunk-801h8bze.js";import{isShellKind,shippedCompletionsPath}from"./chunk-yybpg7fh.js";import{isNpxMissing,markSkillHintSeen,npxSkillsArgv,npxSkillsCommand}from"./chunk-g4c4ffqp.js";import{loadStateFile,patchStateFile}from"./chunk-7q43tysm.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{t}from"./chunk-snhcvjdb.js";import{spawnSync}from"child_process";import{appendFileSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"fs";import{homedir}from"os";import{basename,join}from"path";var WELCOMED_KEY="welcomed",PENDING_SKILL_KEY="welcomePendingSkillInstall";function shouldWelcome(state){if(state[WELCOMED_KEY]===!0)return!1;return typeof state[LAST_RUN_VERSION_KEY]!=="string"}function takeWelcome(){if(!shouldWelcome(loadStateFile()))return null;try{patchStateFile({[WELCOMED_KEY]:!0})}catch{}return{shell:detectShell()}}var PENDING_COMPLETIONS_KEY="welcomePendingCompletions";function detectShell(env=process.env){let shell=basename(env.SHELL??"");return shell==="zsh"||shell==="bash"||shell==="fish"?shell:null}function completionHook(shell,cli,shipped){if(shipped===null)return legacyCompletionHook(shell,cli);return shell==="fish"?`test -f "${shipped}"; and source "${shipped}"`:`[ -f "${shipped}" ] && source "${shipped}"`}function legacyCompletionHook(shell,cli){return shell==="fish"?`${cli} completions fish | source`:`command -v ${cli} >/dev/null && source <(${cli} completions ${shell})`}function shippedScriptFor(shell,cli){let path=shippedCompletionsPath(shell,cli);return existsSync(path)?path:null}function installCompletions(shell,home=homedir(),cli=ROVE_PRODUCT_NAME,shipped=shippedScriptFor(shell,cli)){let hook=completionHook(shell,cli,shipped);if(shell==="fish"){let dir=join(home,".config","fish","completions"),path=join(dir,`${cli}.fish`);return mkdirSync(dir,{recursive:!0}),writeFileSync(path,`${hook}
|
|
3
|
+
`),{path,installed:!0}}let rc=join(home,shell==="zsh"?".zshrc":".bashrc"),existing=existsSync(rc)?readFileSync(rc,"utf8"):"";if(existing.includes(hook))return{path:rc,installed:!1};let legacy=legacyCompletionHook(shell,cli);if(existing.includes(legacy))return writeFileSync(rc,existing.replace(legacy,hook)),{path:rc,installed:!0};if(existing.includes(`${cli} completions`))return{path:rc,installed:!1};return appendFileSync(rc,`
|
|
4
|
+
# ${cli} completions
|
|
5
|
+
${hook}
|
|
6
|
+
`),{path:rc,installed:!0}}function recordWelcomeChoices(choices,shell){if(!choices.skill)markSkillHintSeen();try{patchStateFile({[PENDING_COMPLETIONS_KEY]:choices.completions&&shell!==null?shell:void 0,[PENDING_SKILL_KEY]:choices.skill?!0:void 0,"welcomeInstall.completions":choices.completions?{status:"queued"}:void 0,"welcomeInstall.skill":choices.skill?{status:"queued"}:void 0})}catch{}}function runPendingWelcomeInstalls(){let state=loadStateFile(),shell=state[PENDING_COMPLETIONS_KEY],wantsSkill=state[PENDING_SKILL_KEY]===!0,pendingShell=typeof shell==="string"&&isShellKind(shell)?shell:null;if(shell!==void 0&&pendingShell===null){process.stdout.write(`! Ignoring invalid pending completion shell; run rove completions --help to choose a supported shell.
|
|
7
|
+
`);try{patchStateFile({[PENDING_COMPLETIONS_KEY]:void 0,"welcomeInstall.completions":{status:"failed",reason:"Unsupported shell"}})}catch{}}if(pendingShell===null&&!wantsSkill)return;let cli=ROVE_PRODUCT_NAME,out=(line)=>process.stdout.write(`${line}
|
|
8
|
+
`),save=(patch)=>{try{patchStateFile(patch)}catch{out("! Could not save setup results; check permissions before retrying.")}},failed=(item,reason,command)=>{save({[`welcomeInstall.${item}`]:{status:"failed",reason}}),out(t("onboarding.installFailed",{item,reason,command}))};if(pendingShell!==null)try{let completion=installCompletions(pendingShell);save({[PENDING_COMPLETIONS_KEY]:void 0,"welcomeInstall.completions":{status:"succeeded"}}),out(t(completion.installed?"onboarding.appliedCompletions":"onboarding.keptCompletions",{path:completion.path}))}catch(error){failed("completions",error instanceof Error?error.message:String(error),`${cli} completions ${pendingShell} --install`)}if(wantsSkill){let command=`${cli} skill install`;if(isNpxMissing())failed("skill","npx is missing (install Node.js from https://nodejs.org)",command);else{out(t("onboarding.installingSkill",{command:npxSkillsCommand()}));try{let result=spawnSync("npx",npxSkillsArgv(),{stdio:"inherit"});if(result.status!==0)failed("skill",result.error?.message??(result.signal?`signal ${result.signal}`:`exit ${result.status}`),command);else save({[PENDING_SKILL_KEY]:void 0,"welcomeInstall.skill":{status:"succeeded"}}),out(t("onboarding.skillSucceeded"))}catch(error){failed("skill",error instanceof Error?error.message:String(error),command)}}}}function settleWelcomeInstall(item,shell){let state=loadStateFile(),key=item==="skill"?PENDING_SKILL_KEY:PENDING_COMPLETIONS_KEY;if(item==="skill"?state[key]!==!0:state[key]!==shell)return;try{patchStateFile({[key]:void 0,[`welcomeInstall.${item}`]:{status:"succeeded"}})}catch{process.stderr.write(`! Setup succeeded, but its queued state could not be saved. Check settings permissions.
|
|
9
|
+
`)}}
|
|
10
|
+
export{takeWelcome,detectShell,installCompletions,recordWelcomeChoices,runPendingWelcomeInstalls,settleWelcomeInstall};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{parsePorcelain,pickPushedChanges,readWorktreeChanges,sameWorktreeChanges}from"./chunk-
|
|
2
|
+
import{parsePorcelain,pickPushedChanges,readWorktreeChanges,sameWorktreeChanges}from"./chunk-dgs0m8r4.js";import"./chunk-dmzg9zdc.js";import"./chunk-6xppe9bj.js";import"./chunk-40dgv828.js";import"./chunk-wqfp8d9w.js";export{sameWorktreeChanges,readWorktreeChanges,pickPushedChanges,parsePorcelain};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{readFileSync,statSync}from"fs";import{isAbsolute,join,resolve}from"path";var DEFAULT_BASE_REF_CANDIDATES=["origin/main","origin/master","main","master"];function mtime(path){let st=statSync(path,{throwIfNoEntry:!1});return st?st.mtimeMs:null}function readText(path){try{return readFileSync(path,"utf8")}catch{return null}}function resolveGitDirs(worktreePath){let dotGit=join(worktreePath,".git"),st=statSync(dotGit,{throwIfNoEntry:!1});if(!st)return null;let gitDir=dotGit;if(!st.isDirectory()){let pointer=readText(dotGit)?.match(/^gitdir:\s*(.+)$/m)?.[1]?.trim();if(!pointer)return null;gitDir=isAbsolute(pointer)?pointer:resolve(worktreePath,pointer)}let common=readText(join(gitDir,"commondir"))?.trim(),commonDir=common?isAbsolute(common)?common:resolve(gitDir,common):gitDir;return{gitDir,commonDir}}function
|
|
2
|
+
import{readFileSync,statSync}from"fs";import{isAbsolute,join,resolve}from"path";var DEFAULT_BASE_REF_CANDIDATES=["origin/main","origin/master","main","master"];function mtime(path){let st=statSync(path,{throwIfNoEntry:!1});return st?st.mtimeMs:null}function readText(path){try{return readFileSync(path,"utf8")}catch{return null}}function resolveGitDirs(worktreePath){let dotGit=join(worktreePath,".git"),st=statSync(dotGit,{throwIfNoEntry:!1});if(!st)return null;let gitDir=dotGit;if(!st.isDirectory()){let pointer=readText(dotGit)?.match(/^gitdir:\s*(.+)$/m)?.[1]?.trim();if(!pointer)return null;gitDir=isAbsolute(pointer)?pointer:resolve(worktreePath,pointer)}let common=readText(join(gitDir,"commondir"))?.trim(),commonDir=common?isAbsolute(common)?common:resolve(gitDir,common):gitDir;return{gitDir,commonDir}}function refNameCandidates(ref){if(ref.startsWith("refs/"))return[ref];return[`refs/remotes/${ref}`,`refs/heads/${ref}`]}function looseRefPaths(dirs,ref){let out=[];for(let name of refNameCandidates(ref))if(out.push(join(dirs.gitDir,name)),dirs.commonDir!==dirs.gitDir)out.push(join(dirs.commonDir,name));return out}function readRefSha(dirs,ref){for(let path of looseRefPaths(dirs,ref)){let raw=readText(path)?.trim();if(raw&&/^[0-9a-f]{40,64}$/.test(raw))return raw}let packed=readText(join(dirs.commonDir,"packed-refs"));if(!packed)return null;let wanted=new Set(refNameCandidates(ref));for(let line of packed.split(`
|
|
3
3
|
`)){if(!line||line.startsWith("#")||line.startsWith("^"))continue;let[sha,name]=line.split(" ",2);if(sha&&name&&wanted.has(name.trim()))return sha}return null}function readHeadSha(dirs){let head=readText(join(dirs.gitDir,"HEAD"))?.trim();if(!head)return null;if(/^[0-9a-f]{40,64}$/.test(head))return head;let ref=head.match(/^ref:\s*(.+)$/)?.[1]?.trim();return ref?readRefSha(dirs,ref):null}function worktreeFingerprint(worktreePath,baseRef){try{let dirs=resolveGitDirs(worktreePath);if(!dirs)return null;let head=readText(join(dirs.gitDir,"HEAD"))?.trim();if(!head)return null;let parts=[head,mtime(worktreePath),mtime(join(dirs.gitDir,"index")),mtime(join(dirs.gitDir,"HEAD")),mtime(join(dirs.gitDir,"FETCH_HEAD")),mtime(join(dirs.commonDir,"FETCH_HEAD")),mtime(join(dirs.commonDir,"packed-refs"))],headRef=head.match(/^ref:\s*(.+)$/)?.[1]?.trim();if(headRef)for(let path of looseRefPaths(dirs,headRef))parts.push(mtime(path));parts.push(mtime(join(dirs.commonDir,"refs/remotes/origin/HEAD")));for(let ref of baseRef?[baseRef,...DEFAULT_BASE_REF_CANDIDATES]:DEFAULT_BASE_REF_CANDIDATES)for(let path of looseRefPaths(dirs,ref))parts.push(mtime(path));return parts.join("|")}catch{return null}}
|
|
4
4
|
export{DEFAULT_BASE_REF_CANDIDATES,resolveGitDirs,readRefSha,readHeadSha,worktreeFingerprint};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{readLastActiveTaskId,writeLastActiveTaskId}from"./chunk-n5dea05t.js";import"./chunk-7q43tysm.js";import"./chunk-mw0ysrpd.js";import"./chunk-a7kq09ke.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-npwzmmv9.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-wqfp8d9w.js";export{writeLastActiveTaskId,readLastActiveTaskId};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{generateCompletions,isShellKind,mergeSubVerbs,shippedCompletionsPath}from"./chunk-yybpg7fh.js";import"./chunk-fhch0x0q.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-wqfp8d9w.js";import{existsSync,readFileSync}from"fs";var HELP_WORDS=["--help","-h","help"];function completionUsage(cliName){return[`Usage: ${cliName} completions <bash|zsh|fish> [--path|--install]`,"",`Print a shell completion script for ${cliName}, or locate the copy shipped`,"with this install.","",` ${cliName} completions zsh --path print the shipped script's path`,` ${cliName} completions zsh --install hook it into your shell config`,"","Install:",` zsh source <(${cliName} completions zsh) # one-off, or in ~/.zshrc after compinit`," # with no process start per shell \u2014 what the first-run wizard writes:",` # ${cliName} completions zsh --install`,` bash ${cliName} completions bash --install # appends to ~/.bashrc`,` fish ${cliName} completions fish --install # writes ~/.config/fish/completions/${cliName}.fish`," # the manual way, any shell:",` # ${cliName} completions zsh > ~/.zsh/completions/_${cliName}`," # fpath=(~/.zsh/completions $fpath) # in ~/.zshrc, BEFORE compinit"," # rm -f ~/.zcompdump && exec zsh # rebuild the completion cache",""].join(`
|
|
3
|
+
`)}async function runCompletionsSubcommand(rest,cliName=ROVE_PRODUCT_NAME,deps={}){let usage=completionUsage(cliName),fail=(message)=>{process.stderr.write(`${cliName} completions: ${message}
|
|
4
|
+
|
|
5
|
+
${usage}`),process.exit(2)};if(rest.some((arg)=>HELP_WORDS.includes(arg))){process.stdout.write(usage);return}let flags=rest.filter((arg)=>arg.startsWith("-")),shells=rest.filter((arg)=>!arg.startsWith("-")),unknownFlag=flags.find((flag)=>flag!=="--path"&&flag!=="--install");if(unknownFlag)fail(`unknown option "${unknownFlag}"`);if(new Set(flags).size>1)fail("--path and --install are different things; pass one");let shell=shells.length===1?shells[0]:void 0;if(!isShellKind(shell))fail(`unknown shell "${shells.join(" ")}"`);let scriptPath=shippedCompletionsPath(shell,cliName,deps.shippedDir),shipped=existsSync(scriptPath)?scriptPath:null;if(flags[0]==="--path"){if(!shipped)fail(`no pre-generated ${shell} script at ${scriptPath}
|
|
6
|
+
${cliName} completions ${shell} # print the script to stdout instead`);process.stdout.write(`${shipped}
|
|
7
|
+
`);return}if(flags[0]==="--install"){let[{installCompletions,settleWelcomeInstall},{t}]=await Promise.all([import("./chunk-f1nf99hj.js"),import("./chunk-28zg6p31.js")]),completion=installCompletions(shell,deps.home,cliName,shipped);settleWelcomeInstall("completions",shell);let line=completion.installed?"onboarding.appliedCompletions":"onboarding.keptCompletions";process.stdout.write(`${t(line,{path:completion.path})}
|
|
8
|
+
`);return}if(shipped){process.stdout.write(readFileSync(shipped,"utf8"));return}let{API_VERBS}=await import("./chunk-abyk3w2j.js");process.stdout.write(generateCompletions(shell,cliName,mergeSubVerbs(API_VERBS)))}export{runCompletionsSubcommand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{RemoteOrchestrator}from"./chunk-80ydqr3s.js";import{discoverMachine}from"./chunk-wtb8cycx.js";import"./chunk-atn06ncf.js";import"./chunk-dgs0m8r4.js";import"./chunk-dmzg9zdc.js";import"./chunk-6xppe9bj.js";import{startTunnel}from"./chunk-9jsm6h5y.js";import{dedupeMachines,duplicateAliasOf,listMachines,readMachines,setMachineIdentity,updateMachine}from"./chunk-9aydqazr.js";import"./chunk-96cjpssp.js";import"./chunk-9w5g7frx.js";import"./chunk-gvrtrvnz.js";import"./chunk-xy8be792.js";import"./chunk-a0ave3g6.js";import"./chunk-40dgv828.js";import{loadStateFile}from"./chunk-7q43tysm.js";import"./chunk-mw0ysrpd.js";import"./chunk-a7kq09ke.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import{createStateCell}from"./chunk-nhakrm1f.js";import{RoveDaemonClient}from"./chunk-adkva3ep.js";import"./chunk-0422g8ac.js";import{logClient,logClientError}from"./chunk-hq9tbzqa.js";import"./chunk-pn5ygqn6.js";import"./chunk-npwzmmv9.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-wqfp8d9w.js";class MachineHub{local;slots=new Map;machinesAcc=createStateCell([],"machines.status");mergedAcc=createStateCell([],"machines.tasks");localUnsubscribe=null;disposed=!1;constructor(local){this.local=local}machinesSignal(){return this.machinesAcc}tasksSignal(){if(this.slots.size===0)return this.local.tasksSignal();return this.mergedAcc}remoteTaskIds(){let ids=new Set;for(let slot of this.slots.values())for(let task of slot.tasks)ids.add(String(task.id));return ids}attach(){let entries=dedupeMachines(listMachines());if(entries.length===0)return;for(let entry of entries)this.slots.set(entry.alias,{entry,tasks:[]});this.publishStatuses(),this.localUnsubscribe=this.local.tasksSignal().subscribe(()=>this.republishTasks()),this.republishTasks();for(let entry of entries)this.connect(entry)}async connect(entry){let slot=this.slots.get(entry.alias);if(!slot||this.disposed)return;let found=await discoverMachine(entry.alias,entry);if(this.disposed)return;let sockets=found.ok?{daemon:found.status.socketPath,pty:found.status.ptySocketPath}:entry.sockets;if(!sockets){this.setStatus(entry.alias,{state:"offline",error:found.ok?void 0:found.message});return}if(found.ok)updateMachine(entry.alias,{sockets});let tunnel=startTunnel({alias:entry.alias,config:entry,remoteDaemonSocket:sockets.daemon,remotePtySocket:sockets.pty});if(slot.tunnel=tunnel,tunnel.state()==="unsupported"){this.setStatus(entry.alias,{state:"unsupported",error:"SSH unix-socket forwarding is not available on Windows"});return}if(tunnel.onState((state)=>{if(state==="online")this.openOrchestrator(entry,tunnel);else if(state==="offline")this.setStatus(entry.alias,{state:"offline"}),this.republishTasks()}),tunnel.state()==="online")await this.openOrchestrator(entry,tunnel)}async openOrchestrator(entry,tunnel){let slot=this.slots.get(entry.alias);if(!slot||this.disposed||slot.orchestrator)return;let client=new RoveDaemonClient(tunnel.daemonSocketPath),orchestrator=new RemoteOrchestrator(client,{role:"gui",expectForeignHome:!0,onPeerIdentity:(peer)=>this.onPeerIdentity(entry.alias,peer)});slot.orchestrator=orchestrator;try{await orchestrator.init()}catch(err){let message=err instanceof Error?err.message:String(err);this.setStatus(entry.alias,{state:classifyHandshakeFailure(message),error:message}),logClientError("machines",err);return}let stopTasks=orchestrator.tasksSignal().subscribe(()=>{slot.tasks=orchestrator.tasksSignal().get(),this.republishTasks()}),stopConnection=orchestrator.connectionStateSignal().subscribe(()=>{let connected=orchestrator.connectionStateSignal().get()==="online";if(this.slots.get(entry.alias)!==slot)return;this.setStatus(entry.alias,{state:connected?"online":"offline"}),this.republishTasks()});slot.unsubscribe=()=>{stopTasks(),stopConnection()},slot.tasks=orchestrator.tasksSignal().get(),this.setStatus(entry.alias,{state:"online",version:orchestrator.daemonVersionSignal().get()??void 0}),this.republishTasks(),logClient("machines",`${entry.alias} online (${slot.tasks.length} tasks)`)}onPeerIdentity(alias,peer){if(!peer.hostname&&!peer.homeDir)return;let identity={hostname:peer.hostname,homeDir:peer.homeDir,daemonPid:peer.daemonPid};setMachineIdentity(alias,identity);let duplicate=duplicateAliasOf(readMachines(loadStateFile()),alias,identity);this.setStatus(alias,{hostname:peer.hostname||void 0,version:peer.roveVersion||void 0,...duplicate?{duplicateOf:duplicate}:{}})}setStatus(alias,patch){let next=this.machinesAcc.get().map((row)=>row.alias===alias?{...row,...patch}:row);this.machinesAcc.set(next)}publishStatuses(){this.machinesAcc.set([...this.slots.values()].map((slot)=>({alias:slot.entry.alias,hostLabel:slot.entry.alias,state:"connecting"})))}republishTasks(){let merged=this.local.tasksSignal().get().map((task)=>stampOrigin(task,"local","local"));for(let slot of this.slots.values()){let status=this.machinesAcc.get().find((row)=>row.alias===slot.entry.alias);if(status?.duplicateOf)continue;let hostLabel=status?.hostLabel||slot.entry.alias,stale=status?.state!=="online";for(let task of slot.tasks)merged.push(stampOrigin(task,slot.entry.alias,hostLabel,stale))}this.mergedAcc.set(merged)}dispose(){this.disposed=!0,this.localUnsubscribe?.();for(let slot of this.slots.values())slot.unsubscribe?.(),slot.orchestrator?.dispose(),slot.tunnel?.stop();this.slots.clear()}}function classifyHandshakeFailure(message){return/protocol v\d/i.test(message)?"mismatch":"offline"}function stampOrigin(task,machineId,hostLabel,stale=!1){return{...task,origin:{machineId,hostLabel,...stale?{stale:!0}:{}}}}export{classifyHandshakeFailure,MachineHub};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{ensureGlobalRoveHooks}from"./chunk-h33m6cc6.js";import{publishRoveTerminalTitle}from"./chunk-3aqhytyy.js";import"./chunk-17e2tgyg.js";import"./chunk-2bgmby1d.js";import"./chunk-rqyaf0dw.js";import"./chunk-y8tysjc2.js";import"./chunk-576r5nht.js";import"./chunk-w7zv47mp.js";import"./chunk-2tfnscxk.js";import"./chunk-zsfyj36g.js";import"./chunk-96cjpssp.js";import"./chunk-9w5g7frx.js";import"./chunk-gvrtrvnz.js";import"./chunk-2brn8e1y.js";import"./chunk-yexx8nec.js";import"./chunk-znjcesdr.js";import"./chunk-7kysxgbq.js";import"./chunk-qzsahxd9.js";import"./chunk-xy8be792.js";import"./chunk-h2m7em1e.js";import"./chunk-tdhk4dz8.js";import{takeWelcome}from"./chunk-jxqs4y9j.js";import{LAST_RUN_VERSION_KEY,enforceResetGate}from"./chunk-801h8bze.js";import"./chunk-yybpg7fh.js";import"./chunk-fhch0x0q.js";import{CURRENT_VERSION,compareSemver}from"./chunk-a0ave3g6.js";import{maybeHintSkillInstall}from"./chunk-g4c4ffqp.js";import"./chunk-h9cg0j3a.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import{loadStateFile,patchStateFile}from"./chunk-7q43tysm.js";import"./chunk-mw0ysrpd.js";import"./chunk-a7kq09ke.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-snhcvjdb.js";import"./chunk-zt49bbg9.js";import"./chunk-nhakrm1f.js";import"./chunk-adkva3ep.js";import"./chunk-0422g8ac.js";import"./chunk-hq9tbzqa.js";import"./chunk-pn5ygqn6.js";import"./chunk-npwzmmv9.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-wqfp8d9w.js";var WHATS_NEW_SEEN_KEY="app.whatsNewSeenVersion";function whatsNewFromVersion(state,current=CURRENT_VERSION){let stamped=state[WHATS_NEW_SEEN_KEY]??state[LAST_RUN_VERSION_KEY];if(typeof stamped!=="string"||stamped.length===0)return null;return compareSemver(current,stamped)>0?stamped:null}function takeWhatsNew(){let from=whatsNewFromVersion(loadStateFile());try{patchStateFile({[WHATS_NEW_SEEN_KEY]:CURRENT_VERSION})}catch{}return from}async function startTui(){let whatsNewFrom=takeWhatsNew(),welcome=takeWelcome();enforceResetGate(),publishRoveTerminalTitle(),await maybeHintSkillInstall(),await ensureGlobalRoveHooks();let{loadPluginEngines}=await import("./chunk-ff1h2qxd.js");loadPluginEngines();let{startWorkspaceHost}=await import("./chunk-q7zzycy7.js");await startWorkspaceHost({whatsNewFrom,welcome})}export{startTui};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{DIRTY_WORKTREE_CODE,canonicalize,describeDirtyWorktreeWork,isRoveManagedPath,isUnderManagedWorktreesRoot,managedWorktreeRootForPath,remoteManagedRootForPath,remoteWorktreePathFor,requireAbsolute,worktreePathFor}from"./chunk-qvfdqrbf.js";import{execHostForRepo,execHostForWorktreePath}from"./chunk-cbjh43yc.js";import{READ_ONLY_GIT_ENV}from"./chunk-6xppe9bj.js";import{getRemoteRepoConfig}from"./chunk-h9cg0j3a.js";import path3 from"path";function parseDirtyPaths(stdout){return stdout.split(`
|
|
3
|
+
`).filter((line)=>line.length>3).map((line)=>line.slice(3).trim()).filter((line)=>line.length>0)}function isDirtyOutput(stdout){return parseDirtyPaths(stdout).length>0}var defaultExecDeps={execForRepo:execHostForRepo,execForPath:execHostForWorktreePath,remoteBasePath(repoKey){return getRemoteRepoConfig(repoKey)?.basePath??null}};class GitCommandError extends Error{args;cwd;exitCode;stdout;stderr;constructor(args,cwd,result){super(`git ${args.join(" ")} (cwd=${cwd}) exited with code ${result.exitCode}: ${result.stderr.trim()||result.stdout.trim()}`);this.name="GitCommandError",this.args=args,this.cwd=cwd,this.exitCode=result.exitCode,this.stdout=result.stdout,this.stderr=result.stderr}}var MAX_IGNORED_ENTRY_KB=65536;function parseIgnoredPaths(stdoutZ){return stdoutZ.split("\x00").filter((entry)=>entry.startsWith("!! ")).map((entry)=>entry.slice(3)).filter((p)=>p.length>0)}var DU_ARGV_BUDGET_BYTES=98304;function parseDuKb(stdout){let sizes=new Map;for(let line of stdout.split(`
|
|
4
|
+
`)){let m=/^(\d+)\s+(.+)$/.exec(line.trim());if(m?.[1]&&m[2])sizes.set(m[2],Number.parseInt(m[1],10))}return sizes}async function duKb(exec,worktreePath,paths){let sizes=new Map,batch=[],bytes=0,flush=async()=>{if(batch.length===0)return;let du=await exec.run(["du","-sk","--",...batch],{cwd:worktreePath});for(let[p,kb]of parseDuKb(du.stdout))sizes.set(p,kb);batch.length=0,bytes=0};for(let p of paths){if(p.includes(`
|
|
5
|
+
`)){let one=await exec.run(["du","-sk","--",p],{cwd:worktreePath}),kb=/^\s*(\d+)/.exec(one.stdout)?.[1];if(one.exitCode===0&&kb)sizes.set(p,Number.parseInt(kb,10));continue}if(bytes+p.length+1>DU_ARGV_BUDGET_BYTES)await flush();batch.push(p),bytes+=p.length+1}return await flush(),sizes}async function smallIgnoredPaths(exec,worktreePath){try{let status=await exec.run(["git","status","--porcelain","-z","--ignored"],{cwd:worktreePath,env:READ_ONLY_GIT_ENV});if(status.exitCode!==0)return"unknown";let paths=parseIgnoredPaths(status.stdout);if(paths.length===0)return[];let sizes=await duKb(exec,worktreePath,paths);return paths.filter((p)=>{let kb=sizes.get(p);return kb!==void 0&&kb<=MAX_IGNORED_ENTRY_KB})}catch{return"unknown"}}async function gitlinkPaths(git,tree){let out=await git(["ls-tree","-r",tree]);if(out.exitCode!==0)return[];return out.stdout.split(`
|
|
6
|
+
`).filter((line)=>line.startsWith("160000 ")).map((line)=>line.slice(line.indexOf("\t")+1)).filter((p)=>p.length>0)}function branchRefSlug(branch){return(branch??"").replace(/[\u0000-\u0020\u007f~^:?*[\\/]+/g,"-").replace(/@\{/g,"-").replace(/\.{2,}/g,"-").replace(/^[-.]+|[-.]+$/g,"")||"detached"}function salvageRef(branch,now){let stamp=now.toISOString().replace(/[-:]/g,"").replace(/\.\d+Z$/,"Z");return`refs/rove/salvage/${branchRefSlug(branch)}-${stamp}`}async function createSalvageRef(git,preferred,commit){for(let attempt=1;attempt<=50;attempt++){let ref=attempt===1?preferred:`${preferred}-${attempt}`;if((await git(["update-ref",ref,commit,""])).exitCode===0)return ref}return null}async function salvageWorktree(deps,exec,worktreePath,now=new Date){let git=(args)=>deps.runGit(exec,args,{cwd:worktreePath,allowFail:!0});try{let status=await git(["status","--porcelain"]);if(status.exitCode!==0)return null;let probe=await smallIgnoredPaths(exec,worktreePath),ignored=probe==="unknown"?[]:probe;if(status.stdout.trim().length===0&&ignored.length===0)return null;let indexPath=(await git(["rev-parse","--git-path","rove-salvage-index"])).stdout.trim();if(!indexPath)return null;let withIndex=(args)=>deps.runGit(exec,args,{cwd:worktreePath,allowFail:!0,env:{GIT_INDEX_FILE:indexPath}}),head=(await git(["rev-parse","HEAD"])).stdout.trim(),branch=(await git(["rev-parse","--abbrev-ref","HEAD"])).stdout.trim();try{if(head&&(await withIndex(["read-tree",head])).exitCode!==0)return null;if((await withIndex(["add","-A"])).exitCode!==0)return null;if(ignored.length>0)await withIndex(["add","-f","--",...ignored]);let tree=(await withIndex(["write-tree"])).stdout.trim();if(!tree)return null;let message=`rove salvage: force-removed worktree ${worktreePath}`,commit=(await withIndex(head?["commit-tree",tree,"-p",head,"-m",message]:["commit-tree",tree,"-m",message])).stdout.trim();if(!commit)return null;let ref=await createSalvageRef(git,salvageRef(branch&&branch!=="HEAD"?branch:null,now),commit);if(!ref)return null;return{ref,commit,uncaptured:await gitlinkPaths(git,tree)}}finally{await exec.run(["rm","-f",indexPath],{cwd:worktreePath}).catch(()=>{return})}}catch{return null}}async function anchorBranchTip(deps,exec,repo,branch,now=new Date){if(!branch||branch==="HEAD")return null;let git=(args)=>deps.runGit(exec,args,{cwd:repo,allowFail:!0});try{let tipOut=await git(["rev-parse","--verify",`refs/heads/${branch}`]),tip=tipOut.stdout.trim();if(tipOut.exitCode!==0||!tip)return null;let holders=await git(["for-each-ref","--contains",tip,"--format=%(refname)"]);if(holders.exitCode===0){if(holders.stdout.split(`
|
|
7
|
+
`).map((l)=>l.trim()).filter((l)=>l.length>0&&l!==`refs/heads/${branch}`).length>0)return null}let ref=salvageRef(branch,now);if((await git(["update-ref",ref,tip])).exitCode!==0)return null;return{ref,commit:tip,uncaptured:[]}}catch{return null}}async function branchExists(deps,ctx,branch){return(await deps.runGit(ctx.exec,["show-ref","--verify","--quiet",`refs/heads/${branch}`],{cwd:ctx.dir,allowFail:!0,readOnly:!0})).exitCode===0}async function deleteBranchIn(deps,exec,repo,branch,force){if(!branch||branch==="HEAD")return{deleted:!1,reason:"no branch to delete"};let out=await deps.runGit(exec,["branch",force?"-D":"-d",branch],{cwd:repo,allowFail:!0});if(out.exitCode===0)return{deleted:!0};if(!await branchExists(deps,{exec,dir:repo,remote:exec.isRemote},branch))return{deleted:!0};return{deleted:!1,reason:out.stderr.trim()||out.stdout.trim()||`git branch exited ${out.exitCode}`}}async function deleteBranchAnchored(deps,exec,repo,branch,opts){if(opts.force)opts.onAnchor?.(await anchorBranchTip(deps,exec,repo,branch));return await deleteBranchIn(deps,exec,repo,branch,opts.force)}async function branchHasUpstream(deps,worktreePath,branch){return(await deps.runGit(deps.execAt(worktreePath),["for-each-ref","--format=%(upstream)",`refs/heads/${branch}`],{cwd:worktreePath,readOnly:!0})).stdout.trim().length>0}async function hasLocalBranch(deps,worktreePath,branch){return(await deps.runGit(deps.execAt(worktreePath),["show-ref","--verify","--quiet",`refs/heads/${branch}`],{cwd:worktreePath,allowFail:!0,readOnly:!0})).exitCode===0}async function renameBranch(deps,worktreePath,from,to){let exec=deps.execAt(worktreePath);if(from===to)return;let repo=await deps.findRepoFor(exec,worktreePath);if(!repo)throw Error(`renameBranch(): ${worktreePath} is not a git worktree`);let out=await deps.runGit(exec,["branch","-m",from,to],{cwd:repo,allowFail:!0});if(out.exitCode===0)return;let ctx={exec,dir:repo,remote:exec.isRemote};if(!await branchExists(deps,ctx,from)&&await branchExists(deps,ctx,to))return;throw new GitCommandError(["branch","-m",from,to],repo,out)}import fs from"fs";import path from"path";async function mapWithLimit(items,limit,fn){let results=Array(items.length),next=0,worker=async()=>{for(;;){let i=next++;if(i>=items.length)return;results[i]=await fn(items[i])}},width=Math.max(1,Math.min(limit,items.length));return await Promise.all(Array.from({length:width},worker)),results}var PROBE_CONCURRENCY=6;function parseWorktreeListPorcelain(out){let records=[],current=null;for(let rawLine of out.split(`
|
|
8
|
+
`)){let line=rawLine.replace(/\r$/,"");if(line===""){if(current)records.push(current);current=null;continue}if(!current)current={};if(line.startsWith("worktree "))current.path=line.slice(9);else if(line.startsWith("HEAD "))current.head=line.slice(5);else if(line.startsWith("branch ")){let ref=line.slice(7);current.branch=ref.startsWith("refs/heads/")?ref.slice(11):ref}else if(line==="detached")current.detached=!0;else if(line==="bare")current.bare=!0}if(current)records.push(current);return records}async function lastActivityMs(deps,ctx,worktreePath){try{let stdout=await deps.runGitStdoutAt(ctx,worktreePath,["log","-1","--format=%ct"]),secs=Number.parseInt(stdout.trim(),10);if(Number.isFinite(secs)&&secs>0)return secs*1000}catch{}if(!ctx.exec.isRemote)try{return fs.statSync(worktreePath).mtimeMs}catch{}return 0}async function listBranchNames(deps,repo){let ctx=deps.ctxFor(repo);requireAbsolute("repo",ctx.dir);let stdout;try{stdout=await deps.runGitStdout(ctx,["for-each-ref","--format=%(refname)","refs/heads","refs/remotes/origin"])}catch{return[]}let names=new Set;for(let line of stdout.split(`
|
|
9
|
+
`)){let name=line.trim().replace(/^refs\/(?:heads|remotes\/origin)\//,"");if(name&&name!=="HEAD")names.add(name)}return[...names]}async function listManaged(deps,repo){let ctx=deps.ctxFor(repo);requireAbsolute("repo",ctx.dir);let all=parseWorktreeListPorcelain(await deps.runGitStdout(ctx,["worktree","list","--porcelain"])),kept=[];for(let entry of all){if(!entry.path)continue;let callerRoot=ctx.remote?remoteManagedRootForPath(ctx.dir,entry.path):managedWorktreeRootForPath(repo,entry.path);if(!callerRoot)continue;if(!entry.branch||entry.detached)continue;let rel=path.relative(canonicalize(callerRoot),canonicalize(entry.path));kept.push({callerPath:path.join(callerRoot,rel),probePath:entry.path,branch:entry.branch,head:entry.head??""})}return mapWithLimit(kept,PROBE_CONCURRENCY,async(e)=>({path:e.callerPath,branch:e.branch,head:e.head,dirty:await deps.isDirty(e.probePath).catch(()=>null)}))}async function listAllAdoptable(deps,repo){let ctx=deps.ctxFor(repo),adoptable=await adoptablePaths(deps,ctx),infos=await mapWithLimit(adoptable,PROBE_CONCURRENCY,async(entry)=>{let[dirty,activityMs]=await Promise.all([deps.isDirty(entry.path).catch(()=>null),lastActivityMs(deps,ctx,entry.path)]);return{path:entry.path,branch:entry.branch,head:entry.head,dirty,roveManaged:ctx.remote?remoteManagedRootForPath(ctx.dir,entry.path)!==null:isRoveManagedPath(repo,entry.path),lastActivityMs:activityMs}});return infos.sort((a,b)=>b.lastActivityMs-a.lastActivityMs),infos}async function adoptablePaths(deps,ctx){requireAbsolute("repo",ctx.dir);let all=parseWorktreeListPorcelain(await deps.runGitStdout(ctx,["worktree","list","--porcelain"])),canon=(p)=>ctx.remote?p:canonicalize(p),canonRepo=canon(ctx.dir),canonMain=all.find((entry)=>entry.path)?.path,canonMainPath=canonMain?canon(canonMain):null,kept=[];for(let entry of all){if(!entry.path)continue;if(entry.bare)continue;if(!entry.branch||entry.detached)continue;let canonEntry=canon(entry.path);if(canonMainPath!==null&&canonEntry===canonMainPath)continue;if(canonEntry===canonRepo)continue;kept.push({path:entry.path,branch:entry.branch,head:entry.head??""})}return kept}async function unreadableWorktreeNames(deps,ctx){if(ctx.remote)return[];requireAbsolute("repo",ctx.dir);let adminRoot;try{let common=(await deps.runGitStdout(ctx,["rev-parse","--git-common-dir"])).trim();if(!common)return[];adminRoot=path.join(path.resolve(ctx.dir,common),"worktrees")}catch{return[]}let names;try{names=fs.readdirSync(adminRoot,{withFileTypes:!0}).filter((entry)=>entry.isDirectory()).map((entry)=>entry.name)}catch{return[]}if(names.length===0)return[];let listed;try{listed=await deps.runGitStdout(ctx,["worktree","list","--porcelain"])}catch{return[]}let reported=new Set(parseWorktreeListPorcelain(listed).filter((entry)=>entry.path).map((entry)=>canonicalize(entry.path))),missing=[];for(let name of names){let gitdir;try{gitdir=fs.readFileSync(path.join(adminRoot,name,"gitdir"),"utf8").trim()}catch{missing.push(name);continue}if(gitdir&&!reported.has(canonicalize(path.dirname(gitdir))))missing.push(name)}return missing}import path2 from"path";async function isRegisteredWorktree(deps,exec,repo,worktreePath){let out=await deps.runGit(exec,["worktree","list","--porcelain"],{cwd:repo,allowFail:!0,readOnly:!0});if(out.exitCode!==0)return!1;let same=(a,b)=>exec.isRemote?a===b:canonicalize(a)===canonicalize(b);return parseWorktreeListPorcelain(out.stdout).some((e)=>e.path!==void 0&&same(e.path,worktreePath))}async function deregisteredWorktreeResidue(exec,worktreePath){let pointer=(await exec.readFile(`${worktreePath}/.git`))?.trim().match(/^gitdir:\s*(.+)$/)?.[1]?.trim();if(!pointer)return!1;let marker="/worktrees/",at=pointer.lastIndexOf(marker);if(at<0)return!1;return await exec.exists(pointer.slice(0,at))}function dirtyRefusal(worktreePath,ignored,dirty=[]){return`${DIRTY_WORKTREE_CODE}: ${worktreePath} has ${describeDirtyWorktreeWork(ignored,dirty)} \u2014 forcing the removal salvages it to a ref first`}async function removeWorktree(deps,worktreePath,opts){requireAbsolute("path",worktreePath);let exec=deps.execForPath(worktreePath),force=opts?.force===!0;if(!await exec.exists(worktreePath)){let goneRepo=opts?.repo??await deps.findRepoFor(exec,path2.dirname(worktreePath));if(goneRepo){if(await deps.runGit(exec,["worktree","prune"],{cwd:goneRepo,allowFail:!0}),opts?.deleteBranch===!0&&opts.branch){let outcome=await deleteBranchAnchored(deps.branchDeps(),exec,goneRepo,opts.branch,{force});if(!outcome.deleted)opts.onBranchKept?.({branch:opts.branch,reason:outcome.reason})}}return}let repo=await deps.findRepoFor(exec,worktreePath);if(repo===null||!await isRegisteredWorktree(deps,exec,repo,worktreePath)){if(await deregisteredWorktreeResidue(exec,worktreePath)){opts?.onResidue?.({path:worktreePath,reason:"a previous removal deregistered the worktree"});return}if(repo){opts?.onResidue?.({path:worktreePath,reason:"a previous removal deregistered the worktree"});return}if(!force)throw Error(`remove(): ${worktreePath} is not a git worktree`);if(!isUnderManagedWorktreesRoot(worktreePath,opts?.repo))throw Error(`remove(): ${worktreePath} has no reachable git repo and is not under a Rove worktrees root; refusing to delete it`);if(opts?.onSalvage?.(null),await exec.run(["rm","-rf",worktreePath]),await exec.exists(worktreePath))opts?.onResidue?.({path:worktreePath,reason:"the directory could not be deleted"});return}let branch=opts?.deleteBranch?await deps.currentBranch(worktreePath).catch(()=>null)??opts.branch??null:null;if(force){let salvaged=await salvageWorktree({runGit:(e,a,o)=>deps.runGit(e,a,o)},exec,worktreePath);opts?.onSalvage?.(salvaged)}else{let dirty=await deps.dirtyPaths(worktreePath);if(dirty.length>0)throw Error(dirtyRefusal(worktreePath,[],dirty));let ignored=await deps.ignoredWork(worktreePath);if(ignored==="unknown"||ignored.length>0)throw Error(dirtyRefusal(worktreePath,ignored))}let args=force?["worktree","remove","--force",worktreePath]:["worktree","remove",worktreePath],result=await deps.runGit(exec,args,{cwd:repo,allowFail:!0});if(result.exitCode!==0){if(await isRegisteredWorktree(deps,exec,repo,worktreePath))throw new GitCommandError(args,repo,result);opts?.onResidue?.({path:worktreePath,reason:result.stderr.trim()||result.stdout.trim()||"unknown"})}if(await deps.runGit(exec,["worktree","prune"],{cwd:repo,allowFail:!0}),branch){let outcome=await deleteBranchAnchored(deps.branchDeps(),exec,repo,branch,{force});if(!outcome.deleted)opts?.onBranchKept?.({branch,reason:outcome.reason})}}class GitWorktreeManager{execDeps;constructor(execDeps=defaultExecDeps){this.execDeps=execDeps}ctxFor(repoKey){let basePath=this.execDeps.remoteBasePath(repoKey);return basePath?{exec:this.execDeps.execForRepo(repoKey),dir:basePath,remote:!0}:{exec:this.execDeps.execForRepo(repoKey),dir:repoKey,remote:!1}}async runGit(exec,args,opts){if(!opts.cwd)throw Error("runGit(): cwd is required; refusing to inherit from process.cwd()");let env=opts.readOnly?{...opts.env,...READ_ONLY_GIT_ENV}:opts.env,r=await exec.run(["git",...args],{cwd:opts.cwd,env}),result={stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode};if(result.exitCode!==0&&!opts.allowFail)throw new GitCommandError(args,opts.cwd,result);return result}async create(repo,branch,worktreePath,baseRef){let ctx=this.ctxFor(repo);if(requireAbsolute("repo",ctx.dir),requireAbsolute("path",worktreePath),!branch)throw Error("create(): branch must be a non-empty string");if(await ctx.exec.exists(worktreePath)){let existing=await this.tryDescribe(ctx,worktreePath);if(existing){if(existing.branch!==branch)throw Error(`worktree at ${worktreePath} is on branch '${existing.branch}', refusing to hijack to '${branch}'`);return existing}throw Error(`create(): ${worktreePath} exists but is not a registered git worktree`)}await ctx.exec.mkdirp(path3.dirname(worktreePath));let args=await branchExists(this.branchDeps(),ctx,branch)?["worktree","add",worktreePath,branch]:baseRef?["worktree","add","-b",branch,worktreePath,baseRef]:["worktree","add","-b",branch,worktreePath];await this.runGit(ctx.exec,args,{cwd:ctx.dir});let info=await this.tryDescribe(ctx,worktreePath);if(!info)throw Error(`create(): git reported success but ${worktreePath} is not a worktree`);if(info.branch!==branch)throw Error(`create(): post-condition failed \u2014 expected branch '${branch}' at ${worktreePath}, got '${info.branch}'`);return info}async createForTask(args){let basePath=this.execDeps.remoteBasePath(args.repo),target=basePath?remoteWorktreePathFor(basePath,args.slug):worktreePathFor(args.repo,args.slug);return this.create(args.repo,args.branch,target,args.baseRef)}async remove(worktreePath,opts){await removeWorktree({runGit:(exec,args,runOpts)=>this.runGit(exec,args,runOpts),execForPath:(p)=>this.execDeps.execForPath(p),findRepoFor:(exec,p)=>this.findRepoFor(exec,p),currentBranch:(p)=>this.currentBranch(p),dirtyPaths:(p)=>this.dirtyPaths(p),ignoredWork:(p)=>this.ignoredWork(p),branchDeps:()=>this.branchDeps()},worktreePath,opts)}async deleteBranch(repo,branch,opts){let ctx=this.ctxFor(repo);return requireAbsolute("repo",ctx.dir),await deleteBranchAnchored(this.branchDeps(),ctx.exec,ctx.dir,branch,{force:opts?.force===!0,onAnchor:opts?.onAnchor})}execAt(worktreePath){return requireAbsolute("path",worktreePath),this.execDeps.execForPath(worktreePath)}branchDeps(){return{runGit:(exec,args,opts)=>this.runGit(exec,args,opts),execAt:(worktreePath)=>this.execAt(worktreePath),findRepoFor:(exec,worktreePath)=>this.findRepoFor(exec,worktreePath)}}listDeps(){return{ctxFor:(repoKey)=>this.ctxFor(repoKey),runGitStdout:async(ctx,args)=>(await this.runGit(ctx.exec,args,{cwd:ctx.dir,readOnly:!0})).stdout,runGitStdoutAt:async(ctx,cwd,args)=>(await this.runGit(ctx.exec,args,{cwd,readOnly:!0})).stdout,isDirty:(worktreePath)=>this.isDirty(worktreePath)}}list(repo){return listManaged(this.listDeps(),repo)}listAll(repo){return listAllAdoptable(this.listDeps(),repo)}listAdoptablePaths(repo){return adoptablePaths(this.listDeps(),this.ctxFor(repo))}listUnreadableWorktrees(repo){return unreadableWorktreeNames(this.listDeps(),this.ctxFor(repo))}listBranchNames(repo){return listBranchNames(this.listDeps(),repo)}async pathExists(worktreePath){return this.execAt(worktreePath).exists(worktreePath)}async pruneWorktrees(repo){let ctx=this.ctxFor(repo);requireAbsolute("repo",ctx.dir),await this.runGit(ctx.exec,["worktree","prune"],{cwd:ctx.dir,allowFail:!0})}async isDirty(worktreePath){return(await this.dirtyPaths(worktreePath)).length>0}async dirtyPaths(worktreePath){let out=await this.runGit(this.execAt(worktreePath),["status","--porcelain"],{cwd:worktreePath,readOnly:!0});return parseDirtyPaths(out.stdout)}async ignoredWork(worktreePath){return smallIgnoredPaths(this.execAt(worktreePath),worktreePath)}async currentBranch(worktreePath){let name=(await this.runGit(this.execAt(worktreePath),["rev-parse","--abbrev-ref","HEAD"],{cwd:worktreePath,readOnly:!0})).stdout.trim();if(!name||name==="HEAD")throw Error(`currentBranch(): ${worktreePath} is in detached-HEAD state`);return name}branchHasUpstream(worktreePath,branch){return branchHasUpstream(this.branchDeps(),worktreePath,branch)}hasLocalBranch(worktreePath,branch){return hasLocalBranch(this.branchDeps(),worktreePath,branch)}renameBranch(worktreePath,from,to){return renameBranch(this.branchDeps(),worktreePath,from,to)}async tryDescribe(ctx,worktreePath){let out=await this.runGit(ctx.exec,["worktree","list","--porcelain"],{cwd:ctx.dir,readOnly:!0}),entries=parseWorktreeListPorcelain(out.stdout),norm=(p)=>ctx.remote?p:canonicalize(p),target=norm(worktreePath),match=entries.find((e)=>e.path&&norm(e.path)===target);if(!match||!match.path||!match.branch||match.detached)return null;return{path:worktreePath,branch:match.branch,head:match.head??"",dirty:await this.isDirty(match.path)}}async findRepoFor(exec,worktreePath){try{let out=await this.runGit(exec,["rev-parse","--git-common-dir"],{cwd:worktreePath,allowFail:!0,readOnly:!0});if(out.exitCode!==0)return null;let gitDir=out.stdout.trim();if(!gitDir)return null;let absolute=path3.isAbsolute(gitDir)?gitDir:path3.resolve(worktreePath,gitDir);return path3.basename(absolute)===".git"?path3.dirname(absolute):absolute}catch(err){if(err instanceof GitCommandError)return null;throw err}}}
|
|
10
|
+
export{parseDirtyPaths,isDirtyOutput,defaultExecDeps,GitWorktreeManager};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{__require}from"./chunk-wqfp8d9w.js";var welcomeWrite=Promise.resolve();function trackWelcomeWrite(write){welcomeWrite=write}async function launchTui(){let{startTui}=await import("./chunk-m1ekbqyz.js");await startTui(),await welcomeWrite;let{runPendingWelcomeInstalls}=await import("./chunk-f1nf99hj.js");runPendingWelcomeInstalls()}
|
|
3
|
+
export{trackWelcomeWrite,launchTui};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{GitWorktreeManager}from"./chunk-m4f5h05c.js";import"./chunk-qvfdqrbf.js";import"./chunk-41x1hrqe.js";import"./chunk-cbjh43yc.js";import"./chunk-2apszxn2.js";import"./chunk-sak1tphp.js";import"./chunk-6xppe9bj.js";import"./chunk-2brn8e1y.js";import"./chunk-7kysxgbq.js";import"./chunk-h9cg0j3a.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-7q43tysm.js";import"./chunk-mw0ysrpd.js";import"./chunk-a7kq09ke.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-pn5ygqn6.js";import"./chunk-npwzmmv9.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-wqfp8d9w.js";export{GitWorktreeManager};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{isProcessAlive}from"./chunk-
|
|
2
|
+
import{isProcessAlive}from"./chunk-a7kq09ke.js";import{randomBytes}from"crypto";import{linkSync,mkdirSync,readFileSync,unlinkSync,writeFileSync}from"fs";import{link,mkdir,readFile,unlink,writeFile}from"fs/promises";import{dirname}from"path";class LockfileError extends Error{heldByPid;constructor(message,heldByPid){super(message);this.name="LockfileError",this.heldByPid=heldByPid}}async function acquire(lockPath,opts={}){await mkdir(dirname(lockPath),{recursive:!0});let token=`${process.pid}:${randomBytes(8).toString("hex")}`,sidecar=`${lockPath}.${token.replace(":","-")}`;for(;;){await writeFile(sidecar,token);try{return await link(sidecar,lockPath),token}catch(err){if(err.code!=="EEXIST")throw err}finally{await unlink(sidecar).catch(()=>{})}let holder;try{holder=(await readFile(lockPath,"utf8")).trim()}catch(err){if(err.code!=="ENOENT")throw err;continue}let holderPid=Number.parseInt(holder,10),alive=Number.isFinite(holderPid)&&holderPid>0&&isProcessAlive(holderPid);if(alive&&!opts.forceTakeover)throw new LockfileError(`task index is locked by another Rove instance (pid ${holderPid})`,holderPid);console.warn(`[rove] removing stale lockfile at ${lockPath} (was held by pid ${holderPid}${alive?", forced":", process gone"})`),releaseSync(lockPath,holder)}}async function release(lockPath,token){let current;try{current=(await readFile(lockPath,"utf8")).trim()}catch(err){if(err.code==="ENOENT")return;throw err}if(current!==token)return;try{await unlink(lockPath)}catch(err){if(err.code==="ENOENT")return;throw err}}function sleepSync(ms){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,ms)}function acquireSync(lockPath,timeoutMs=5000){mkdirSync(dirname(lockPath),{recursive:!0});let token=`${process.pid}:${randomBytes(8).toString("hex")}`,sidecar=`${lockPath}.${token.replace(":","-")}`,deadline=Date.now()+timeoutMs;for(;;){writeFileSync(sidecar,token);try{return linkSync(sidecar,lockPath),token}catch(err){if(err.code!=="EEXIST")throw err}finally{try{unlinkSync(sidecar)}catch{}}let holder;try{holder=readFileSync(lockPath,"utf8").trim()}catch(err){if(err.code!=="ENOENT")throw err;continue}let holderPid=Number.parseInt(holder,10);if(Number.isFinite(holderPid)&&holderPid>0&&isProcessAlive(holderPid)){if(Date.now()>=deadline)throw new LockfileError(`${lockPath} is locked by another Rove instance (pid ${holderPid})`,holderPid);sleepSync(25);continue}console.warn(`[rove] removing stale lockfile at ${lockPath} (was held by pid ${holderPid}, process gone)`),releaseSync(lockPath,holder)}}function releaseSync(lockPath,token){try{if(readFileSync(lockPath,"utf8").trim()!==token)return;unlinkSync(lockPath)}catch(err){if(err.code!=="ENOENT")throw err}}
|
|
3
3
|
export{LockfileError,acquire,release,acquireSync,releaseSync};
|