@sma1lboy/rove 0.9.208 → 0.9.210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/cli/{chunk-4ewx2gqc.js → chunk-0g2g4r4e.js} +1 -1
  2. package/dist/cli/{chunk-chdpmrbj.js → chunk-0nzvv8mj.js} +2 -2
  3. package/dist/cli/{chunk-9dmz0j3m.js → chunk-103d5h4q.js} +2 -2
  4. package/dist/cli/{chunk-3e5n28t6.js → chunk-17e2tgyg.js} +2 -2
  5. package/dist/cli/{chunk-7g48tbk2.js → chunk-1p5334mv.js} +1 -1
  6. package/dist/cli/chunk-21wffeb7.js +2 -0
  7. package/dist/cli/{chunk-dvtxfpxn.js → chunk-2cnzaw88.js} +1 -1
  8. package/dist/cli/{chunk-sqtrxk5j.js → chunk-2me76rna.js} +1 -1
  9. package/dist/cli/{chunk-agmm5cmd.js → chunk-2n62t174.js} +1 -1
  10. package/dist/cli/{chunk-fnq3wqkg.js → chunk-3cx92j5n.js} +1 -1
  11. package/dist/cli/{chunk-d3e2883v.js → chunk-4takap24.js} +1 -1
  12. package/dist/cli/chunk-59xc6m1q.js +3 -0
  13. package/dist/cli/{chunk-mxhb132n.js → chunk-5y3d3sk3.js} +1 -1
  14. package/dist/cli/{chunk-vgva383h.js → chunk-64jfgj4a.js} +1 -1
  15. package/dist/cli/{chunk-bj5kfpds.js → chunk-6yd2fgd5.js} +2 -2
  16. package/dist/cli/{chunk-ax6fqjpd.js → chunk-71xmayay.js} +3 -3
  17. package/dist/cli/{chunk-k4qb9x7r.js → chunk-72b8g3hs.js} +1 -1
  18. package/dist/cli/{chunk-bxf7p7r3.js → chunk-77txn9kg.js} +1 -1
  19. package/dist/cli/chunk-7h2ddwpe.js +14 -0
  20. package/dist/cli/{chunk-58k4dd30.js → chunk-7rw5nym1.js} +1 -1
  21. package/dist/cli/{chunk-n5n3y9rb.js → chunk-9jnjnkqf.js} +1 -1
  22. package/dist/cli/{chunk-kg0snka2.js → chunk-9k41erqh.js} +1 -1
  23. package/dist/cli/{chunk-01p4h28g.js → chunk-aafa44gd.js} +1 -1
  24. package/dist/cli/{chunk-b9ge49ge.js → chunk-c42pwcvw.js} +1 -1
  25. package/dist/cli/{chunk-e5gkvpxx.js → chunk-c8e76j2n.js} +1 -1
  26. package/dist/cli/{chunk-7fpaq7qp.js → chunk-cqspsq15.js} +1 -1
  27. package/dist/cli/{chunk-jhq4v6p4.js → chunk-czbapp4b.js} +1 -1
  28. package/dist/cli/{chunk-h9zx36b2.js → chunk-d9qq7g3p.js} +18 -18
  29. package/dist/cli/{chunk-3jd20m32.js → chunk-dzqqsasz.js} +2 -2
  30. package/dist/cli/{chunk-be7a6xn6.js → chunk-ekmwvws5.js} +1 -1
  31. package/dist/cli/{chunk-r33kmyjp.js → chunk-ezrxbask.js} +1 -1
  32. package/dist/cli/{chunk-3jc514xd.js → chunk-faw6kk1m.js} +1 -1
  33. package/dist/cli/chunk-fjsetmsk.js +3 -0
  34. package/dist/cli/{chunk-egn8kp0g.js → chunk-g9xb8jh5.js} +1 -1
  35. package/dist/cli/{chunk-6mg9f4wq.js → chunk-gc4wfaps.js} +1 -1
  36. package/dist/cli/{chunk-8trdkbjz.js → chunk-gnncrk3w.js} +1 -1
  37. package/dist/cli/{chunk-0tbwx9va.js → chunk-gqqnp8fp.js} +1 -1
  38. package/dist/cli/{chunk-xn402ss7.js → chunk-hbqktm5a.js} +2 -2
  39. package/dist/cli/{chunk-5ysn2pzt.js → chunk-hqbmzz4t.js} +1 -1
  40. package/dist/cli/{chunk-6bxqqgr6.js → chunk-hrwcx8r5.js} +1 -1
  41. package/dist/cli/chunk-j0qr24ff.js +2 -0
  42. package/dist/cli/{chunk-p1dwr9fc.js → chunk-j54zvyrc.js} +1 -1
  43. package/dist/cli/{chunk-0ex8f5gg.js → chunk-jgdjfsdk.js} +1 -1
  44. package/dist/cli/{chunk-q6y71kfg.js → chunk-keyxeze5.js} +1 -1
  45. package/dist/cli/{chunk-kd4x7g32.js → chunk-mat5dr5y.js} +1 -1
  46. package/dist/cli/{chunk-b4bf8w8q.js → chunk-mjdt28x6.js} +1 -1
  47. package/dist/cli/chunk-n6rrghg7.js +9 -0
  48. package/dist/cli/{chunk-kwb78cyq.js → chunk-n85tdzdg.js} +2 -2
  49. package/dist/cli/{chunk-6r8xbmdn.js → chunk-p1zab1sy.js} +1 -1
  50. package/dist/cli/{chunk-sckmwzrw.js → chunk-qgfwebxj.js} +1 -1
  51. package/dist/cli/chunk-qpmfpbw1.js +2 -0
  52. package/dist/cli/{chunk-s53jt3zr.js → chunk-qv5y4xb3.js} +1 -1
  53. package/dist/cli/{chunk-0e8rvs30.js → chunk-qwc3mmhj.js} +1 -1
  54. package/dist/cli/chunk-t142gqnt.js +2 -0
  55. package/dist/cli/chunk-t3dm9102.js +2 -0
  56. package/dist/cli/{chunk-ytmsrjr4.js → chunk-t73qprdc.js} +1 -1
  57. package/dist/cli/{chunk-mtkd49cn.js → chunk-tqr77st4.js} +1 -1
  58. package/dist/cli/{chunk-xkqt2f1p.js → chunk-w0aw0mdw.js} +2 -2
  59. package/dist/cli/{chunk-2v51z796.js → chunk-wrafvgdr.js} +3 -3
  60. package/dist/cli/{chunk-bawdfhqg.js → chunk-x9www3mg.js} +1 -1
  61. package/dist/cli/{chunk-pqgq0452.js → chunk-xf09hgzv.js} +1 -1
  62. package/dist/cli/{chunk-0p5t9dfq.js → chunk-xpwaf5v6.js} +1 -1
  63. package/dist/cli/{chunk-j94fb5fe.js → chunk-yfd7pzj4.js} +1 -1
  64. package/dist/cli/{chunk-gwfkkeq0.js → chunk-yv44tdjq.js} +1 -1
  65. package/dist/cli/{chunk-bc2hf4m0.js → chunk-yvcc04zd.js} +1 -1
  66. package/dist/cli/{chunk-b9x2wvxv.js → chunk-zhbcnr04.js} +1 -1
  67. package/dist/cli/index.js +3 -3
  68. package/dist/cli/kobe.js +1 -1
  69. package/dist/cli/pty-host-node.mjs +1 -0
  70. package/dist/cli/rove.js +1 -1
  71. package/dist/completions/kobe.bash +1 -1
  72. package/dist/completions/kobe.fish +1 -1
  73. package/dist/completions/kobe.zsh +1 -1
  74. package/dist/completions/rove.bash +1 -1
  75. package/dist/completions/rove.fish +1 -1
  76. package/dist/completions/rove.zsh +1 -1
  77. package/dist/skills/rove/SKILL.md +35 -1
  78. package/dist/skills/rove/references/api-flags.md +3 -0
  79. package/package.json +1 -1
  80. package/dist/cli/chunk-0n9xhvwk.js +0 -8
  81. package/dist/cli/chunk-9wgem9z4.js +0 -2
  82. package/dist/cli/chunk-mfj08sv4.js +0 -3
  83. package/dist/cli/chunk-n80zwzae.js +0 -2
  84. package/dist/cli/chunk-q2wxd83y.js +0 -2
  85. package/dist/cli/chunk-qynjk1fb.js +0 -15
  86. package/dist/cli/chunk-szn9z7nt.js +0 -2
  87. package/dist/cli/chunk-wqeeky10.js +0 -2
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{serialized,writeJsonAtomic}from"./chunk-xsrcwcjc.js";import{loadPluginRegistry,pluginConfigDir,pluginDataDir,pluginLogPath,pluginRegistryPath,pluginStateDir,pluginsRootDir}from"./chunk-7g48tbk2.js";import{logDaemonError}from"./chunk-rcyasw34.js";import{OWNER_ONLY_DIR_MODE,OWNER_ONLY_FILE_MODE}from"./chunk-9prawccj.js";import{ROVE_STATE_DIR_BASENAME,readRoveHomeDirEnv}from"./chunk-hq4b3fca.js";import{chmodSync}from"fs";function tighten(path,mode){try{chmodSync(path,mode)}catch{}}function tightenPluginPermissions(id,homeDir){tighten(pluginDataDir(id,homeDir),OWNER_ONLY_DIR_MODE),tighten(pluginConfigDir(id,homeDir),OWNER_ONLY_DIR_MODE),tighten(pluginStateDir(id,homeDir),OWNER_ONLY_DIR_MODE),tighten(`${pluginConfigDir(id,homeDir)}/.env`,OWNER_ONLY_FILE_MODE),tighten(pluginLogPath(id,homeDir),OWNER_ONLY_FILE_MODE)}function tightenInstalledPluginPermissions(homeDir){tighten(pluginsRootDir(homeDir),OWNER_ONLY_DIR_MODE),tighten(pluginRegistryPath(homeDir),OWNER_ONLY_FILE_MODE);for(let entry of loadPluginRegistry(homeDir).plugins)tightenPluginPermissions(entry.id,homeDir)}import{execFile as execFile2}from"child_process";import{readFile}from"fs/promises";import{homedir}from"os";import{join,resolve as resolve2}from"path";import{promisify as promisify2}from"util";import{execFile}from"child_process";import{realpath,stat}from"fs/promises";import{isAbsolute,resolve}from"path";import{promisify}from"util";var execFileAsync=promisify(execFile);async function gitCommonDir(path){let{stdout}=await execFileAsync("git",["-C",path,"rev-parse","--git-common-dir"]),dir=stdout.trim();return realpath(isAbsolute(dir)?dir:resolve(path,dir))}async function gitMainWorktree(path){let{stdout}=await execFileAsync("git",["-C",path,"worktree","list","--porcelain"]),first=stdout.split(/\r?\n/).find((line)=>line.startsWith("worktree "))?.slice(9).trim();return first?await realpath(first):null}async function gitTopLevel(path){let{stdout}=await execFileAsync("git",["-C",path,"rev-parse","--show-toplevel"]);return stdout.trim()}async function resolveRepoRoot(raw){let absolute=resolve(raw);if(!(await stat(absolute).catch(()=>null))?.isDirectory())throw Error("repoRoot does not exist");let[repoRoot,repoKey]=await Promise.all([gitMainWorktree(absolute),gitCommonDir(absolute)]);return{repoRoot,repoKey}}var execFileAsync2=promisify2(execFile2),ISSUE_STATUSES=["open","doing","hold","done"],ISSUE_NOT_FOUND_CODE="ISSUE_NOT_FOUND",ISSUE_STORE_UNREADABLE_CODE="ISSUE_STORE_UNREADABLE";function isGitNotRepositoryError(err){return(err instanceof Error?err.message:String(err)).includes("not a git repository")}function defaultIssuesStorePath(homeDir=readRoveHomeDirEnv()??homedir()){return join(homeDir,ROVE_STATE_DIR_BASENAME,"issues.json")}function isValidStatus(value){return typeof value==="string"&&ISSUE_STATUSES.includes(value)}function normalizeIssue(entry){if(entry===null||typeof entry!=="object"||Array.isArray(entry))return null;let raw=entry;if(typeof raw.id!=="number")return null;return{id:raw.id,title:typeof raw.title==="string"?raw.title:"(untitled)",status:isValidStatus(raw.status)?raw.status:"open",created:typeof raw.created==="string"?raw.created:"",body:typeof raw.body==="string"?raw.body:"",taskId:typeof raw.taskId==="string"?raw.taskId:void 0}}function emptyStore(){return{version:1,repos:{}}}function nextIdFallback(entries){let max=0;for(let entry of entries){let id=Number(entry?.id);if(Number.isFinite(id)&&id>max)max=Math.trunc(id)}return max+1}function todayStamp(){let pinned=process.env.KOBE_ISSUES_TODAY;if(pinned&&/^\d{4}-\d{2}-\d{2}$/.test(pinned))return pinned;let d=new Date,mm=String(d.getMonth()+1).padStart(2,"0"),dd=String(d.getDate()).padStart(2,"0");return`${d.getFullYear()}-${mm}-${dd}`}async function resolveRepo(raw){if(typeof raw!=="string"||raw.length===0)throw Error("repoRoot is required");try{let{repoRoot,repoKey}=await resolveRepoRoot(raw);return{repoRoot:repoRoot??await gitTopLevel(resolve2(raw)),repoKey}}catch(err){if(isGitNotRepositoryError(err))throw Error("repoRoot is not a git repository");throw err}}function unreadableCount(raw){if(raw!==null&&typeof raw==="object"&&!Array.isArray(raw))return Math.max(1,Object.keys(raw).length);return 1}function readRecord(value){let record=value,base={repoRoot:typeof record.repoRoot==="string"?record.repoRoot:"",nextId:typeof record.nextId==="number"?record.nextId:1};if(record.issues!==void 0&&!Array.isArray(record.issues))return{record:{...base,issues:[],unusable:[],unreadable:{raw:value}},dropped:unreadableCount(record.issues)};let entries=Array.isArray(record.issues)?record.issues:[],issues=entries.map(normalizeIssue).filter((issue)=>issue!==null),unusable=entries.filter((entry)=>normalizeIssue(entry)===null);return{record:{repoRoot:base.repoRoot,nextId:typeof record.nextId==="number"?record.nextId:nextIdFallback(entries),issues,unusable},dropped:unusable.length}}async function readStore(path){try{let raw=JSON.parse(await readFile(path,"utf8")),repos={},skipped=new Map;if(raw.repos&&typeof raw.repos==="object")for(let[key,value]of Object.entries(raw.repos)){if(!value||typeof value!=="object")continue;let{record,dropped}=readRecord(value);if(dropped>0){skipped.set(key,dropped);let why=record.unreadable?'"issues" is not an array':"id is not a number";logDaemonError("issues-store-read",Error(`${key}: dropped ${dropped} unreadable issue entr${dropped===1?"y":"ies"} (${why})`))}repos[key]=record}return{file:{version:1,repos},skipped}}catch(err){if(err.code==="ENOENT")return{file:emptyStore(),skipped:new Map};throw Error(`${path}: ${err instanceof Error?err.message:String(err)}`)}}function serializeRecord(record){if(record.unreadable)return record.unreadable.raw;return{repoRoot:record.repoRoot,nextId:record.nextId,issues:[...record.issues,...record.unusable]}}async function writeStore(path,store){let repos={};for(let[key,record]of Object.entries(store.repos))repos[key]=serializeRecord(record);await writeJsonAtomic(path,{version:store.version,repos})}function response(repoRoot,record,skipped=0){return{repoRoot,exists:record!==null,nextId:record?.nextId??1,issues:record?.issues??[],skipped}}class IssuesStore{path;constructor(path=defaultIssuesStorePath()){this.path=path}async list(repo){let{repoRoot,repoKey}=await resolveRepo(repo);return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey]??null;if(record&&!record.unreadable&&record.repoRoot!==repoRoot)record.repoRoot=repoRoot,await writeStore(this.path,store);return response(repoRoot,record,skipped.get(repoKey)??0)})}async repos(){return serialized(this.path,async()=>{let{file}=await readStore(this.path);return Object.values(file.repos).map((record)=>record.repoRoot).filter((root)=>root.length>0)})}async mirrorTaskDone(repo,taskId){let{repoRoot,repoKey}=await resolveRepo(repo);if(!taskId)return null;return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey];if(!record)return null;let issue=record.issues.find((i)=>i.taskId===taskId);if(!issue||issue.status==="done")return null;return issue.status="done",record.repoRoot=repoRoot,await writeStore(this.path,store),response(repoRoot,record,skipped.get(repoKey)??0)})}async unlinkTask(repo,taskId){let{repoRoot,repoKey}=await resolveRepo(repo);if(!taskId)return null;return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey];if(!record)return null;let issue=record.issues.find((i)=>i.taskId===taskId);if(!issue)return null;return issue.taskId=void 0,record.repoRoot=repoRoot,await writeStore(this.path,store),response(repoRoot,record,skipped.get(repoKey)??0)})}async mutate(repo,op){let{repoRoot,repoKey}=await resolveRepo(repo);if(!op||typeof op!=="object"||Array.isArray(op)||typeof op.type!=="string")throw Error("missing op");return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey];if(!record)record={repoRoot,nextId:1,issues:[],unusable:[]},store.repos[repoKey]=record;if(record.unreadable)throw Error(`${ISSUE_STORE_UNREADABLE_CODE}: ${repoRoot}'s record in ${this.path} has a non-array "issues" \u2014 refusing to write over stories this build cannot read`);record.repoRoot=repoRoot;let typed=op;if(typed.type==="create"){if(typeof typed.title!=="string"||typed.title.trim().length===0)throw Error("create requires a non-empty title");if(typed.body!==void 0&&typeof typed.body!=="string")throw Error("body must be a string");record.issues=[{id:record.nextId,title:typed.title,status:"open",created:todayStamp(),body:typeof typed.body==="string"?typed.body:""},...record.issues],record.nextId+=1}else if(typed.type==="setStatus"){if(typeof typed.id!=="number")throw Error("setStatus requires a numeric id");if(!isValidStatus(typed.status))throw Error(`invalid status: must be one of ${ISSUE_STATUSES.join(", ")}`);let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);issue.status=typed.status}else if(typed.type==="update"){if(typeof typed.id!=="number")throw Error("update requires a numeric id");if(typed.title!==void 0&&(typeof typed.title!=="string"||typed.title.trim().length===0))throw Error("title must be a non-empty string");if(typed.body!==void 0&&typeof typed.body!=="string")throw Error("body must be a string");if(typed.taskId!==void 0&&typed.taskId!==null&&(typeof typed.taskId!=="string"||typed.taskId.length===0))throw Error("taskId must be a non-empty string or null");let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);if(typeof typed.title==="string")issue.title=typed.title;if(typeof typed.body==="string")issue.body=typed.body;if(typed.taskId!==void 0)issue.taskId=typed.taskId===null?void 0:typed.taskId}else if(typed.type==="link"){if(typeof typed.id!=="number")throw Error("link requires a numeric id");if(typeof typed.taskId!=="string"||typed.taskId.length===0)throw Error("link requires a non-empty taskId");let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);issue.taskId=typed.taskId}else if(typed.type==="unlink"){if(typeof typed.id!=="number")throw Error("unlink requires a numeric id");let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);issue.taskId=void 0}else if(typed.type==="delete"){if(typeof typed.id!=="number")throw Error("delete requires a numeric id");let nextIssues=record.issues.filter((i)=>i.id!==typed.id);if(nextIssues.length===record.issues.length)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);record.issues=nextIssues}else throw Error(`unknown op type: ${typed.type}`);return await writeStore(this.path,store),response(repoRoot,record,skipped.get(repoKey)??0)})}}var MONTH_NAMES=new Map([["JAN",1],["FEB",2],["MAR",3],["APR",4],["MAY",5],["JUN",6],["JUL",7],["AUG",8],["SEP",9],["OCT",10],["NOV",11],["DEC",12]]),DAY_NAMES=new Map([["SUN",0],["MON",1],["TUE",2],["WED",3],["THU",4],["FRI",5],["SAT",6]]);function fieldNumber(raw,names,field){let token=raw.toUpperCase(),named=names?.get(token);if(named!==void 0)return named;if(!/^\d+$/.test(token))throw Error(`invalid cron ${field}: ${raw}`);return Number(token)}function parseField(args){let out=new Set;for(let part of args.value.split(",")){if(part.length===0)throw Error(`invalid cron ${args.field}: ${args.value}`);let[spec,stepRaw,...extra]=part.split("/");if(extra.length>0||spec===void 0)throw Error(`invalid cron ${args.field}: ${part}`);let step=1;if(stepRaw!==void 0){if(!/^\d+$/.test(stepRaw))throw Error(`invalid cron ${args.field} step: ${part}`);if(step=Number(stepRaw),step<1)throw Error(`invalid cron ${args.field} step: ${part}`)}let lo,hi;if(spec==="*")lo=args.min,hi=args.max;else if(spec.includes("-")){let[loRaw,hiRaw,...rest]=spec.split("-");if(rest.length>0||loRaw===void 0||hiRaw===void 0)throw Error(`invalid cron ${args.field} range: ${part}`);lo=fieldNumber(loRaw,args.names,args.field),hi=fieldNumber(hiRaw,args.names,args.field)}else lo=fieldNumber(spec,args.names,args.field),hi=stepRaw===void 0?lo:args.max;if(lo<args.min||hi>args.max||lo>hi)throw Error(`cron ${args.field} out of range: ${part}`);for(let v=lo;v<=hi;v+=step)out.add(args.normalize?args.normalize(v):v)}return out}function parseCron(expression){if(expression.length>256)throw Error("cron expression is too long");let fields=expression.trim().split(/\s+/).filter(Boolean);if(fields.length!==5)throw Error(`cron expression needs 5 fields (minute hour day month weekday), got ${fields.length}`);let[minute,hour,dayOfMonth,month,dayOfWeek]=fields;return{minutes:parseField({value:minute,min:0,max:59,field:"minute"}),hours:parseField({value:hour,min:0,max:23,field:"hour"}),daysOfMonth:parseField({value:dayOfMonth,min:1,max:31,field:"day-of-month"}),months:parseField({value:month,min:1,max:12,field:"month",names:MONTH_NAMES}),daysOfWeek:parseField({value:dayOfWeek,min:0,max:7,field:"weekday",names:DAY_NAMES,normalize:(v)=>v%7}),dayOfMonthRestricted:dayOfMonth!=="*",dayOfWeekRestricted:dayOfWeek!=="*"}}function isValidCron(expression){try{return parseCron(expression),!0}catch{return!1}}function instantOf(d,hour,minute){return new Date(d.year,d.month-1,d.day,hour,minute,0,0).getTime()}function localDayOf(ms){let d=new Date(ms);return{year:d.getFullYear(),month:d.getMonth()+1,day:d.getDate()}}function shiftDay(d,delta){return localDayOf(new Date(d.year,d.month-1,d.day+delta,12,0,0,0).getTime())}function dayMatches(rule,d){if(!rule.months.has(d.month))return!1;let domMatch=rule.daysOfMonth.has(d.day),dowMatch=rule.daysOfWeek.has(new Date(d.year,d.month-1,d.day).getDay());if(rule.dayOfMonthRestricted&&rule.dayOfWeekRestricted)return domMatch||dowMatch;if(rule.dayOfMonthRestricted)return domMatch;if(rule.dayOfWeekRestricted)return dowMatch;return!0}function floorToMinute(ms){return ms-ms%60000}function sorted(values,direction){return[...values].sort((a,b)=>(a-b)*direction)}function nextCronAfter(expression,afterMs){let rule=parseCron(expression),hours=sorted(rule.hours,1),minutes=sorted(rule.minutes,1),from=new Date(floorToMinute(afterMs)+60000),day=localDayOf(from.getTime()),fromHour=from.getHours(),fromMinute=from.getMinutes();for(let i=0;i<3294;i++){if(dayMatches(rule,day))for(let hour of hours){if(hour<fromHour)continue;for(let minute of minutes){if(hour===fromHour&&minute<fromMinute)continue;let at=instantOf(day,hour,minute);if(at>afterMs)return at}}day=shiftDay(day,1),fromHour=0,fromMinute=0}throw Error(`cron expression never matches: ${expression}`)}function latestCronAtOrBefore(expression,nowMs,notBeforeMs){if(nowMs<notBeforeMs)return null;let rule=parseCron(expression),hours=sorted(rule.hours,-1),minutes=sorted(rule.minutes,-1),from=new Date(floorToMinute(nowMs)),day=localDayOf(from.getTime()),fromHour=from.getHours(),fromMinute=from.getMinutes();for(let i=0;i<3294;i++){if(instantOf(day,23,59)<notBeforeMs)return null;if(dayMatches(rule,day))for(let hour of hours){if(hour>fromHour)continue;for(let minute of minutes){if(hour===fromHour&&minute>fromMinute)continue;let at=instantOf(day,hour,minute);if(at>nowMs)continue;return at>=notBeforeMs?at:null}}day=shiftDay(day,-1),fromHour=23,fromMinute=59}return null}function countCronBetween(expression,fromMs,beforeMs,cap=500){if(!(fromMs<beforeMs))return 0;let count=0,cursor=fromMs-1;while(count<cap){let at;try{at=nextCronAfter(expression,cursor)}catch{return count}if(at>=beforeMs)return count;count+=1,cursor=at}return count}
2
+ import{serialized,writeJsonAtomic}from"./chunk-xsrcwcjc.js";import{loadPluginRegistry,pluginConfigDir,pluginDataDir,pluginLogPath,pluginRegistryPath,pluginStateDir,pluginsRootDir}from"./chunk-1p5334mv.js";import{logDaemonError}from"./chunk-rcyasw34.js";import{OWNER_ONLY_DIR_MODE,OWNER_ONLY_FILE_MODE}from"./chunk-9prawccj.js";import{ROVE_STATE_DIR_BASENAME,readRoveHomeDirEnv}from"./chunk-hq4b3fca.js";import{chmodSync}from"fs";function tighten(path,mode){try{chmodSync(path,mode)}catch{}}function tightenPluginPermissions(id,homeDir){tighten(pluginDataDir(id,homeDir),OWNER_ONLY_DIR_MODE),tighten(pluginConfigDir(id,homeDir),OWNER_ONLY_DIR_MODE),tighten(pluginStateDir(id,homeDir),OWNER_ONLY_DIR_MODE),tighten(`${pluginConfigDir(id,homeDir)}/.env`,OWNER_ONLY_FILE_MODE),tighten(pluginLogPath(id,homeDir),OWNER_ONLY_FILE_MODE)}function tightenInstalledPluginPermissions(homeDir){tighten(pluginsRootDir(homeDir),OWNER_ONLY_DIR_MODE),tighten(pluginRegistryPath(homeDir),OWNER_ONLY_FILE_MODE);for(let entry of loadPluginRegistry(homeDir).plugins)tightenPluginPermissions(entry.id,homeDir)}import{execFile as execFile2}from"child_process";import{readFile}from"fs/promises";import{homedir}from"os";import{join,resolve as resolve2}from"path";import{promisify as promisify2}from"util";import{execFile}from"child_process";import{realpath,stat}from"fs/promises";import{isAbsolute,resolve}from"path";import{promisify}from"util";var execFileAsync=promisify(execFile);async function gitCommonDir(path){let{stdout}=await execFileAsync("git",["-C",path,"rev-parse","--git-common-dir"]),dir=stdout.trim();return realpath(isAbsolute(dir)?dir:resolve(path,dir))}async function gitMainWorktree(path){let{stdout}=await execFileAsync("git",["-C",path,"worktree","list","--porcelain"]),first=stdout.split(/\r?\n/).find((line)=>line.startsWith("worktree "))?.slice(9).trim();return first?await realpath(first):null}async function gitTopLevel(path){let{stdout}=await execFileAsync("git",["-C",path,"rev-parse","--show-toplevel"]);return stdout.trim()}async function resolveRepoRoot(raw){let absolute=resolve(raw);if(!(await stat(absolute).catch(()=>null))?.isDirectory())throw Error("repoRoot does not exist");let[repoRoot,repoKey]=await Promise.all([gitMainWorktree(absolute),gitCommonDir(absolute)]);return{repoRoot,repoKey}}var execFileAsync2=promisify2(execFile2),ISSUE_STATUSES=["open","doing","hold","done"],ISSUE_NOT_FOUND_CODE="ISSUE_NOT_FOUND",ISSUE_STORE_UNREADABLE_CODE="ISSUE_STORE_UNREADABLE";function isGitNotRepositoryError(err){return(err instanceof Error?err.message:String(err)).includes("not a git repository")}function defaultIssuesStorePath(homeDir=readRoveHomeDirEnv()??homedir()){return join(homeDir,ROVE_STATE_DIR_BASENAME,"issues.json")}function isValidStatus(value){return typeof value==="string"&&ISSUE_STATUSES.includes(value)}function normalizeIssue(entry){if(entry===null||typeof entry!=="object"||Array.isArray(entry))return null;let raw=entry;if(typeof raw.id!=="number")return null;return{id:raw.id,title:typeof raw.title==="string"?raw.title:"(untitled)",status:isValidStatus(raw.status)?raw.status:"open",created:typeof raw.created==="string"?raw.created:"",body:typeof raw.body==="string"?raw.body:"",taskId:typeof raw.taskId==="string"?raw.taskId:void 0}}function emptyStore(){return{version:1,repos:{}}}function nextIdFallback(entries){let max=0;for(let entry of entries){let id=Number(entry?.id);if(Number.isFinite(id)&&id>max)max=Math.trunc(id)}return max+1}function todayStamp(){let pinned=process.env.KOBE_ISSUES_TODAY;if(pinned&&/^\d{4}-\d{2}-\d{2}$/.test(pinned))return pinned;let d=new Date,mm=String(d.getMonth()+1).padStart(2,"0"),dd=String(d.getDate()).padStart(2,"0");return`${d.getFullYear()}-${mm}-${dd}`}async function resolveRepo(raw){if(typeof raw!=="string"||raw.length===0)throw Error("repoRoot is required");try{let{repoRoot,repoKey}=await resolveRepoRoot(raw);return{repoRoot:repoRoot??await gitTopLevel(resolve2(raw)),repoKey}}catch(err){if(isGitNotRepositoryError(err))throw Error("repoRoot is not a git repository");throw err}}function unreadableCount(raw){if(raw!==null&&typeof raw==="object"&&!Array.isArray(raw))return Math.max(1,Object.keys(raw).length);return 1}function readRecord(value){let record=value,base={repoRoot:typeof record.repoRoot==="string"?record.repoRoot:"",nextId:typeof record.nextId==="number"?record.nextId:1};if(record.issues!==void 0&&!Array.isArray(record.issues))return{record:{...base,issues:[],unusable:[],unreadable:{raw:value}},dropped:unreadableCount(record.issues)};let entries=Array.isArray(record.issues)?record.issues:[],issues=entries.map(normalizeIssue).filter((issue)=>issue!==null),unusable=entries.filter((entry)=>normalizeIssue(entry)===null);return{record:{repoRoot:base.repoRoot,nextId:typeof record.nextId==="number"?record.nextId:nextIdFallback(entries),issues,unusable},dropped:unusable.length}}async function readStore(path){try{let raw=JSON.parse(await readFile(path,"utf8")),repos={},skipped=new Map;if(raw.repos&&typeof raw.repos==="object")for(let[key,value]of Object.entries(raw.repos)){if(!value||typeof value!=="object")continue;let{record,dropped}=readRecord(value);if(dropped>0){skipped.set(key,dropped);let why=record.unreadable?'"issues" is not an array':"id is not a number";logDaemonError("issues-store-read",Error(`${key}: dropped ${dropped} unreadable issue entr${dropped===1?"y":"ies"} (${why})`))}repos[key]=record}return{file:{version:1,repos},skipped}}catch(err){if(err.code==="ENOENT")return{file:emptyStore(),skipped:new Map};throw Error(`${path}: ${err instanceof Error?err.message:String(err)}`)}}function serializeRecord(record){if(record.unreadable)return record.unreadable.raw;return{repoRoot:record.repoRoot,nextId:record.nextId,issues:[...record.issues,...record.unusable]}}async function writeStore(path,store){let repos={};for(let[key,record]of Object.entries(store.repos))repos[key]=serializeRecord(record);await writeJsonAtomic(path,{version:store.version,repos})}function response(repoRoot,record,skipped=0){return{repoRoot,exists:record!==null,nextId:record?.nextId??1,issues:record?.issues??[],skipped}}class IssuesStore{path;constructor(path=defaultIssuesStorePath()){this.path=path}async list(repo){let{repoRoot,repoKey}=await resolveRepo(repo);return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey]??null;if(record&&!record.unreadable&&record.repoRoot!==repoRoot)record.repoRoot=repoRoot,await writeStore(this.path,store);return response(repoRoot,record,skipped.get(repoKey)??0)})}async repos(){return serialized(this.path,async()=>{let{file}=await readStore(this.path);return Object.values(file.repos).map((record)=>record.repoRoot).filter((root)=>root.length>0)})}async mirrorTaskDone(repo,taskId){let{repoRoot,repoKey}=await resolveRepo(repo);if(!taskId)return null;return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey];if(!record)return null;let issue=record.issues.find((i)=>i.taskId===taskId);if(!issue||issue.status==="done")return null;return issue.status="done",record.repoRoot=repoRoot,await writeStore(this.path,store),response(repoRoot,record,skipped.get(repoKey)??0)})}async unlinkTask(repo,taskId){let{repoRoot,repoKey}=await resolveRepo(repo);if(!taskId)return null;return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey];if(!record)return null;let issue=record.issues.find((i)=>i.taskId===taskId);if(!issue)return null;return issue.taskId=void 0,record.repoRoot=repoRoot,await writeStore(this.path,store),response(repoRoot,record,skipped.get(repoKey)??0)})}async mutate(repo,op){let{repoRoot,repoKey}=await resolveRepo(repo);if(!op||typeof op!=="object"||Array.isArray(op)||typeof op.type!=="string")throw Error("missing op");return serialized(this.path,async()=>{let{file:store,skipped}=await readStore(this.path),record=store.repos[repoKey];if(!record)record={repoRoot,nextId:1,issues:[],unusable:[]},store.repos[repoKey]=record;if(record.unreadable)throw Error(`${ISSUE_STORE_UNREADABLE_CODE}: ${repoRoot}'s record in ${this.path} has a non-array "issues" \u2014 refusing to write over stories this build cannot read`);record.repoRoot=repoRoot;let typed=op;if(typed.type==="create"){if(typeof typed.title!=="string"||typed.title.trim().length===0)throw Error("create requires a non-empty title");if(typed.body!==void 0&&typeof typed.body!=="string")throw Error("body must be a string");record.issues=[{id:record.nextId,title:typed.title,status:"open",created:todayStamp(),body:typeof typed.body==="string"?typed.body:""},...record.issues],record.nextId+=1}else if(typed.type==="setStatus"){if(typeof typed.id!=="number")throw Error("setStatus requires a numeric id");if(!isValidStatus(typed.status))throw Error(`invalid status: must be one of ${ISSUE_STATUSES.join(", ")}`);let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);issue.status=typed.status}else if(typed.type==="update"){if(typeof typed.id!=="number")throw Error("update requires a numeric id");if(typed.title!==void 0&&(typeof typed.title!=="string"||typed.title.trim().length===0))throw Error("title must be a non-empty string");if(typed.body!==void 0&&typeof typed.body!=="string")throw Error("body must be a string");if(typed.taskId!==void 0&&typed.taskId!==null&&(typeof typed.taskId!=="string"||typed.taskId.length===0))throw Error("taskId must be a non-empty string or null");let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);if(typeof typed.title==="string")issue.title=typed.title;if(typeof typed.body==="string")issue.body=typed.body;if(typed.taskId!==void 0)issue.taskId=typed.taskId===null?void 0:typed.taskId}else if(typed.type==="link"){if(typeof typed.id!=="number")throw Error("link requires a numeric id");if(typeof typed.taskId!=="string"||typed.taskId.length===0)throw Error("link requires a non-empty taskId");let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);issue.taskId=typed.taskId}else if(typed.type==="unlink"){if(typeof typed.id!=="number")throw Error("unlink requires a numeric id");let issue=record.issues.find((i)=>i.id===typed.id);if(!issue)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);issue.taskId=void 0}else if(typed.type==="delete"){if(typeof typed.id!=="number")throw Error("delete requires a numeric id");let nextIssues=record.issues.filter((i)=>i.id!==typed.id);if(nextIssues.length===record.issues.length)throw Error(`${ISSUE_NOT_FOUND_CODE}: no issue #${typed.id}`);record.issues=nextIssues}else throw Error(`unknown op type: ${typed.type}`);return await writeStore(this.path,store),response(repoRoot,record,skipped.get(repoKey)??0)})}}var MONTH_NAMES=new Map([["JAN",1],["FEB",2],["MAR",3],["APR",4],["MAY",5],["JUN",6],["JUL",7],["AUG",8],["SEP",9],["OCT",10],["NOV",11],["DEC",12]]),DAY_NAMES=new Map([["SUN",0],["MON",1],["TUE",2],["WED",3],["THU",4],["FRI",5],["SAT",6]]);function fieldNumber(raw,names,field){let token=raw.toUpperCase(),named=names?.get(token);if(named!==void 0)return named;if(!/^\d+$/.test(token))throw Error(`invalid cron ${field}: ${raw}`);return Number(token)}function parseField(args){let out=new Set;for(let part of args.value.split(",")){if(part.length===0)throw Error(`invalid cron ${args.field}: ${args.value}`);let[spec,stepRaw,...extra]=part.split("/");if(extra.length>0||spec===void 0)throw Error(`invalid cron ${args.field}: ${part}`);let step=1;if(stepRaw!==void 0){if(!/^\d+$/.test(stepRaw))throw Error(`invalid cron ${args.field} step: ${part}`);if(step=Number(stepRaw),step<1)throw Error(`invalid cron ${args.field} step: ${part}`)}let lo,hi;if(spec==="*")lo=args.min,hi=args.max;else if(spec.includes("-")){let[loRaw,hiRaw,...rest]=spec.split("-");if(rest.length>0||loRaw===void 0||hiRaw===void 0)throw Error(`invalid cron ${args.field} range: ${part}`);lo=fieldNumber(loRaw,args.names,args.field),hi=fieldNumber(hiRaw,args.names,args.field)}else lo=fieldNumber(spec,args.names,args.field),hi=stepRaw===void 0?lo:args.max;if(lo<args.min||hi>args.max||lo>hi)throw Error(`cron ${args.field} out of range: ${part}`);for(let v=lo;v<=hi;v+=step)out.add(args.normalize?args.normalize(v):v)}return out}function parseCron(expression){if(expression.length>256)throw Error("cron expression is too long");let fields=expression.trim().split(/\s+/).filter(Boolean);if(fields.length!==5)throw Error(`cron expression needs 5 fields (minute hour day month weekday), got ${fields.length}`);let[minute,hour,dayOfMonth,month,dayOfWeek]=fields;return{minutes:parseField({value:minute,min:0,max:59,field:"minute"}),hours:parseField({value:hour,min:0,max:23,field:"hour"}),daysOfMonth:parseField({value:dayOfMonth,min:1,max:31,field:"day-of-month"}),months:parseField({value:month,min:1,max:12,field:"month",names:MONTH_NAMES}),daysOfWeek:parseField({value:dayOfWeek,min:0,max:7,field:"weekday",names:DAY_NAMES,normalize:(v)=>v%7}),dayOfMonthRestricted:dayOfMonth!=="*",dayOfWeekRestricted:dayOfWeek!=="*"}}function isValidCron(expression){try{return parseCron(expression),!0}catch{return!1}}function instantOf(d,hour,minute){return new Date(d.year,d.month-1,d.day,hour,minute,0,0).getTime()}function localDayOf(ms){let d=new Date(ms);return{year:d.getFullYear(),month:d.getMonth()+1,day:d.getDate()}}function shiftDay(d,delta){return localDayOf(new Date(d.year,d.month-1,d.day+delta,12,0,0,0).getTime())}function dayMatches(rule,d){if(!rule.months.has(d.month))return!1;let domMatch=rule.daysOfMonth.has(d.day),dowMatch=rule.daysOfWeek.has(new Date(d.year,d.month-1,d.day).getDay());if(rule.dayOfMonthRestricted&&rule.dayOfWeekRestricted)return domMatch||dowMatch;if(rule.dayOfMonthRestricted)return domMatch;if(rule.dayOfWeekRestricted)return dowMatch;return!0}function floorToMinute(ms){return ms-ms%60000}function sorted(values,direction){return[...values].sort((a,b)=>(a-b)*direction)}function nextCronAfter(expression,afterMs){let rule=parseCron(expression),hours=sorted(rule.hours,1),minutes=sorted(rule.minutes,1),from=new Date(floorToMinute(afterMs)+60000),day=localDayOf(from.getTime()),fromHour=from.getHours(),fromMinute=from.getMinutes();for(let i=0;i<3294;i++){if(dayMatches(rule,day))for(let hour of hours){if(hour<fromHour)continue;for(let minute of minutes){if(hour===fromHour&&minute<fromMinute)continue;let at=instantOf(day,hour,minute);if(at>afterMs)return at}}day=shiftDay(day,1),fromHour=0,fromMinute=0}throw Error(`cron expression never matches: ${expression}`)}function latestCronAtOrBefore(expression,nowMs,notBeforeMs){if(nowMs<notBeforeMs)return null;let rule=parseCron(expression),hours=sorted(rule.hours,-1),minutes=sorted(rule.minutes,-1),from=new Date(floorToMinute(nowMs)),day=localDayOf(from.getTime()),fromHour=from.getHours(),fromMinute=from.getMinutes();for(let i=0;i<3294;i++){if(instantOf(day,23,59)<notBeforeMs)return null;if(dayMatches(rule,day))for(let hour of hours){if(hour>fromHour)continue;for(let minute of minutes){if(hour===fromHour&&minute>fromMinute)continue;let at=instantOf(day,hour,minute);if(at>nowMs)continue;return at>=notBeforeMs?at:null}}day=shiftDay(day,-1),fromHour=23,fromMinute=59}return null}function countCronBetween(expression,fromMs,beforeMs,cap=500){if(!(fromMs<beforeMs))return 0;let count=0,cursor=fromMs-1;while(count<cap){let at;try{at=nextCronAfter(expression,cursor)}catch{return count}if(at>=beforeMs)return count;count+=1,cursor=at}return count}
3
3
  export{tightenPluginPermissions,tightenInstalledPluginPermissions,isValidCron,nextCronAfter,latestCronAtOrBefore,countCronBetween,resolveRepoRoot,ISSUE_STATUSES,defaultIssuesStorePath,IssuesStore};
@@ -1,9 +1,9 @@
1
1
  // @bun
2
- import{getPersistedString,setPersistedString}from"./chunk-crt11rmj.js";import{activeCliName}from"./chunk-00ehck55.js";import{ROVE_PRODUCT_NAME}from"./chunk-sb0ns0c7.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 KOBE_SKILL_VERSION=47,ROVE_SKILL_REL_PATHS=[".agents/skills/rove/SKILL.md",".claude/skills/rove/SKILL.md"],LEGACY_SKILL_REL_PATHS=[".agents/skills/kobe/SKILL.md",".claude/skills/kobe/SKILL.md"],SKILL_REL_PATHS=[...ROVE_SKILL_REL_PATHS,...LEGACY_SKILL_REL_PATHS];function skillInstallCommand(env=process.env){return`${activeCliName(env)} skill install`}var SKILL_SOURCE_SLUG="Sma1lboy/rove";function bundledSkillDir(){let here=fileURLToPath(import.meta.url);return[resolve(here,"../../../../../.agents/skills/kobe"),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`${activeCliName()} skill install needs \`npx\` (part of Node.js), which isn't on your PATH.
2
+ import{getPersistedString,setPersistedString}from"./chunk-crt11rmj.js";import{activeCliName}from"./chunk-00ehck55.js";import{ROVE_PRODUCT_NAME}from"./chunk-sb0ns0c7.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 KOBE_SKILL_VERSION=49,ROVE_SKILL_REL_PATHS=[".agents/skills/rove/SKILL.md",".claude/skills/rove/SKILL.md"],LEGACY_SKILL_REL_PATHS=[".agents/skills/kobe/SKILL.md",".claude/skills/kobe/SKILL.md"],SKILL_REL_PATHS=[...ROVE_SKILL_REL_PATHS,...LEGACY_SKILL_REL_PATHS];function skillInstallCommand(env=process.env){return`${activeCliName(env)} skill install`}var SKILL_SOURCE_SLUG="Sma1lboy/rove";function bundledSkillDir(){let here=fileURLToPath(import.meta.url);return[resolve(here,"../../../../../.agents/skills/kobe"),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`${activeCliName()} skill install needs \`npx\` (part of Node.js), which isn't on your PATH.
3
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
4
  ${npxMissingMessage()}
5
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 kobeSkillPaths(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|kobe)-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 kobeSkillState(opts={}){let roots=[opts.home??homedir(),opts.cwd??process.cwd()],roveCopies=distinctSkillFiles(roots,ROVE_SKILL_REL_PATHS).map((path)=>({path,version:skillVersionAt(path)})),legacy=distinctSkillFiles(roots,LEGACY_SKILL_REL_PATHS).map((path)=>({path,version:skillVersionAt(path)})),best=[...roveCopies].sort((a,b)=>(b.version??-1)-(a.version??-1))[0]??legacy[0];if(!best)return{installed:!1,installedVersion:null,currentVersion:KOBE_SKILL_VERSION,stale:!1,legacyCopies:[],path:null};let stale=best.version===null||best.version<KOBE_SKILL_VERSION;return{installed:!0,installedVersion:best.version,currentVersion:KOBE_SKILL_VERSION,stale,legacyCopies:legacy.filter((copy)=>copy.path!==best.path),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 describeLegacyCopies(copies){return copies.map((c)=>`${c.path}${c.version===null?"":` (v${c.version})`}`).join(", ")}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-pqgq0452.js");if(isRovePluginEnabled())return;let cliName=activeCliName(),installCommand=skillInstallCommand(),state=kobeSkillState();if(!state.installed){if(getPersistedString(HINT_SEEN_KEY)==="1")return;setPersistedString(HINT_SEEN_KEY,"1"),process.stderr.write(`
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 kobeSkillPaths(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|kobe)-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 kobeSkillState(opts={}){let roots=[opts.home??homedir(),opts.cwd??process.cwd()],roveCopies=distinctSkillFiles(roots,ROVE_SKILL_REL_PATHS).map((path)=>({path,version:skillVersionAt(path)})),legacy=distinctSkillFiles(roots,LEGACY_SKILL_REL_PATHS).map((path)=>({path,version:skillVersionAt(path)})),best=[...roveCopies].sort((a,b)=>(b.version??-1)-(a.version??-1))[0]??legacy[0];if(!best)return{installed:!1,installedVersion:null,currentVersion:KOBE_SKILL_VERSION,stale:!1,legacyCopies:[],path:null};let stale=best.version===null||best.version<KOBE_SKILL_VERSION;return{installed:!0,installedVersion:best.version,currentVersion:KOBE_SKILL_VERSION,stale,legacyCopies:legacy.filter((copy)=>copy.path!==best.path),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 describeLegacyCopies(copies){return copies.map((c)=>`${c.path}${c.version===null?"":` (v${c.version})`}`).join(", ")}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-xf09hgzv.js");if(isRovePluginEnabled())return;let cliName=activeCliName(),installCommand=skillInstallCommand(),state=kobeSkillState();if(!state.installed){if(getPersistedString(HINT_SEEN_KEY)==="1")return;setPersistedString(HINT_SEEN_KEY,"1"),process.stderr.write(`
7
7
  ${cliName}: the Rove agent skill isn't installed \u2014 install it so your coding agent can drive Rove via \`${cliName} api\`:
8
8
  ${installCommand}
9
9
  (wraps \`${npxSkillsCommand()}\`; check anytime with \`${cliName} doctor\`)