agent-afk 3.73.0 → 3.73.1

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/dist/cli.mjs CHANGED
@@ -64,7 +64,7 @@ You are an inspector auditing skills for correct type categorization. Skills com
64
64
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/skills/<name>/SKILL.md\`
65
65
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/skills/<name>/SKILL.md\`
66
66
 
67
- The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
67
+ The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob to discover additional skills** (the list below is exhaustive); just read each path and emit a verdict. Targeted Glob of a known skill's own \`scripts/\`/\`references/\`/\`assets/\` subdirectories is allowed when needed \u2014 see Tools.
68
68
 
69
69
  ## Task
70
70
 
@@ -132,7 +132,7 @@ You are an inspector auditing commands for correct type categorization. Commands
132
132
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/commands/<name>.md\`
133
133
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/commands/<name>.md\`
134
134
 
135
- The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
135
+ The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob to discover additional commands** (the list below is exhaustive); just read each path and emit a verdict.
136
136
 
137
137
  ## Task
138
138
 
@@ -201,7 +201,7 @@ You are an inspector auditing agents for correct type categorization. Agents com
201
201
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/agents/<name>.md\`
202
202
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/agents/<name>.md\`
203
203
 
204
- The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
204
+ The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob to discover additional agents** (the list below is exhaustive); just read each path and emit a verdict.
205
205
 
206
206
  ## Task
207
207
 
@@ -1865,6 +1865,8 @@ ${JSON.stringify(e.buildResults,null,2)}
1865
1865
  Verification results:
1866
1866
  ${JSON.stringify(e.verifyResults,null,2)}
1867
1867
 
1868
+ Heal iterations used: ${e.healIterations}
1869
+
1868
1870
  Create a ship-ready summary with next steps.`,c=await a.runToResult(l);if(c.status!=="succeeded"||!c.message)throw new Error(`ship phase failed: ${Ce(c)}`);let u=e.buildResults?.filesChanged.length??0,d=e.healIterations;return Ro({kind:"checkpoint",title:"ship \u2014 done",body:[`Files changed: ${u}`,`Heal iterations: ${d}`,`Idea: ${e.idea}`]}),c.message.content}H();import{existsSync as kw,mkdirSync as AD,readFileSync as _D,unlinkSync as CD,writeFileSync as ID}from"fs";import{dirname as PD,join as MD}from"path";function op(e){return MD(_n(),e,"mint-state.json")}function vw(e,t){let n=op(e);AD(PD(n),{recursive:!0}),ID(n,JSON.stringify(t,null,2),"utf-8")}function OD(e){if(typeof e!="object"||e===null)return!1;let t=e;return typeof t.currentPhase=="string"&&typeof t.idea=="string"&&typeof t.spec=="string"&&typeof t.healIterations=="number"&&Array.isArray(t.history)}function Tw(e){let t=op(e);if(!kw(t))return null;try{let n=JSON.parse(_D(t,"utf-8"));return OD(n)?n:null}catch{return null}}function sp(e){let t=op(e);if(kw(t))try{CD(t)}catch{}}var $D=2,Ew=/^\s*(?:--continue(?:\s+(?:approved|yes|y))?|approved?|yes|y|lgtm|sure)\s*$/i,DD='To approve and run the rest of the pipeline, say "approve", "yes", "sure", or "lgtm" \u2014 or invoke /mint --continue approved. The handler will reload the spec state from disk.';function zt(e,t,n){e.history.push({phase:t,output:n,timestamp:Date.now()})}function _w(e){if("completed"in e&&"paused"in e)throw new Error("mint: invariant violation \u2014 MintResult carries both completed and paused keys simultaneously")}var xw=240;function LD(e){return e.length<=xw?e:e.slice(0,xw)+"\u2026"}function Cw(e){if(typeof e=="string"){if(Ew.test(e))return{userApproved:!0};if(e.length>1&&e.trimStart().startsWith("{"))try{let t=JSON.parse(e);if(typeof t=="object"&&t!==null)return Cw(t)}catch{}return{idea:e}}if(typeof e=="object"&&e!==null){let t=e,n=typeof t.idea=="string"?t.idea:void 0;if(n!==void 0&&Ew.test(n))return{userApproved:!0};if("idea"in t||"resumeFrom"in t||t.userApproved===!0)return t}throw new Error("mint handler requires input.idea (string), input as string, or {userApproved: true} to resume")}async function Rw(e,t,n){if(!t.sessionId)throw new Error("runPhasesAfterSpec requires parentSession.sessionId");let r=t.sessionId,o=t.cwd;try{e.currentPhase="research",e.research=await gw(e.spec,r,o,n),zt(e,"research",e.research),e.currentPhase="plan",e.plan=await hw(e.spec,e.research,r,o,n),zt(e,"plan",e.plan),e.currentPhase="parallelize";let s=await yw(e.plan,t,n);if(s.kind==="plan")e.waveOrchestrationPlan=s.plan,zt(e,"parallelize",JSON.stringify(s.plan));else if(s.kind==="skipped")e.waveOrchestrationPlan=void 0,zt(e,"parallelize",`skipped: ${s.reason}`);else if(s.kind==="failed"){e.waveOrchestrationPlan=void 0;let l=LD(s.error);zt(e,"parallelize",`failed: ${l}`),_e({event:"fallback.inline",parent_session_id:r,reason:"parallelize-dispatch-failed",error_message:l}),console.warn(`[mint] parallelize dispatch failed (single-lane fallback): ${l}`)}else{let l=s}e.currentPhase="build",e.buildResults=await bw(e.plan,e.waveOrchestrationPlan,r,o,n),zt(e,"build",JSON.stringify(e.buildResults)),e.currentPhase="verify",e.verifyResults=await Va(e.plan,e.buildResults,r,o,n),zt(e,"verify",JSON.stringify(e.verifyResults)),e.currentPhase="heal";let i=e.verifyResults.testsPassed&&e.verifyResults.lintPassed&&e.verifyResults.designReviewPassed;for(;!i&&e.healIterations<$D;){let l=await ww(e.plan,e.buildResults,e.verifyResults,e.healIterations,t,n);e.healIterations=l.newHealIterations,e.verifyResults=l.newVerifyResults,i=l.healed,zt(e,"heal",`Iterations: ${e.healIterations}, Success: ${i}`)}if(!i)return{paused:!0,phase:"heal-failed",reason:`Heal capped at ${e.healIterations} iterations; still have failures`,state:e,nextStep:"Heal loop exhausted. Inspect verifyResults, fix manually, then re-invoke /mint with a fresh idea \u2014 resume is not supported from heal-failed."};e.currentPhase="ship";let a=await Sw(e,r,o,n);return zt(e,"ship",a),{completed:!0,artifact:a,state:e}}catch(s){throw new Error(`mint failed at ${e.currentPhase}: ${s}`)}}function Aw(e,t){return _w(t),("completed"in t||t.phase==="heal-failed")&&sp(e),t}async function FD(e,t,n){let r=Cw(e);if(!t?.sessionId)throw new Error("mint handler requires a parent session to fork subagents");let o=t.sessionId,s=n?.callId;if(r.userApproved){let l=r.resumeFrom??Tw(o);if(!l)throw new Error("mint: no paused spec found for this session to continue. Run /mint <idea> first, then /mint --continue approved.");let c=await Rw(l,t,s);return Aw(o,c)}if(!r.idea)throw new Error("mint: no idea provided. Run /mint <idea> to start, or /mint --continue approved to resume a paused spec.");sp(o);let i={currentPhase:"spec",idea:r.idea,healIterations:0,history:[]};try{i.spec=await fw(r.idea,o,t.cwd,s),zt(i,"spec",i.spec)}catch(l){throw new Error(`mint failed at spec: ${l}`)}if(!r.autoApprove){vw(o,i);let l={paused:!0,phase:"spec",spec:i.spec,state:i,nextStep:DD};return _w(l),l}let a=await Rw(i,t,s);return Aw(o,a)}var ND={name:"mint",description:"Takes a feature idea or refactor scope and delivers a ship-ready, verified implementation end-to-end",handler:FD,argumentHint:"<idea> | --continue [approved]",whenToUse:'When the user wants a feature or refactor delivered end-to-end (spec \u2192 research \u2192 build \u2192 verify) in one ship-ready pass. After the spec phase pauses for approval, resume by invoking mint again with the literal string `"approved"` (or `"yes"`, `"lgtm"`, `"--continue approved"`) as the arguments. Equivalent JSON forms `{"userApproved": true}` and `{"idea": "approved"}` are also accepted. The handler reloads the spec state from disk and runs phases 2\u20138.',flags:["--continue"]};rt(ND);async function BD(){throw new Error("service-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/service-setup` slash command.")}var UD={name:"service-setup",description:"Install an AFK background process (telegram bot or daemon) as a macOS LaunchAgent so it auto-starts on login and relaunches on crash. Runs pre-flight checks (e.g., refuses to install the telegram service with an invalid token, which would otherwise crash-loop under KeepAlive), invokes `afk service install`, verifies with `afk service status`, and surfaces the management cheatsheet. macOS-only \u2014 gracefully refuses on other platforms.",handler:BD,context:"fork",whenToUse:"When the user wants to make `afk telegram start` or `afk daemon` always-on \u2014 i.e., survive reboot, crash, OOM. Triggers on phrasings like 'install as a service', 'auto-start on login', 'keep the bot running', 'launchd', 'always-on telegram', or right after a successful `/telegram-setup` when the user asks how to make it persistent."};rt(UD);async function jD(){throw new Error("telegram-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/telegram-setup` slash command.")}var WD={name:"telegram-setup",description:"Guide the user through first-time Telegram bot onboarding without leaking the bearer token. Walks the user to run `afk telegram setup` in a terminal for token entry, then uses the sanctioned `afk telegram check-token`/`discover-chat`/`set-allowed-chat` subcommands to validate and finish allowlist setup \u2014 the token never enters the model context. Works in REPL or Telegram. Use when the user wants to set up Telegram push notifications for the first time, or to debug a partially-configured install.",handler:jD,context:"fork",whenToUse:`When the user wants to set up Telegram bot notifications for the first time, or when they say something like "set up telegram", "connect telegram", "enable push", or you detect that TELEGRAM_BOT_TOKEN is unset and they're asking for notifications.`};rt(WD);H();import{readdirSync as KD,readFileSync as GD}from"fs";import{join as Mw}from"path";var HD=/(?<![a-zA-Z0-9_/-])--([a-z][a-z0-9-]*)(?![a-zA-Z0-9_-])/g;function Iw(e){return e.startsWith("--")?e:`--${e}`}function ip(e){let t=new Set;for(let n of e.matchAll(HD))n[1]&&t.add(`--${n[1]}`);return Array.from(t).sort()}function qs(e){if(!e.startsWith(`---
1869
1871
  `))return{frontmatter:null,frontmatterFlags:null,body:e};let t=e.indexOf(`
1870
1872
  ---
@@ -2225,7 +2227,7 @@ _ended: ${new Date().toISOString()}_
2225
2227
  `))!==-1;){let u=n.slice(0,c);n=n.slice(c+1),this.opts.writeLine(u)}},s={command:t,mode:"foreground",onChunk:c=>{r||(n+=c.toString("utf8"),o())}},i=this.opts.getCwd();i!==void 0&&(s.cwd=i);let{job:a,handle:l}=this.registry.start(s);this.activeFgJobId=a.id;try{let c=await l.promise;r=!0,n.length>0&&(this.opts.writeLine(n),n=""),this.opts.writeLine(sK(a,c)),this.queueInjection({command:t,mode:"foreground",result:c})}finally{this.activeFgJobId=null}}startBackground(t){let n={command:t,mode:"background"},r=this.opts.getCwd();r!==void 0&&(n.cwd=r);let{job:o}=this.registry.start(n);this.opts.writeLine(m.dim(` [${o.id}] background: `)+t)}queueInjection(t){this.pendingInjections.push(t),this.pendingInjections.length>e.MAX_PENDING_INJECTIONS&&this.pendingInjections.shift()}};function aK(e,t){if(e!==void 0){let n=e.toLowerCase();return!(n==="0"||n==="false"||n==="off"||n==="no")}return typeof t=="boolean"?t:!0}async function lK(e,t){if(e.firstTurnHook&&e.stats.totalTurns===0){let n=e.firstTurnHook;e.firstTurnHook=void 0;try{await n(t)}catch(r){e.completionWriter.fn(m.warning("\u26A0 ")+"first-turn hook failed: "+(r instanceof Error?r.message:String(r)))}}}function jc(e,t){let n=m.brand("afk")+m.dim(` (${e})`),r=t?m.warning(" \u25CF plan"):"";return n+r+m.dim(" \u203A ")}async function xx(e,t,n,r){let o=null,s=[];e.session.current.waitForInitialization().then(async v=>{je()&&(o=kl(v)),await mc(e.session.current),je()&&(s=yE())}).catch(()=>{});let i=await ax(),a=new Nc({rl:e.rl,history:i,statusLine:e.statusLine}),l,c,u,d,p,f,g,h=!1,b=!1,y=Av({onError:v=>V("[afk suggest] Tier-2 completion failed:",v)}),w=aK(T.AFK_SUGGEST_GHOST,e.suggestGhostConfig);try{await a.armCompositor({promptFn:()=>jc(e.stats.model,e.stats.planMode),onCancel:r,onShiftTab:()=>{let A=e.slashCtx;A.stats.planMode&&A.stats.pendingPlanExit?(A.stats.pendingPlanExit=!1,Pt(A,!1,{closureSummarySkipped:!0}).catch(()=>{})):Pt(A).catch(()=>{}),e.statusLine.rearm()},scrollRegion:e.statusLine,...e.preArmAnchorRow!==void 0?{anchorRow:e.preArmAnchorRow}:{},...w?{suggest:{engine:y,getContext:()=>({model:e.stats.model,apiKey:e.suggestApiKey,baseUrl:e.suggestBaseUrl,cwd:e.stats.cwd??process.cwd(),getHistory:()=>{let A=a.history;return A.getEntries?[...A.getEntries()]:[]},getDropdownTopCandidate:A=>{let $=a.autocompleteState.candidates[0];return $&&$.value.startsWith(A)&&$.value.length>A.length?$.value:null},getTranscriptTail:()=>"",getRecentCommands:()=>[],llmEnabled:()=>/^(1|true|yes|on)$/i.test(T.AFK_SUGGEST_ENABLED??"")})}}:{}});let v=a.getCompositor();jt.install(yl({readLine:A=>a.readLine({promptFn:()=>A}).then(L=>L.text),writer:{line:(A="")=>{let L=a.getCompositor();L?L.commitAbove(A):process.stdout.write(A+`
2226
2228
  `)}},pendingCount:()=>jt.pendingCount(),...v?{pickFromList:A=>px(v,A),readTextOverlay:A=>mx(v,A)}:{}})),e.replRenderer.setCompositor(a.getCompositor()),e.slashCtx.getCompositor=()=>a.getCompositor();let R=a.getCompositor();if(R){let A=L=>R.commitAbove(L);e.completionWriter.fn=A,e.completionWriter.idleFn=A}e.slashCtx.setSoftStopHandler=A=>a.setSoftStopHandler(A),e.inputSurfaceRef&&(e.inputSurfaceRef.current=a),c=bx(),g=wx(),e.clearVerdictLedger=()=>g?.reset(),u=new _l,Wk(u),Xk(u),ev(u),Zk(e.backgroundRegistry);let S=0,E=0,_=1,C=()=>e.statusLine.setExtraRows(_+S+E);g.setRowCountChangeHandler(A=>{E=A,C(),d?.redraw(),p?.redraw()}),d=new Bc(u,e.backgroundRegistry,{getAdjacentRows:()=>E}),d.setRowCountChangeHandler(A=>{S=A,C()}),p=new Yl({getExtraRows:()=>e.statusLine.getExtraRows()}),p.setRowCountChangeHandler(A=>{C()}),e.statusLine.setAfterScrollRestore(()=>{g?.repaint(),d?.redraw(),p?.redraw()}),d.start(),p.start(),g.start({stream:process.stdout});let P=50,M=[];for(u.on("complete",A=>{M.length>=P&&M.shift(),M.push(A)}),f=new Uc({writeLine:A=>e.replRenderer.writeLine(A),getCwd:()=>e.stats.cwd}),Vk(f),n.tryAbortShellForeground=()=>f.abortActiveForeground();;){if(o&&(e.replRenderer.writeLine(o),e.replRenderer.writeLine(""),o=null),s.length>0){for(let x of s)e.replRenderer.writeLine(x);e.replRenderer.writeLine(""),s=[]}for(;M.length>0;){let x=M.shift(),O=x.status==="succeeded"?"\u2713":"\u2717",U=[];if(x.resultText){let K=x.resultText.trim().split(`
2227
2229
  `)[0]?.slice(0,80)??"";K&&U.push(K)}x.error&&U.push(x.error.message);let J=[x.stats.toolUses>0?`${x.stats.toolUses} tools`:"",x.stats.tokens>0?`${Math.round(x.stats.tokens/1e3)}k tok`:"",x.stats.durationMs>0?`${Math.round(x.stats.durationMs/1e3)}s`:""].filter(Boolean).join(" \xB7 ");J&&U.push(J),e.replRenderer.writeLine(Mn({kind:x.status==="succeeded"?"checkpoint":"diagnosis",title:`${O} ${x.id} ${x.label}`,body:U})),e.replRenderer.writeLine("")}let A=f.drainNotifications();for(let{job:x,result:O}of A){let U=O.errorReason===void 0?"\u2713":"\u2717",J=O.errorReason==="abort"?"killed":O.errorReason==="timeout"?"timed out":O.errorReason==="signal-killed"?"killed by signal":`exit ${O.exitCode??0}`,K=Math.max(0,Math.round(O.durationMs/100)/10);e.replRenderer.writeLine(m.dim(` ${U} [${x.id}] ${J} \xB7 ${K}s \xB7 `)+x.command)}let L=c.renderIfChanged(e.stats.sessionId);if(L.length>0){for(let x of L)e.replRenderer.writeLine(x);e.replRenderer.writeLine("")}let $,I;if(l!==void 0){let x=l;l=void 0;let O=jc(e.stats.model,e.stats.planMode),U=Tr({buffer:x.text,promptText:O,isTTY:!!process.stdout.isTTY,attachmentSummary:jo([...x.attachments])});e.replRenderer.writeLine(U),$=x.text.trim(),I=x.attachments}else{let x=await a.readLine({promptFn:()=>jc(e.stats.model,e.stats.planMode),onSigint:r,onShiftTab:()=>{let O=e.slashCtx;O.stats.planMode&&O.stats.pendingPlanExit?(O.stats.pendingPlanExit=!1,Pt(O,!1,{closureSummarySkipped:!0}).catch(()=>{})):Pt(O).catch(()=>{}),e.statusLine.rearm()}});$=x.text.trim(),I=x.attachments}if(!$&&I.length===0)continue;if($.startsWith("!")){let x=/^(0|false|off|no)$/i.test(T.AFK_SHELL_PASSTHROUGH??"");if(e.options.shellPassthrough!==!1&&!x&&(h||(h=!0,e.replRenderer.writeLine(m.dim(" \u2139 ! prefix shells out. Pass --no-shell-passthrough (or set AFK_SHELL_PASSTHROUGH=0) to send ! text to the model instead."))),await f.dispatch($))){e.statusLine.rearm();continue}}let F=!1;if($.startsWith("/")){let x=await rk($,e.slashCtx,I);if(x.handled){if(x.result==="exit"){e.rl.close();return}if(($==="/clear"||$.startsWith("/clear "))&&(await t.rotateOnClear(),e.replRenderer.writeLine(m.dim(` transcript: ${t.path()}`)),g.reset()),x.result!==null&&typeof x.result=="object"&&"kind"in x.result&&x.result.kind==="submit"){l={text:x.result.message,attachments:I??[]},e.statusLine.rearm();continue}e.statusLine.rearm();continue}F=!0}i.push($),await lK(e,$);let j=$;if(F){let x=Rp($);if(x){let O=x.name.replace(/^\//,"").split(":").pop()??"";if(O&&_m(O)){let U={skillName:O,rawArgs:x.args,source:"plugin",capabilities:{compose:!0,subagents:!0}},J=e.session.current.sessionId,K=Or(J),N=Date.now();V(`[afk trace] preflight.start commandName=${O}`);let Y=!1,be=await Mr(U,{cwd:e.stats.cwd??process.cwd(),artifactDir:K},Be=>{je()&&e.replRenderer.writeLine(m.warning(`\u26A0 preflight(${O}) failed: `)+(Be instanceof Error?Be.message:String(Be)))});Y=be!==null,V(`[afk trace] preflight.end commandName=${O} durationMs=${Date.now()-N} success=${Y}`),j=Pm(be?.manifestBlock,$)}}}let B=f.drainInjections();B.length>0&&(j=B+j),await yx({text:j,attachments:I},e.session.current,e.stats,{setInFlight(x){n.turnInFlight=x},async onTurnComplete(x,O){if(await t.appendTurn(x,O),e.stats.sessionId)try{qt(e.stats)}catch(U){b||(b=!0,e.replRenderer.writeLine(m.warning("\u26A0 ")+"session autosave failed \u2014 this conversation may not be resumable: "+(U instanceof Error?U.message:String(U))))}},async onAfterTurn(){await e.contextSampler.onTurn(e.stats.totalTurns),await yk(e.slashCtx),e.statusLine.rearm(),p?.repaint("observing")},rearmStatus:()=>e.statusLine.rearm(),onTerminalState:x=>g?.push(x),setActiveCompositor:x=>{n.activeCompositor=x},setInterruptNotifier:x=>{n.notifyInterrupting=x},scrollRegion:e.statusLine,getCompositor:()=>a.getCompositor(),setBackgroundHandler:x=>a.setBackgroundHandler(x),setSoftStopHandler:x=>a.setSoftStopHandler(x),async onContextProgress(){await e.contextSampler.refresh(),e.statusLine.repaint(vr(e.stats,e.contextSampler))},...p?{onStageChange:x=>p.repaint(x)}:{}},e.options.thinkingUi,e.completionWriter,u,a.toRunTurnRefs(jc(e.stats.model,e.stats.planMode)))}}finally{if(u!==void 0)for(let R of u.running())u.cancel(R.id);n.tryAbortShellForeground=null,f?.drainOnExit(),p?.stop(),d?.stop(),g?.stop(),c?.dispose();let v=R=>console.log(R);e.completionWriter.fn=v,e.completionWriter.idleFn=v,await a.dispose(),e.inputSurfaceRef&&(e.inputSurfaceRef.current=null)}}import{execFile as cK}from"node:child_process";import{dirname as uK,isAbsolute as dK,resolve as pK}from"node:path";import{promisify as mK}from"node:util";var Rx=mK(cK),fK=3e3,gK=new Set(["empty","orphaned-dir","orphaned-registration","dead-owner"]);async function hK(){let t=(await Rx("git",["rev-parse","--git-common-dir"])).stdout.trim();if(!t)throw new Error("Not in a git repository.");let n=dK(t)?t:pK(process.cwd(),t);return uK(n)}async function Ax(e){if(e?.disabled)return{ran:!1,removedCount:0,skippedReason:"disabled"};let t;try{t=await hK()}catch{return{ran:!1,removedCount:0,skippedReason:"not-in-repo"}}let n,r=new Promise(o=>{n=setTimeout(()=>o("timeout"),fK)});try{let o=Xt({execFile:Rx,repoRoot:t,dryRun:!1,scope:"interactive",bypassSoftLaunch:!0}),s=await Promise.race([o,r]);if(s==="timeout")return{ran:!1,removedCount:0,skippedReason:"timeout"};let i=s;return i.warnings.some(c=>c.toLowerCase().includes("contested"))?{ran:!1,removedCount:0,skippedReason:"lock-contested"}:{ran:!0,removedCount:i.candidates.filter(c=>gK.has(c.verdict)&&i.removed.includes(c.path)).length}}catch{return{ran:!1,removedCount:0,skippedReason:"error"}}finally{n&&clearTimeout(n)}}import{promises as yK}from"node:fs";import{dirname as bK,join as Ix}from"node:path";import{randomBytes as wK}from"node:crypto";var SK=["Generate a 2-4 word kebab-case slug describing this work request.","Rules:","- ASCII lowercase letters and digits only, separated by single hyphens","- 2 to 4 hyphen-separated words","- Maximum 30 characters total","- No prefix, no quotes, no punctuation other than hyphens","- Output ONLY the slug \u2014 no explanation, no preamble","Examples: fix-cleanup-race, add-telegram-allowlist, refactor-prompt-loader, debug-flaky-test"].join(`
2228
- `),_x=/^[a-z0-9]+(-[a-z0-9]+){1,3}$/,nf=30,kK=1024,vK=8e3,TK="haiku";async function EK(e,t){let n=e.trim();if(n.length===0)return t.onSkip?.("empty-message"),null;if(n.startsWith("/"))return t.onSkip?.("slash-command"),null;let r=_K(n,kK),o=new AbortController,s=setTimeout(()=>o.abort(),t.timeoutMs??vK),i=t.signal?CK([t.signal,o.signal]):o.signal,a;try{t.slugGenerator?a=await t.slugGenerator(r,i):a=await to({token:t.token,model:t.model??TK,system:SK,user:r,maxTokens:32,signal:i})}catch(d){let p=d instanceof Error?d.message:String(d);return t.onSkip?.("slug-generator-error",p.slice(0,200)),null}finally{clearTimeout(s)}let l=xK(a);if(l===null)return t.onSkip?.("invalid-slug-output",a.slice(0,60)),null;let c=bK(t.worktreePath);return await RK(l,c)}function xK(e){let t=e.trim().toLowerCase();if(t.length===0)return null;if(_x.test(t)&&t.length<=nf)return t;let n=t.replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");if(n.length===0)return null;let r=n.split("-").filter(s=>s.length>0).slice(0,4);if(r.length<2)return null;let o=r[0];for(let s=1;s<r.length;s++){let i=`${o}-${r[s]}`;if(i.length>nf)break;o=i}return _x.test(o)?o:null}async function RK(e,t){if(!await AK(Ix(t,e)))return e;let n=wK(2).toString("hex");return`${e.split("-").slice(0,3).join("-").slice(0,nf-5)}-${n}`}async function AK(e){try{return await yK.access(e),!0}catch{return!1}}function _K(e,t){let n=Buffer.from(e,"utf8");if(n.length<=t)return e;let r=t;for(;r>0&&n[r]!==void 0&&(n[r]&192)===128;)r--;return n.slice(0,r).toString("utf8")}function CK(e){let t=AbortSignal.any;if(typeof t=="function")return t.call(AbortSignal,e);let n=new AbortController;for(let r of e){if(r.aborted)return n.abort(r.reason),n.signal;r.addEventListener("abort",()=>n.abort(r.reason),{once:!0})}return n.signal}async function Px(e){let t,n,r=Ix(e.deferred.repoRoot,".afk-worktrees","unnamed"),o=await EK(e.message,{token:e.token,...e.model!==void 0?{model:e.model}:{},...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},worktreePath:r,...e.signal!==void 0?{signal:e.signal}:{},...e.slugGenerator!==void 0?{slugGenerator:e.slugGenerator}:{},onSkip:(a,l)=>{t=a,n=l}}),s=t??"unknown",i=n;if(o!==null){let l=`${Ga(e.branchPrefix)}${o}`;try{let c=await e.deferred.create(l);return Cx(e.session,c.path),{status:"created",path:c.path,branch:c.branch,slug:o}}catch(c){s="create-failed",i=(c instanceof Error?c.message:String(c)).slice(0,200)}}try{let a=await e.deferred.create(!0);return Cx(e.session,a.path),{status:"created-fallback",path:a.path,branch:a.branch,reason:s,...i!==void 0?{detail:i}:{}}}catch(a){return{status:"failed",reason:a instanceof Error?a.message:String(a)}}}function Cx(e,t){e&&e.setCwd(t),IK(t)}function IK(e){try{process.chdir(e)}catch{}}H();import{spawn as Mx}from"child_process";import{existsSync as DK,mkdirSync as LK,readFileSync as Ox,unlinkSync as FK,writeFileSync as NK}from"fs";import{get as BK}from"https";import{join as $x}from"path";import{readFileSync as PK}from"fs";import{dirname as MK,join as OK}from"path";import{fileURLToPath as $K}from"url";function En(){try{return"3.73.0"}catch{}try{let e=MK($K(import.meta.url));for(let t of["../../package.json","../package.json"])try{let n=JSON.parse(PK(OK(e,t),"utf-8"));if(typeof n.version=="string")return n.version}catch{}}catch{}return"0.0.0-unknown"}G();var UK=64*1024,jK=1440*60*1e3,WK="update-check.json",HK="pending-update.json";function Dx(){return $x(qi(),WK)}function rf(){return $x(qi(),HK)}function Lx(){let e=qi();DK(e)||LK(e,{recursive:!0})}function KK(e,t){let n=e.split(".").map(Number),r=t.split(".").map(Number),o=Math.max(n.length,r.length);for(let s=0;s<o;s++){let i=n[s]??0,a=r[s]??0;if(a>i)return!0;if(a<i)return!1}return!1}function GK(){try{let e=Ox(Dx(),"utf-8"),t=JSON.parse(e);if(typeof t.latestVersion=="string"&&typeof t.checkedAt=="number")return t}catch{}return null}function zK(){try{Lx();let e=`
2230
+ `),_x=/^[a-z0-9]+(-[a-z0-9]+){1,3}$/,nf=30,kK=1024,vK=8e3,TK="haiku";async function EK(e,t){let n=e.trim();if(n.length===0)return t.onSkip?.("empty-message"),null;if(n.startsWith("/"))return t.onSkip?.("slash-command"),null;let r=_K(n,kK),o=new AbortController,s=setTimeout(()=>o.abort(),t.timeoutMs??vK),i=t.signal?CK([t.signal,o.signal]):o.signal,a;try{t.slugGenerator?a=await t.slugGenerator(r,i):a=await to({token:t.token,model:t.model??TK,system:SK,user:r,maxTokens:32,signal:i})}catch(d){let p=d instanceof Error?d.message:String(d);return t.onSkip?.("slug-generator-error",p.slice(0,200)),null}finally{clearTimeout(s)}let l=xK(a);if(l===null)return t.onSkip?.("invalid-slug-output",a.slice(0,60)),null;let c=bK(t.worktreePath);return await RK(l,c)}function xK(e){let t=e.trim().toLowerCase();if(t.length===0)return null;if(_x.test(t)&&t.length<=nf)return t;let n=t.replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");if(n.length===0)return null;let r=n.split("-").filter(s=>s.length>0).slice(0,4);if(r.length<2)return null;let o=r[0];for(let s=1;s<r.length;s++){let i=`${o}-${r[s]}`;if(i.length>nf)break;o=i}return _x.test(o)?o:null}async function RK(e,t){if(!await AK(Ix(t,e)))return e;let n=wK(2).toString("hex");return`${e.split("-").slice(0,3).join("-").slice(0,nf-5)}-${n}`}async function AK(e){try{return await yK.access(e),!0}catch{return!1}}function _K(e,t){let n=Buffer.from(e,"utf8");if(n.length<=t)return e;let r=t;for(;r>0&&n[r]!==void 0&&(n[r]&192)===128;)r--;return n.slice(0,r).toString("utf8")}function CK(e){let t=AbortSignal.any;if(typeof t=="function")return t.call(AbortSignal,e);let n=new AbortController;for(let r of e){if(r.aborted)return n.abort(r.reason),n.signal;r.addEventListener("abort",()=>n.abort(r.reason),{once:!0})}return n.signal}async function Px(e){let t,n,r=Ix(e.deferred.repoRoot,".afk-worktrees","unnamed"),o=await EK(e.message,{token:e.token,...e.model!==void 0?{model:e.model}:{},...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},worktreePath:r,...e.signal!==void 0?{signal:e.signal}:{},...e.slugGenerator!==void 0?{slugGenerator:e.slugGenerator}:{},onSkip:(a,l)=>{t=a,n=l}}),s=t??"unknown",i=n;if(o!==null){let l=`${Ga(e.branchPrefix)}${o}`;try{let c=await e.deferred.create(l);return Cx(e.session,c.path),{status:"created",path:c.path,branch:c.branch,slug:o}}catch(c){s="create-failed",i=(c instanceof Error?c.message:String(c)).slice(0,200)}}try{let a=await e.deferred.create(!0);return Cx(e.session,a.path),{status:"created-fallback",path:a.path,branch:a.branch,reason:s,...i!==void 0?{detail:i}:{}}}catch(a){return{status:"failed",reason:a instanceof Error?a.message:String(a)}}}function Cx(e,t){e&&e.setCwd(t),IK(t)}function IK(e){try{process.chdir(e)}catch{}}H();import{spawn as Mx}from"child_process";import{existsSync as DK,mkdirSync as LK,readFileSync as Ox,unlinkSync as FK,writeFileSync as NK}from"fs";import{get as BK}from"https";import{join as $x}from"path";import{readFileSync as PK}from"fs";import{dirname as MK,join as OK}from"path";import{fileURLToPath as $K}from"url";function En(){try{return"3.73.1"}catch{}try{let e=MK($K(import.meta.url));for(let t of["../../package.json","../package.json"])try{let n=JSON.parse(PK(OK(e,t),"utf-8"));if(typeof n.version=="string")return n.version}catch{}}catch{}return"0.0.0-unknown"}G();var UK=64*1024,jK=1440*60*1e3,WK="update-check.json",HK="pending-update.json";function Dx(){return $x(qi(),WK)}function rf(){return $x(qi(),HK)}function Lx(){let e=qi();DK(e)||LK(e,{recursive:!0})}function KK(e,t){let n=e.split(".").map(Number),r=t.split(".").map(Number),o=Math.max(n.length,r.length);for(let s=0;s<o;s++){let i=n[s]??0,a=r[s]??0;if(a>i)return!0;if(a<i)return!1}return!1}function GK(){try{let e=Ox(Dx(),"utf-8"),t=JSON.parse(e);if(typeof t.latestVersion=="string"&&typeof t.checkedAt=="number")return t}catch{}return null}function zK(){try{Lx();let e=`
2229
2231
  const https = require('https');
2230
2232
  const fs = require('fs');
2231
2233
  const url = 'https://registry.npmjs.org/agent-afk/latest';
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ You are an inspector auditing skills for correct type categorization. Skills com
7
7
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/skills/<name>/SKILL.md\`
8
8
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/skills/<name>/SKILL.md\`
9
9
 
10
- The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
10
+ The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob to discover additional skills** (the list below is exhaustive); just read each path and emit a verdict. Targeted Glob of a known skill's own \`scripts/\`/\`references/\`/\`assets/\` subdirectories is allowed when needed \u2014 see Tools.
11
11
 
12
12
  ## Task
13
13
 
@@ -75,7 +75,7 @@ You are an inspector auditing commands for correct type categorization. Commands
75
75
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/commands/<name>.md\`
76
76
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/commands/<name>.md\`
77
77
 
78
- The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
78
+ The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob to discover additional commands** (the list below is exhaustive); just read each path and emit a verdict.
79
79
 
80
80
  ## Task
81
81
 
@@ -144,7 +144,7 @@ You are an inspector auditing agents for correct type categorization. Agents com
144
144
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/agents/<name>.md\`
145
145
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/agents/<name>.md\`
146
146
 
147
- The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
147
+ The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob to discover additional agents** (the list below is exhaustive); just read each path and emit a verdict.
148
148
 
149
149
  ## Task
150
150
 
@@ -1565,6 +1565,8 @@ ${JSON.stringify(t.buildResults,null,2)}
1565
1565
  Verification results:
1566
1566
  ${JSON.stringify(t.verifyResults,null,2)}
1567
1567
 
1568
+ Heal iterations used: ${t.healIterations}
1569
+
1568
1570
  Create a ship-ready summary with next steps.`,l=await a.runToResult(c);if(l.status!=="succeeded"||!l.message)throw new Error(`ship phase failed: ${z(l)}`);let d=t.buildResults?.filesChanged.length??0,u=t.healIterations;return ut({kind:"checkpoint",title:"ship \u2014 done",body:[`Files changed: ${d}`,`Heal iterations: ${u}`,`Idea: ${t.idea}`]}),l.message.content}U();import{existsSync as Ja,mkdirSync as Jg,readFileSync as Vg,unlinkSync as Yg,writeFileSync as Xg}from"fs";import{dirname as Qg,join as Zg}from"path";function no(t){return Zg(Et(),t,"mint-state.json")}function Va(t,e){let n=no(t);Jg(Qg(n),{recursive:!0}),Xg(n,JSON.stringify(e,null,2),"utf-8")}function eh(t){if(typeof t!="object"||t===null)return!1;let e=t;return typeof e.currentPhase=="string"&&typeof e.idea=="string"&&typeof e.spec=="string"&&typeof e.healIterations=="number"&&Array.isArray(e.history)}function Ya(t){let e=no(t);if(!Ja(e))return null;try{let n=JSON.parse(Vg(e,"utf-8"));return eh(n)?n:null}catch{return null}}function ro(t){let e=no(t);if(Ja(e))try{Yg(e)}catch{}}var th=2,Xa=/^\s*(?:--continue(?:\s+(?:approved|yes|y))?|approved?|yes|y|lgtm|sure)\s*$/i,nh='To approve and run the rest of the pipeline, say "approve", "yes", "sure", or "lgtm" \u2014 or invoke /mint --continue approved. The handler will reload the spec state from disk.';function ye(t,e,n){t.history.push({phase:e,output:n,timestamp:Date.now()})}function tc(t){if("completed"in t&&"paused"in t)throw new Error("mint: invariant violation \u2014 MintResult carries both completed and paused keys simultaneously")}var Qa=240;function rh(t){return t.length<=Qa?t:t.slice(0,Qa)+"\u2026"}function nc(t){if(typeof t=="string"){if(Xa.test(t))return{userApproved:!0};if(t.length>1&&t.trimStart().startsWith("{"))try{let e=JSON.parse(t);if(typeof e=="object"&&e!==null)return nc(e)}catch{}return{idea:t}}if(typeof t=="object"&&t!==null){let e=t,n=typeof e.idea=="string"?e.idea:void 0;if(n!==void 0&&Xa.test(n))return{userApproved:!0};if("idea"in e||"resumeFrom"in e||e.userApproved===!0)return e}throw new Error("mint handler requires input.idea (string), input as string, or {userApproved: true} to resume")}async function Za(t,e,n){if(!e.sessionId)throw new Error("runPhasesAfterSpec requires parentSession.sessionId");let r=e.sessionId,o=e.cwd;try{t.currentPhase="research",t.research=await ja(t.spec,r,o,n),ye(t,"research",t.research),t.currentPhase="plan",t.plan=await Wa(t.spec,t.research,r,o,n),ye(t,"plan",t.plan),t.currentPhase="parallelize";let s=await Ka(t.plan,e,n);if(s.kind==="plan")t.waveOrchestrationPlan=s.plan,ye(t,"parallelize",JSON.stringify(s.plan));else if(s.kind==="skipped")t.waveOrchestrationPlan=void 0,ye(t,"parallelize",`skipped: ${s.reason}`);else if(s.kind==="failed"){t.waveOrchestrationPlan=void 0;let c=rh(s.error);ye(t,"parallelize",`failed: ${c}`),ae({event:"fallback.inline",parent_session_id:r,reason:"parallelize-dispatch-failed",error_message:c}),console.warn(`[mint] parallelize dispatch failed (single-lane fallback): ${c}`)}else{let c=s}t.currentPhase="build",t.buildResults=await Ga(t.plan,t.waveOrchestrationPlan,r,o,n),ye(t,"build",JSON.stringify(t.buildResults)),t.currentPhase="verify",t.verifyResults=await Cn(t.plan,t.buildResults,r,o,n),ye(t,"verify",JSON.stringify(t.verifyResults)),t.currentPhase="heal";let i=t.verifyResults.testsPassed&&t.verifyResults.lintPassed&&t.verifyResults.designReviewPassed;for(;!i&&t.healIterations<th;){let c=await qa(t.plan,t.buildResults,t.verifyResults,t.healIterations,e,n);t.healIterations=c.newHealIterations,t.verifyResults=c.newVerifyResults,i=c.healed,ye(t,"heal",`Iterations: ${t.healIterations}, Success: ${i}`)}if(!i)return{paused:!0,phase:"heal-failed",reason:`Heal capped at ${t.healIterations} iterations; still have failures`,state:t,nextStep:"Heal loop exhausted. Inspect verifyResults, fix manually, then re-invoke /mint with a fresh idea \u2014 resume is not supported from heal-failed."};t.currentPhase="ship";let a=await za(t,r,o,n);return ye(t,"ship",a),{completed:!0,artifact:a,state:t}}catch(s){throw new Error(`mint failed at ${t.currentPhase}: ${s}`)}}function ec(t,e){return tc(e),("completed"in e||e.phase==="heal-failed")&&ro(t),e}async function oh(t,e,n){let r=nc(t);if(!e?.sessionId)throw new Error("mint handler requires a parent session to fork subagents");let o=e.sessionId,s=n?.callId;if(r.userApproved){let c=r.resumeFrom??Ya(o);if(!c)throw new Error("mint: no paused spec found for this session to continue. Run /mint <idea> first, then /mint --continue approved.");let l=await Za(c,e,s);return ec(o,l)}if(!r.idea)throw new Error("mint: no idea provided. Run /mint <idea> to start, or /mint --continue approved to resume a paused spec.");ro(o);let i={currentPhase:"spec",idea:r.idea,healIterations:0,history:[]};try{i.spec=await Ha(r.idea,o,e.cwd,s),ye(i,"spec",i.spec)}catch(c){throw new Error(`mint failed at spec: ${c}`)}if(!r.autoApprove){Va(o,i);let c={paused:!0,phase:"spec",spec:i.spec,state:i,nextStep:nh};return tc(c),c}let a=await Za(i,e,s);return ec(o,a)}var sh={name:"mint",description:"Takes a feature idea or refactor scope and delivers a ship-ready, verified implementation end-to-end",handler:oh,argumentHint:"<idea> | --continue [approved]",whenToUse:'When the user wants a feature or refactor delivered end-to-end (spec \u2192 research \u2192 build \u2192 verify) in one ship-ready pass. After the spec phase pauses for approval, resume by invoking mint again with the literal string `"approved"` (or `"yes"`, `"lgtm"`, `"--continue approved"`) as the arguments. Equivalent JSON forms `{"userApproved": true}` and `{"idea": "approved"}` are also accepted. The handler reloads the spec state from disk and runs phases 2\u20138.',flags:["--continue"]};Z(sh);async function ih(){throw new Error("service-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/service-setup` slash command.")}var ah={name:"service-setup",description:"Install an AFK background process (telegram bot or daemon) as a macOS LaunchAgent so it auto-starts on login and relaunches on crash. Runs pre-flight checks (e.g., refuses to install the telegram service with an invalid token, which would otherwise crash-loop under KeepAlive), invokes `afk service install`, verifies with `afk service status`, and surfaces the management cheatsheet. macOS-only \u2014 gracefully refuses on other platforms.",handler:ih,context:"fork",whenToUse:"When the user wants to make `afk telegram start` or `afk daemon` always-on \u2014 i.e., survive reboot, crash, OOM. Triggers on phrasings like 'install as a service', 'auto-start on login', 'keep the bot running', 'launchd', 'always-on telegram', or right after a successful `/telegram-setup` when the user asks how to make it persistent."};Z(ah);async function ch(){throw new Error("telegram-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/telegram-setup` slash command.")}var lh={name:"telegram-setup",description:"Guide the user through first-time Telegram bot onboarding without leaking the bearer token. Walks the user to run `afk telegram setup` in a terminal for token entry, then uses the sanctioned `afk telegram check-token`/`discover-chat`/`set-allowed-chat` subcommands to validate and finish allowlist setup \u2014 the token never enters the model context. Works in REPL or Telegram. Use when the user wants to set up Telegram push notifications for the first time, or to debug a partially-configured install.",handler:ch,context:"fork",whenToUse:`When the user wants to set up Telegram bot notifications for the first time, or when they say something like "set up telegram", "connect telegram", "enable push", or you detect that TELEGRAM_BOT_TOKEN is unset and they're asking for notifications.`};Z(lh);U();import{readdirSync as ph,readFileSync as fh}from"fs";import{join as sc}from"path";var dh=/(?<![a-zA-Z0-9_/-])--([a-z][a-z0-9-]*)(?![a-zA-Z0-9_-])/g;function rc(t){return t.startsWith("--")?t:`--${t}`}function uh(t){let e=new Set;for(let n of t.matchAll(dh))n[1]&&e.add(`--${n[1]}`);return Array.from(e).sort()}function oo(t){if(!t.startsWith(`---
1569
1571
  `))return{frontmatter:null,frontmatterFlags:null,body:t};let e=t.indexOf(`
1570
1572
  ---
package/dist/telegram.mjs CHANGED
@@ -134,7 +134,7 @@ You are an inspector auditing skills for correct type categorization. Skills com
134
134
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/skills/<name>/SKILL.md\`
135
135
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/skills/<name>/SKILL.md\`
136
136
 
137
- The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
137
+ The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob to discover additional skills** (the list below is exhaustive); just read each path and emit a verdict. Targeted Glob of a known skill's own \`scripts/\`/\`references/\`/\`assets/\` subdirectories is allowed when needed \u2014 see Tools.
138
138
 
139
139
  ## Task
140
140
 
@@ -202,7 +202,7 @@ You are an inspector auditing commands for correct type categorization. Commands
202
202
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/commands/<name>.md\`
203
203
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/commands/<name>.md\`
204
204
 
205
- The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
205
+ The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob to discover additional commands** (the list below is exhaustive); just read each path and emit a verdict.
206
206
 
207
207
  ## Task
208
208
 
@@ -271,7 +271,7 @@ You are an inspector auditing agents for correct type categorization. Agents com
271
271
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/agents/<name>.md\`
272
272
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/agents/<name>.md\`
273
273
 
274
- The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
274
+ The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob to discover additional agents** (the list below is exhaustive); just read each path and emit a verdict.
275
275
 
276
276
  ## Task
277
277
 
@@ -1593,6 +1593,8 @@ ${JSON.stringify(t.buildResults,null,2)}
1593
1593
  Verification results:
1594
1594
  ${JSON.stringify(t.verifyResults,null,2)}
1595
1595
 
1596
+ Heal iterations used: ${t.healIterations}
1597
+
1596
1598
  Create a ship-ready summary with next steps.`,l=await a.runToResult(c);if(l.status!=="succeeded"||!l.message)throw new Error(`ship phase failed: ${V(l)}`);let d=t.buildResults?.filesChanged.length??0,u=t.healIterations;return vt({kind:"checkpoint",title:"ship \u2014 done",body:[`Files changed: ${d}`,`Heal iterations: ${u}`,`Idea: ${t.idea}`]}),l.message.content}U();import{existsSync as ml,mkdirSync as db,readFileSync as ub,unlinkSync as pb,writeFileSync as fb}from"fs";import{dirname as mb,join as gb}from"path";function No(t){return gb(Me(),t,"mint-state.json")}function gl(t,e){let n=No(t);db(mb(n),{recursive:!0}),fb(n,JSON.stringify(e,null,2),"utf-8")}function hb(t){if(typeof t!="object"||t===null)return!1;let e=t;return typeof e.currentPhase=="string"&&typeof e.idea=="string"&&typeof e.spec=="string"&&typeof e.healIterations=="number"&&Array.isArray(e.history)}function hl(t){let e=No(t);if(!ml(e))return null;try{let n=JSON.parse(ub(e,"utf-8"));return hb(n)?n:null}catch{return null}}function $o(t){let e=No(t);if(ml(e))try{pb(e)}catch{}}var yb=2,yl=/^\s*(?:--continue(?:\s+(?:approved|yes|y))?|approved?|yes|y|lgtm|sure)\s*$/i,bb='To approve and run the rest of the pipeline, say "approve", "yes", "sure", or "lgtm" \u2014 or invoke /mint --continue approved. The handler will reload the spec state from disk.';function ve(t,e,n){t.history.push({phase:e,output:n,timestamp:Date.now()})}function kl(t){if("completed"in t&&"paused"in t)throw new Error("mint: invariant violation \u2014 MintResult carries both completed and paused keys simultaneously")}var bl=240;function wb(t){return t.length<=bl?t:t.slice(0,bl)+"\u2026"}function vl(t){if(typeof t=="string"){if(yl.test(t))return{userApproved:!0};if(t.length>1&&t.trimStart().startsWith("{"))try{let e=JSON.parse(t);if(typeof e=="object"&&e!==null)return vl(e)}catch{}return{idea:t}}if(typeof t=="object"&&t!==null){let e=t,n=typeof e.idea=="string"?e.idea:void 0;if(n!==void 0&&yl.test(n))return{userApproved:!0};if("idea"in e||"resumeFrom"in e||e.userApproved===!0)return e}throw new Error("mint handler requires input.idea (string), input as string, or {userApproved: true} to resume")}async function wl(t,e,n){if(!e.sessionId)throw new Error("runPhasesAfterSpec requires parentSession.sessionId");let r=e.sessionId,o=e.cwd;try{t.currentPhase="research",t.research=await cl(t.spec,r,o,n),ve(t,"research",t.research),t.currentPhase="plan",t.plan=await ll(t.spec,t.research,r,o,n),ve(t,"plan",t.plan),t.currentPhase="parallelize";let s=await dl(t.plan,e,n);if(s.kind==="plan")t.waveOrchestrationPlan=s.plan,ve(t,"parallelize",JSON.stringify(s.plan));else if(s.kind==="skipped")t.waveOrchestrationPlan=void 0,ve(t,"parallelize",`skipped: ${s.reason}`);else if(s.kind==="failed"){t.waveOrchestrationPlan=void 0;let c=wb(s.error);ve(t,"parallelize",`failed: ${c}`),Y({event:"fallback.inline",parent_session_id:r,reason:"parallelize-dispatch-failed",error_message:c}),console.warn(`[mint] parallelize dispatch failed (single-lane fallback): ${c}`)}else{let c=s}t.currentPhase="build",t.buildResults=await ul(t.plan,t.waveOrchestrationPlan,r,o,n),ve(t,"build",JSON.stringify(t.buildResults)),t.currentPhase="verify",t.verifyResults=await or(t.plan,t.buildResults,r,o,n),ve(t,"verify",JSON.stringify(t.verifyResults)),t.currentPhase="heal";let i=t.verifyResults.testsPassed&&t.verifyResults.lintPassed&&t.verifyResults.designReviewPassed;for(;!i&&t.healIterations<yb;){let c=await pl(t.plan,t.buildResults,t.verifyResults,t.healIterations,e,n);t.healIterations=c.newHealIterations,t.verifyResults=c.newVerifyResults,i=c.healed,ve(t,"heal",`Iterations: ${t.healIterations}, Success: ${i}`)}if(!i)return{paused:!0,phase:"heal-failed",reason:`Heal capped at ${t.healIterations} iterations; still have failures`,state:t,nextStep:"Heal loop exhausted. Inspect verifyResults, fix manually, then re-invoke /mint with a fresh idea \u2014 resume is not supported from heal-failed."};t.currentPhase="ship";let a=await fl(t,r,o,n);return ve(t,"ship",a),{completed:!0,artifact:a,state:t}}catch(s){throw new Error(`mint failed at ${t.currentPhase}: ${s}`)}}function Sl(t,e){return kl(e),("completed"in e||e.phase==="heal-failed")&&$o(t),e}async function Sb(t,e,n){let r=vl(t);if(!e?.sessionId)throw new Error("mint handler requires a parent session to fork subagents");let o=e.sessionId,s=n?.callId;if(r.userApproved){let c=r.resumeFrom??hl(o);if(!c)throw new Error("mint: no paused spec found for this session to continue. Run /mint <idea> first, then /mint --continue approved.");let l=await wl(c,e,s);return Sl(o,l)}if(!r.idea)throw new Error("mint: no idea provided. Run /mint <idea> to start, or /mint --continue approved to resume a paused spec.");$o(o);let i={currentPhase:"spec",idea:r.idea,healIterations:0,history:[]};try{i.spec=await al(r.idea,o,e.cwd,s),ve(i,"spec",i.spec)}catch(c){throw new Error(`mint failed at spec: ${c}`)}if(!r.autoApprove){gl(o,i);let c={paused:!0,phase:"spec",spec:i.spec,state:i,nextStep:bb};return kl(c),c}let a=await wl(i,e,s);return Sl(o,a)}var kb={name:"mint",description:"Takes a feature idea or refactor scope and delivers a ship-ready, verified implementation end-to-end",handler:Sb,argumentHint:"<idea> | --continue [approved]",whenToUse:'When the user wants a feature or refactor delivered end-to-end (spec \u2192 research \u2192 build \u2192 verify) in one ship-ready pass. After the spec phase pauses for approval, resume by invoking mint again with the literal string `"approved"` (or `"yes"`, `"lgtm"`, `"--continue approved"`) as the arguments. Equivalent JSON forms `{"userApproved": true}` and `{"idea": "approved"}` are also accepted. The handler reloads the spec state from disk and runs phases 2\u20138.',flags:["--continue"]};re(kb);async function vb(){throw new Error("service-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/service-setup` slash command.")}var _b={name:"service-setup",description:"Install an AFK background process (telegram bot or daemon) as a macOS LaunchAgent so it auto-starts on login and relaunches on crash. Runs pre-flight checks (e.g., refuses to install the telegram service with an invalid token, which would otherwise crash-loop under KeepAlive), invokes `afk service install`, verifies with `afk service status`, and surfaces the management cheatsheet. macOS-only \u2014 gracefully refuses on other platforms.",handler:vb,context:"fork",whenToUse:"When the user wants to make `afk telegram start` or `afk daemon` always-on \u2014 i.e., survive reboot, crash, OOM. Triggers on phrasings like 'install as a service', 'auto-start on login', 'keep the bot running', 'launchd', 'always-on telegram', or right after a successful `/telegram-setup` when the user asks how to make it persistent."};re(_b);async function Eb(){throw new Error("telegram-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/telegram-setup` slash command.")}var Ab={name:"telegram-setup",description:"Guide the user through first-time Telegram bot onboarding without leaking the bearer token. Walks the user to run `afk telegram setup` in a terminal for token entry, then uses the sanctioned `afk telegram check-token`/`discover-chat`/`set-allowed-chat` subcommands to validate and finish allowlist setup \u2014 the token never enters the model context. Works in REPL or Telegram. Use when the user wants to set up Telegram push notifications for the first time, or to debug a partially-configured install.",handler:Eb,context:"fork",whenToUse:`When the user wants to set up Telegram bot notifications for the first time, or when they say something like "set up telegram", "connect telegram", "enable push", or you detect that TELEGRAM_BOT_TOKEN is unset and they're asking for notifications.`};re(Ab);U();import{readdirSync as Rb,readFileSync as Ib}from"fs";import{join as Al}from"path";var xb=/(?<![a-zA-Z0-9_/-])--([a-z][a-z0-9-]*)(?![a-zA-Z0-9_-])/g;function _l(t){return t.startsWith("--")?t:`--${t}`}function Tb(t){let e=new Set;for(let n of t.matchAll(xb))n[1]&&e.add(`--${n[1]}`);return Array.from(e).sort()}function Uo(t){if(!t.startsWith(`---
1597
1599
  `))return{frontmatter:null,frontmatterFlags:null,body:t};let e=t.indexOf(`
1598
1600
  ---
package/dist/threads.mjs CHANGED
@@ -122,7 +122,7 @@ You are an inspector auditing skills for correct type categorization. Skills com
122
122
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/skills/<name>/SKILL.md\`
123
123
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/skills/<name>/SKILL.md\`
124
124
 
125
- The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
125
+ The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob to discover additional skills** (the list below is exhaustive); just read each path and emit a verdict. Targeted Glob of a known skill's own \`scripts/\`/\`references/\`/\`assets/\` subdirectories is allowed when needed \u2014 see Tools.
126
126
 
127
127
  ## Task
128
128
 
@@ -190,7 +190,7 @@ You are an inspector auditing commands for correct type categorization. Commands
190
190
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/commands/<name>.md\`
191
191
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/commands/<name>.md\`
192
192
 
193
- The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
193
+ The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob to discover additional commands** (the list below is exhaustive); just read each path and emit a verdict.
194
194
 
195
195
  ## Task
196
196
 
@@ -259,7 +259,7 @@ You are an inspector auditing agents for correct type categorization. Agents com
259
259
  - **User-scope** \u2014 authored directly by the user under \`~/.afk/agents/<name>.md\`
260
260
  - **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/agents/<name>.md\`
261
261
 
262
- The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
262
+ The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob to discover additional agents** (the list below is exhaustive); just read each path and emit a verdict.
263
263
 
264
264
  ## Task
265
265
 
@@ -1580,6 +1580,8 @@ ${JSON.stringify(t.buildResults,null,2)}
1580
1580
  Verification results:
1581
1581
  ${JSON.stringify(t.verifyResults,null,2)}
1582
1582
 
1583
+ Heal iterations used: ${t.healIterations}
1584
+
1583
1585
  Create a ship-ready summary with next steps.`,l=await a.runToResult(c);if(l.status!=="succeeded"||!l.message)throw new Error(`ship phase failed: ${G(l)}`);let d=t.buildResults?.filesChanged.length??0,u=t.healIterations;return gt({kind:"checkpoint",title:"ship \u2014 done",body:[`Files changed: ${d}`,`Heal iterations: ${u}`,`Idea: ${t.idea}`]}),l.message.content}$();import{existsSync as Tc,mkdirSync as ly,readFileSync as dy,unlinkSync as uy,writeFileSync as py}from"fs";import{dirname as fy,join as my}from"path";function so(t){return my(wt(),t,"mint-state.json")}function Rc(t,e){let n=so(t);ly(fy(n),{recursive:!0}),py(n,JSON.stringify(e,null,2),"utf-8")}function gy(t){if(typeof t!="object"||t===null)return!1;let e=t;return typeof e.currentPhase=="string"&&typeof e.idea=="string"&&typeof e.spec=="string"&&typeof e.healIterations=="number"&&Array.isArray(e.history)}function Ic(t){let e=so(t);if(!Tc(e))return null;try{let n=JSON.parse(dy(e,"utf-8"));return gy(n)?n:null}catch{return null}}function io(t){let e=so(t);if(Tc(e))try{uy(e)}catch{}}var hy=2,Pc=/^\s*(?:--continue(?:\s+(?:approved|yes|y))?|approved?|yes|y|lgtm|sure)\s*$/i,yy='To approve and run the rest of the pipeline, say "approve", "yes", "sure", or "lgtm" \u2014 or invoke /mint --continue approved. The handler will reload the spec state from disk.';function Se(t,e,n){t.history.push({phase:e,output:n,timestamp:Date.now()})}function Dc(t){if("completed"in t&&"paused"in t)throw new Error("mint: invariant violation \u2014 MintResult carries both completed and paused keys simultaneously")}var Cc=240;function by(t){return t.length<=Cc?t:t.slice(0,Cc)+"\u2026"}function Lc(t){if(typeof t=="string"){if(Pc.test(t))return{userApproved:!0};if(t.length>1&&t.trimStart().startsWith("{"))try{let e=JSON.parse(t);if(typeof e=="object"&&e!==null)return Lc(e)}catch{}return{idea:t}}if(typeof t=="object"&&t!==null){let e=t,n=typeof e.idea=="string"?e.idea:void 0;if(n!==void 0&&Pc.test(n))return{userApproved:!0};if("idea"in e||"resumeFrom"in e||e.userApproved===!0)return e}throw new Error("mint handler requires input.idea (string), input as string, or {userApproved: true} to resume")}async function Oc(t,e,n){if(!e.sessionId)throw new Error("runPhasesAfterSpec requires parentSession.sessionId");let r=e.sessionId,o=e.cwd;try{t.currentPhase="research",t.research=await Sc(t.spec,r,o,n),Se(t,"research",t.research),t.currentPhase="plan",t.plan=await vc(t.spec,t.research,r,o,n),Se(t,"plan",t.plan),t.currentPhase="parallelize";let s=await _c(t.plan,e,n);if(s.kind==="plan")t.waveOrchestrationPlan=s.plan,Se(t,"parallelize",JSON.stringify(s.plan));else if(s.kind==="skipped")t.waveOrchestrationPlan=void 0,Se(t,"parallelize",`skipped: ${s.reason}`);else if(s.kind==="failed"){t.waveOrchestrationPlan=void 0;let c=by(s.error);Se(t,"parallelize",`failed: ${c}`),J({event:"fallback.inline",parent_session_id:r,reason:"parallelize-dispatch-failed",error_message:c}),console.warn(`[mint] parallelize dispatch failed (single-lane fallback): ${c}`)}else{let c=s}t.currentPhase="build",t.buildResults=await Ec(t.plan,t.waveOrchestrationPlan,r,o,n),Se(t,"build",JSON.stringify(t.buildResults)),t.currentPhase="verify",t.verifyResults=await Un(t.plan,t.buildResults,r,o,n),Se(t,"verify",JSON.stringify(t.verifyResults)),t.currentPhase="heal";let i=t.verifyResults.testsPassed&&t.verifyResults.lintPassed&&t.verifyResults.designReviewPassed;for(;!i&&t.healIterations<hy;){let c=await Ac(t.plan,t.buildResults,t.verifyResults,t.healIterations,e,n);t.healIterations=c.newHealIterations,t.verifyResults=c.newVerifyResults,i=c.healed,Se(t,"heal",`Iterations: ${t.healIterations}, Success: ${i}`)}if(!i)return{paused:!0,phase:"heal-failed",reason:`Heal capped at ${t.healIterations} iterations; still have failures`,state:t,nextStep:"Heal loop exhausted. Inspect verifyResults, fix manually, then re-invoke /mint with a fresh idea \u2014 resume is not supported from heal-failed."};t.currentPhase="ship";let a=await xc(t,r,o,n);return Se(t,"ship",a),{completed:!0,artifact:a,state:t}}catch(s){throw new Error(`mint failed at ${t.currentPhase}: ${s}`)}}function Mc(t,e){return Dc(e),("completed"in e||e.phase==="heal-failed")&&io(t),e}async function wy(t,e,n){let r=Lc(t);if(!e?.sessionId)throw new Error("mint handler requires a parent session to fork subagents");let o=e.sessionId,s=n?.callId;if(r.userApproved){let c=r.resumeFrom??Ic(o);if(!c)throw new Error("mint: no paused spec found for this session to continue. Run /mint <idea> first, then /mint --continue approved.");let l=await Oc(c,e,s);return Mc(o,l)}if(!r.idea)throw new Error("mint: no idea provided. Run /mint <idea> to start, or /mint --continue approved to resume a paused spec.");io(o);let i={currentPhase:"spec",idea:r.idea,healIterations:0,history:[]};try{i.spec=await kc(r.idea,o,e.cwd,s),Se(i,"spec",i.spec)}catch(c){throw new Error(`mint failed at spec: ${c}`)}if(!r.autoApprove){Rc(o,i);let c={paused:!0,phase:"spec",spec:i.spec,state:i,nextStep:yy};return Dc(c),c}let a=await Oc(i,e,s);return Mc(o,a)}var ky={name:"mint",description:"Takes a feature idea or refactor scope and delivers a ship-ready, verified implementation end-to-end",handler:wy,argumentHint:"<idea> | --continue [approved]",whenToUse:'When the user wants a feature or refactor delivered end-to-end (spec \u2192 research \u2192 build \u2192 verify) in one ship-ready pass. After the spec phase pauses for approval, resume by invoking mint again with the literal string `"approved"` (or `"yes"`, `"lgtm"`, `"--continue approved"`) as the arguments. Equivalent JSON forms `{"userApproved": true}` and `{"idea": "approved"}` are also accepted. The handler reloads the spec state from disk and runs phases 2\u20138.',flags:["--continue"]};Q(ky);async function Sy(){throw new Error("service-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/service-setup` slash command.")}var vy={name:"service-setup",description:"Install an AFK background process (telegram bot or daemon) as a macOS LaunchAgent so it auto-starts on login and relaunches on crash. Runs pre-flight checks (e.g., refuses to install the telegram service with an invalid token, which would otherwise crash-loop under KeepAlive), invokes `afk service install`, verifies with `afk service status`, and surfaces the management cheatsheet. macOS-only \u2014 gracefully refuses on other platforms.",handler:Sy,context:"fork",whenToUse:"When the user wants to make `afk telegram start` or `afk daemon` always-on \u2014 i.e., survive reboot, crash, OOM. Triggers on phrasings like 'install as a service', 'auto-start on login', 'keep the bot running', 'launchd', 'always-on telegram', or right after a successful `/telegram-setup` when the user asks how to make it persistent."};Q(vy);async function _y(){throw new Error("telegram-setup is a fork skill; its handler should never be called directly. Invoke via the `skill` tool or `/telegram-setup` slash command.")}var Ey={name:"telegram-setup",description:"Guide the user through first-time Telegram bot onboarding without leaking the bearer token. Walks the user to run `afk telegram setup` in a terminal for token entry, then uses the sanctioned `afk telegram check-token`/`discover-chat`/`set-allowed-chat` subcommands to validate and finish allowlist setup \u2014 the token never enters the model context. Works in REPL or Telegram. Use when the user wants to set up Telegram push notifications for the first time, or to debug a partially-configured install.",handler:_y,context:"fork",whenToUse:`When the user wants to set up Telegram bot notifications for the first time, or when they say something like "set up telegram", "connect telegram", "enable push", or you detect that TELEGRAM_BOT_TOKEN is unset and they're asking for notifications.`};Q(Ey);$();import{readdirSync as Ty,readFileSync as Ry}from"fs";import{join as $c}from"path";var Ay=/(?<![a-zA-Z0-9_/-])--([a-z][a-z0-9-]*)(?![a-zA-Z0-9_-])/g;function Fc(t){return t.startsWith("--")?t:`--${t}`}function xy(t){let e=new Set;for(let n of t.matchAll(Ay))n[1]&&e.add(`--${n[1]}`);return Array.from(e).sort()}function ao(t){if(!t.startsWith(`---
1584
1586
  `))return{frontmatter:null,frontmatterFlags:null,body:t};let e=t.indexOf(`
1585
1587
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-afk",
3
- "version": "3.73.0",
3
+ "version": "3.73.1",
4
4
  "description": "CLI tool for interacting with AI agents via multiple interfaces",
5
5
  "main": "dist/index.mjs",
6
6
  "type": "module",