agent-afk 3.80.6 → 3.80.7

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.
@@ -10,7 +10,7 @@ Self-referential meta-capability questions about the current repository, framewo
10
10
 
11
11
  - "What does this repo enable?"
12
12
  - "What are the orchestration patterns available?"
13
- - "List the skills in agent-framework-private."
13
+ - "List the skills in private-framework."
14
14
  - "What capabilities does the plugin provide?"
15
15
  - "Show me what the framework can do."
16
16
 
package/dist/cli.mjs CHANGED
@@ -1141,7 +1141,7 @@ Unless the dispatcher specifies a different schema, return:
1141
1141
  **\`boundary_flag\` is required.** If nothing applies, emit \`"none"\` \u2014 do not omit the field. Treat missing as \`"none"\` is acceptable on the orchestrator side, but emit the field explicitly so downstream synthesizers and validators do not see \`null\`.
1142
1142
 
1143
1143
  If \`scope_check\` flags implementation (non-git), the orchestrator should dispatch a different sub-agent type for follow-up. Do not re-dispatch the same task through \`research-agent\`.
1144
- `,sourcePath:"agent-framework-private/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};W();W();import{existsSync as Ln,readdirSync as e$,readFileSync as t$}from"fs";import{join as dn}from"path";W();import{existsSync as Fd,readFileSync as YO,readdirSync as VO,statSync as XO}from"fs";import{join as Ws,resolve as kb}from"path";W();import{existsSync as bb,mkdirSync as HO,readFileSync as WO,renameSync as KO,writeFileSync as GO,unlinkSync as zO}from"fs";import{dirname as yb,join as qO}from"path";import{randomBytes as JO}from"crypto";function ge(e=ae()){if(!bb(e))return Ua();try{let t=WO(e,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return Ua();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return Ua()}catch{return Ua()}}function js(e,t=ae()){HO(yb(t),{recursive:!0});let n=qO(yb(t),`.index.json.${process.pid}.${JO(4).toString("hex")}.tmp`),r=JSON.stringify(e,null,2);try{GO(n,r,"utf8"),KO(n,t)}catch(o){try{bb(n)&&zO(n)}catch{}throw o}}function Dn(e,t,n=ae()){let r=ge(n);return r.plugins[e]=t,js(r,n),r}function wb(e,t=ae()){let n=ge(t);return e in n.plugins&&(delete n.plugins[e],js(n,t)),n}function Ld(e,t,n=ae()){let r=ge(n),o=r.plugins[e];if(!o)throw new Error(`plugin "${e}" is not in the index`);return o.enabled=t,o.updatedAt=new Date().toISOString(),js(r,n),r}function Hs(e,t,n=ae()){let r=ge(n);return r.marketplaces[e]=t,js(r,n),r}function Sb(e,t=ae()){let n=ge(t),r=!1;e in n.marketplaces&&(delete n.marketplaces[e],r=!0);for(let[o,s]of Object.entries(n.plugins))s.marketplace===e&&(delete n.plugins[o],r=!0);return r&&js(n,t),n}function Ua(){return{version:2,plugins:{},marketplaces:{}}}var ZO=5,vb="cache",go;function un(){go=void 0}function Rt(e=Me()){go||(go=new Map);let t=go.get(e);if(t)return[...t];if(!Fd(e))return go.set(e,[]),[];let n=e===Me()?ae():Ws(e,".index.json"),r=ge(n),o=[];return Tb(e,e,0,o,new Set,r.plugins),go.set(e,o),[...o]}function Tb(e,t,n,r,o,s){if(n>ZO||o.has(t))return;if(o.add(t),Fd(Ws(t,".claude-plugin","plugin.json"))){let a=Nd(e,t);if(a===null){r.push({type:"local",path:t});return}if(a.layout==="cache"){let c=s[a.key];if(!c||c.enabled===!1)return;r.push({type:"local",path:t});return}let l=s[a.key];if(l&&l.enabled===!1)return;r.push({type:"local",path:t});return}let i;try{i=VO(t)}catch{return}for(let a of i){if(a.startsWith("."))continue;let l=Ws(t,a),c;try{c=XO(l)}catch{continue}c.isDirectory()&&Tb(e,l,n+1,r,o,s)}}function Nd(e,t){if(!t.startsWith(e))return null;let n=t.slice(e.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===vb&&r.length>=3){let s=r[1];if(s){let i=Ws(e,vb,s),l=QO(i,t)??r[2];if(l)return{layout:"cache",key:`${s}:${l}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function QO(e,t){let n=Ws(e,".claude-plugin","marketplace.json");if(!Fd(n))return null;let r;try{r=JSON.parse(YO(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=kb(t);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&kb(e,a.source)===s)return a.name}return null}var xb=["command","agent"];function Eb(e=Ie()){let t=[],n=dn(e,"skills");if(Ln(n))for(let r of ja(n)){let o=dn(n,r,"SKILL.md");Ln(o)&&t.push({path:o,type:"skill",source:"user"})}for(let r of xb){let o=dn(e,`${r}s`);if(Ln(o))for(let s of ja(o))s.endsWith(".md")&&t.push({path:dn(o,s),type:r,source:"user"})}return t}function Rb(e=Me()){if(!Ln(e))return[];let t=[],n=Rt(e);for(let r of n){let s=Nd(e,r.path)?.key,i=dn(r.path,"skills");if(Ln(i))for(let a of ja(i)){let l=dn(i,a,"SKILL.md");if(!Ln(l))continue;let c={path:l,type:"skill",source:"plugin"};s&&(c.plugin_key=s),t.push(c)}for(let a of xb){let l=dn(r.path,`${a}s`);if(Ln(l))for(let c of ja(l)){if(!c.endsWith(".md"))continue;let u={path:dn(l,c),type:a,source:"plugin"};s&&(u.plugin_key=s),t.push(u)}}}return t}function Ab(e=dn(Ie(),"settings.json")){if(!Ln(e))return[];try{let t=t$(e,"utf8"),r=JSON.parse(t).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function ja(e){try{return e$(e).filter(t=>!t.startsWith("."))}catch{return[]}}var Pb=xe.object({path:xe.string(),type:xe.enum(["skill","command","agent","hook"]),source:xe.enum(["user","plugin"]),plugin_key:xe.string().optional(),verdict:xe.enum(["correct","misfit","outlier"]),recommended_type:xe.string(),rationale:xe.string(),confidence:xe.enum(["high","med","low"])}),Ib=xe.record(xe.string(),xe.record(xe.string(),xe.number())),P7=xe.object({inventory:xe.object({user:Ib,plugin:Ib}),misfits:xe.array(Pb),briefs_written:xe.number(),total_artifacts:xe.number()}),n$=xe.object({writeBriefs:xe.boolean().optional(),scope:xe.enum(["user","plugin","all"]).optional()}),r$=["skill","command","agent"],Mb=["skill","command","agent","hook"];function o$(e){return{runUserDiscovery:e!=="plugin",runPluginDiscovery:e!=="user",runHookInspector:e!=="plugin"}}function s$(e){let t=()=>{let s={};for(let i of Mb)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:t(),plugin:t()};for(let s of e)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=e.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function i$(e){return e.verdict==="misfit"&&e.confidence==="high"&&e.source==="user"}function a$(e){let t=e.filter(o=>o.source==="user"),n=e.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),t.length===0)r.push("(none discovered)");else for(let o of t)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1144
+ `,sourcePath:"private-framework/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};W();W();import{existsSync as Ln,readdirSync as e$,readFileSync as t$}from"fs";import{join as dn}from"path";W();import{existsSync as Fd,readFileSync as YO,readdirSync as VO,statSync as XO}from"fs";import{join as Ws,resolve as kb}from"path";W();import{existsSync as bb,mkdirSync as HO,readFileSync as WO,renameSync as KO,writeFileSync as GO,unlinkSync as zO}from"fs";import{dirname as yb,join as qO}from"path";import{randomBytes as JO}from"crypto";function ge(e=ae()){if(!bb(e))return Ua();try{let t=WO(e,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return Ua();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return Ua()}catch{return Ua()}}function js(e,t=ae()){HO(yb(t),{recursive:!0});let n=qO(yb(t),`.index.json.${process.pid}.${JO(4).toString("hex")}.tmp`),r=JSON.stringify(e,null,2);try{GO(n,r,"utf8"),KO(n,t)}catch(o){try{bb(n)&&zO(n)}catch{}throw o}}function Dn(e,t,n=ae()){let r=ge(n);return r.plugins[e]=t,js(r,n),r}function wb(e,t=ae()){let n=ge(t);return e in n.plugins&&(delete n.plugins[e],js(n,t)),n}function Ld(e,t,n=ae()){let r=ge(n),o=r.plugins[e];if(!o)throw new Error(`plugin "${e}" is not in the index`);return o.enabled=t,o.updatedAt=new Date().toISOString(),js(r,n),r}function Hs(e,t,n=ae()){let r=ge(n);return r.marketplaces[e]=t,js(r,n),r}function Sb(e,t=ae()){let n=ge(t),r=!1;e in n.marketplaces&&(delete n.marketplaces[e],r=!0);for(let[o,s]of Object.entries(n.plugins))s.marketplace===e&&(delete n.plugins[o],r=!0);return r&&js(n,t),n}function Ua(){return{version:2,plugins:{},marketplaces:{}}}var ZO=5,vb="cache",go;function un(){go=void 0}function Rt(e=Me()){go||(go=new Map);let t=go.get(e);if(t)return[...t];if(!Fd(e))return go.set(e,[]),[];let n=e===Me()?ae():Ws(e,".index.json"),r=ge(n),o=[];return Tb(e,e,0,o,new Set,r.plugins),go.set(e,o),[...o]}function Tb(e,t,n,r,o,s){if(n>ZO||o.has(t))return;if(o.add(t),Fd(Ws(t,".claude-plugin","plugin.json"))){let a=Nd(e,t);if(a===null){r.push({type:"local",path:t});return}if(a.layout==="cache"){let c=s[a.key];if(!c||c.enabled===!1)return;r.push({type:"local",path:t});return}let l=s[a.key];if(l&&l.enabled===!1)return;r.push({type:"local",path:t});return}let i;try{i=VO(t)}catch{return}for(let a of i){if(a.startsWith("."))continue;let l=Ws(t,a),c;try{c=XO(l)}catch{continue}c.isDirectory()&&Tb(e,l,n+1,r,o,s)}}function Nd(e,t){if(!t.startsWith(e))return null;let n=t.slice(e.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===vb&&r.length>=3){let s=r[1];if(s){let i=Ws(e,vb,s),l=QO(i,t)??r[2];if(l)return{layout:"cache",key:`${s}:${l}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function QO(e,t){let n=Ws(e,".claude-plugin","marketplace.json");if(!Fd(n))return null;let r;try{r=JSON.parse(YO(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=kb(t);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&kb(e,a.source)===s)return a.name}return null}var xb=["command","agent"];function Eb(e=Ie()){let t=[],n=dn(e,"skills");if(Ln(n))for(let r of ja(n)){let o=dn(n,r,"SKILL.md");Ln(o)&&t.push({path:o,type:"skill",source:"user"})}for(let r of xb){let o=dn(e,`${r}s`);if(Ln(o))for(let s of ja(o))s.endsWith(".md")&&t.push({path:dn(o,s),type:r,source:"user"})}return t}function Rb(e=Me()){if(!Ln(e))return[];let t=[],n=Rt(e);for(let r of n){let s=Nd(e,r.path)?.key,i=dn(r.path,"skills");if(Ln(i))for(let a of ja(i)){let l=dn(i,a,"SKILL.md");if(!Ln(l))continue;let c={path:l,type:"skill",source:"plugin"};s&&(c.plugin_key=s),t.push(c)}for(let a of xb){let l=dn(r.path,`${a}s`);if(Ln(l))for(let c of ja(l)){if(!c.endsWith(".md"))continue;let u={path:dn(l,c),type:a,source:"plugin"};s&&(u.plugin_key=s),t.push(u)}}}return t}function Ab(e=dn(Ie(),"settings.json")){if(!Ln(e))return[];try{let t=t$(e,"utf8"),r=JSON.parse(t).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function ja(e){try{return e$(e).filter(t=>!t.startsWith("."))}catch{return[]}}var Pb=xe.object({path:xe.string(),type:xe.enum(["skill","command","agent","hook"]),source:xe.enum(["user","plugin"]),plugin_key:xe.string().optional(),verdict:xe.enum(["correct","misfit","outlier"]),recommended_type:xe.string(),rationale:xe.string(),confidence:xe.enum(["high","med","low"])}),Ib=xe.record(xe.string(),xe.record(xe.string(),xe.number())),P7=xe.object({inventory:xe.object({user:Ib,plugin:Ib}),misfits:xe.array(Pb),briefs_written:xe.number(),total_artifacts:xe.number()}),n$=xe.object({writeBriefs:xe.boolean().optional(),scope:xe.enum(["user","plugin","all"]).optional()}),r$=["skill","command","agent"],Mb=["skill","command","agent","hook"];function o$(e){return{runUserDiscovery:e!=="plugin",runPluginDiscovery:e!=="user",runHookInspector:e!=="plugin"}}function s$(e){let t=()=>{let s={};for(let i of Mb)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:t(),plugin:t()};for(let s of e)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=e.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function i$(e){return e.verdict==="misfit"&&e.confidence==="high"&&e.source==="user"}function a$(e){let t=e.filter(o=>o.source==="user"),n=e.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),t.length===0)r.push("(none discovered)");else for(let o of t)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1145
1145
  `)}function l$(e,t){let n=["","## Discovered hooks (audit only these)",""];if(n.push(`Settings file (use this absolute path verbatim in each verdict's \`path\` field): \`${e}\``),n.push(""),t.length===0)return n.push("(no hooks discovered)"),n.join(`
1146
1146
  `);for(let r of t){let o=`${r.event}-${r.index}`;n.push(`### Hook \`${o}\``),n.push(""),n.push("```json"),n.push(JSON.stringify(r.raw,null,2)),n.push("```"),n.push("")}return n.join(`
1147
1147
  `)}function c$(e,t){if(!t)return{kind:"failure",message:`${e}: no result`};if(t.schemaError)return{kind:"failure",message:`${e}: schema mismatch \u2014 ${t.schemaError.message}`};if(t.status!=="succeeded"){let n=t.error?` \u2014 ${t.error.message}`:"";return{kind:"failure",message:`${e}: ${t.status}${n}`}}return t.output?{kind:"success",output:t.output}:{kind:"failure",message:`${e}: no output`}}async function u$(e,t,n){let r=n?.apiKey,o=n?.callId,s=typeof e=="object"&&e!==null?e:{},i=n$.parse(s),a=i.writeBriefs??!0,l=i.scope??"all",c=o$(l);if(!t?.sessionId)throw new Error("audit-fit requires a parent session with sessionId");let u=t.sessionId,d=Ce("audit-fit"),p={skill:d["01-skill-inspector.md"],command:d["02-command-inspector.md"],agent:d["03-agent-inspector.md"],hook:d["04-hook-inspector.md"]};for(let L of Mb)if(!p[L])throw new Error(`audit-fit skill missing inspector prompt for ${L}`);let f=c.runUserDiscovery?Eb():[],g=c.runPluginDiscovery?Rb():[],h={skill:[],command:[],agent:[]};for(let L of[...f,...g])h[L.type].push(L);let b=new te({apiKey:r}),y=()=>async L=>bt.allowedTools.includes(L)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${L} not allowed for audit-fit inspectors. Allowed tools: ${bt.allowedTools.join(", ")}`},w=[];for(let L of r$){let D=h[L];if(D.length===0)continue;let j=p[L];j&&w.push({type:L,prompt:`${j}
@@ -1173,7 +1173,7 @@ ${D.rationale}
1173
1173
  ---
1174
1174
  Generated by audit-fit on ${new Date().toISOString().split(".")[0]}Z
1175
1175
  `;await Cb(N,K),R++}}let C=Mt();await _b(C,{recursive:!0});let A=L=>{let D=0;for(let j of Object.values(L))for(let N of Object.values(j))D+=N;return D},P=L=>{let D=E.user[L]??{},j=E.plugin[L]??{},N=K=>Object.values(K).reduce((M,x)=>M+x,0);return N(D)+N(j)},I={timestamp:new Date().toISOString(),surface:"afk",scope:l,total_artifacts:S.length,misfits_count:k.length,briefs_written:R,by_source:{user:A(E.user),plugin:A(E.plugin)},by_type:{skill:P("skill"),command:P("command"),agent:P("agent"),hook:P("hook")}},_=Bd(C,"audit-fit-telemetry.jsonl");return await Cb(_,JSON.stringify(I)+`
1176
- `),{inventory:E,misfits:k,briefs_written:R,total_artifacts:S.length}}var d$={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:u$,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};mt(d$);import{z as X}from"zod";import{execFile as g$}from"node:child_process";import{promisify as h$}from"node:util";import{tmpdir as y$}from"node:os";import{join as Lb}from"node:path";function Ob(e){return e.confidence<.5?{verify:!0,reason:`low confidence (${e.confidence.toFixed(2)} < ${.5})`}:e.boundary_flag&&e.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${e.boundary_flag}`}:e.coverage_gaps&&e.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${e.coverage_gaps.length===1?"":"s"}: ${e.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${e.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as p$}from"node:url";import{dirname as m$}from"node:path";var f$=p$(import.meta.url),F7=m$(f$),$b={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"agent-framework-private/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function Db(e){let t={description:e.description,prompt:e.systemPrompt};return e.allowedTools&&(t.tools=[...e.allowedTools]),e.model&&(t.model=e.model),t}function b$(e){let t=e.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(t)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(t)?"REFUTED":"INCONCLUSIVE"}var w$=X.object({verifications:X.array(X.object({claim:X.string().optional(),verdict:X.string(),evidence:X.string().optional()}))});function S$(e){let t=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<e.length;i++){let a=e[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(t.push(e.slice(r,i+1)),r=-1))}return t}function k$(e){let t=S$(e);for(let n of t){let r;try{r=JSON.parse(n)}catch{continue}let o=w$.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:b$(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${t.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${e.slice(0,300)}`)}var Ha=h$(g$),Bb=X.object({id:X.string(),claim:X.string(),confidence:X.number().min(0).max(1),evidence_sources:X.array(X.string()),location:X.string().optional(),proposed_fix:X.string().optional(),coverage_gaps:X.array(X.string()).nullish().transform(e=>e??void 0),boundary_flag:X.string().nullish().transform(e=>e??void 0)}),v$=X.object({hypothesis_id:X.string(),claim:X.string(),verdict:X.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:X.string(),gate_reason:X.string()}),Ub=X.object({hypothesis_id:X.string(),reproducer_passed:X.boolean(),regressions:X.array(X.string()),confidence:X.number().min(0).max(1),verification_log:X.string()}),T$=X.enum(["crash","regression","logic-error","flaky","environment","unknown"]),x$=X.object({failure_type:T$,error_signature:X.string(),affected_area:X.string()}),E$=X.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),eZ=X.object({reproducer:X.string().optional(),triage:x$.optional(),hypotheses:X.array(Bb),premise_verifications:X.array(v$).optional(),winner:X.object({hypothesis_id:X.string(),verification_log:X.string(),proposed_fix:X.string()}).optional(),verification_results:X.array(Ub).optional(),outcome:E$.optional(),recommended_next_skill:X.enum(["spec"]).optional()});async function R$(e,t){let n=e.map(l=>({hypothesis:l,decision:Ob(l)})).filter(l=>l.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:e};let r=[],o;try{let l=await t(n.map(c=>c.hypothesis.claim));r=Array.isArray(l)?l:[]}catch(l){o=l instanceof Error?l.message:String(l)}let s=n.map((l,c)=>{let u=r[c];return o!==void 0?{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:l.decision.reason}:u?{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:u.verdict,evidence:u.evidence,gate_reason:l.decision.reason}:{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:l.decision.reason}}),i=new Set(s.filter(l=>l.verdict==="REFUTED").map(l=>l.hypothesis_id)),a=i.size===0?e:e.filter(l=>!i.has(l.id));return{premise_verifications:s,hypotheses_to_test:a}}async function A$(e,t,n){let r=n?.apiKey,o=(()=>{if(typeof e=="string")return{failure:e,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof e=="object"&&e!==null){let H=e;if(typeof H.failure=="string")return{failure:H.failure,repoPath:H.repoPath||process.cwd(),context:H.context||"",maxHypotheses:Math.min(H.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!t?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=t.sessionId,i=Ce("diagnose"),a=i["system.md"],l=i["research.md"],c=i["hypothesis.md"],u=i["verify.md"];if(!a||!l||!c||!u)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let d=new te({apiKey:r}),p=P$(o.context),f=_$(o.failure,o.context),g=`Triage:
1176
+ `),{inventory:E,misfits:k,briefs_written:R,total_artifacts:S.length}}var d$={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:u$,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};mt(d$);import{z as X}from"zod";import{execFile as g$}from"node:child_process";import{promisify as h$}from"node:util";import{tmpdir as y$}from"node:os";import{join as Lb}from"node:path";function Ob(e){return e.confidence<.5?{verify:!0,reason:`low confidence (${e.confidence.toFixed(2)} < ${.5})`}:e.boundary_flag&&e.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${e.boundary_flag}`}:e.coverage_gaps&&e.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${e.coverage_gaps.length===1?"":"s"}: ${e.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${e.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as p$}from"node:url";import{dirname as m$}from"node:path";var f$=p$(import.meta.url),F7=m$(f$),$b={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"private-framework/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function Db(e){let t={description:e.description,prompt:e.systemPrompt};return e.allowedTools&&(t.tools=[...e.allowedTools]),e.model&&(t.model=e.model),t}function b$(e){let t=e.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(t)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(t)?"REFUTED":"INCONCLUSIVE"}var w$=X.object({verifications:X.array(X.object({claim:X.string().optional(),verdict:X.string(),evidence:X.string().optional()}))});function S$(e){let t=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<e.length;i++){let a=e[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(t.push(e.slice(r,i+1)),r=-1))}return t}function k$(e){let t=S$(e);for(let n of t){let r;try{r=JSON.parse(n)}catch{continue}let o=w$.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:b$(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${t.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${e.slice(0,300)}`)}var Ha=h$(g$),Bb=X.object({id:X.string(),claim:X.string(),confidence:X.number().min(0).max(1),evidence_sources:X.array(X.string()),location:X.string().optional(),proposed_fix:X.string().optional(),coverage_gaps:X.array(X.string()).nullish().transform(e=>e??void 0),boundary_flag:X.string().nullish().transform(e=>e??void 0)}),v$=X.object({hypothesis_id:X.string(),claim:X.string(),verdict:X.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:X.string(),gate_reason:X.string()}),Ub=X.object({hypothesis_id:X.string(),reproducer_passed:X.boolean(),regressions:X.array(X.string()),confidence:X.number().min(0).max(1),verification_log:X.string()}),T$=X.enum(["crash","regression","logic-error","flaky","environment","unknown"]),x$=X.object({failure_type:T$,error_signature:X.string(),affected_area:X.string()}),E$=X.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),eZ=X.object({reproducer:X.string().optional(),triage:x$.optional(),hypotheses:X.array(Bb),premise_verifications:X.array(v$).optional(),winner:X.object({hypothesis_id:X.string(),verification_log:X.string(),proposed_fix:X.string()}).optional(),verification_results:X.array(Ub).optional(),outcome:E$.optional(),recommended_next_skill:X.enum(["spec"]).optional()});async function R$(e,t){let n=e.map(l=>({hypothesis:l,decision:Ob(l)})).filter(l=>l.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:e};let r=[],o;try{let l=await t(n.map(c=>c.hypothesis.claim));r=Array.isArray(l)?l:[]}catch(l){o=l instanceof Error?l.message:String(l)}let s=n.map((l,c)=>{let u=r[c];return o!==void 0?{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:l.decision.reason}:u?{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:u.verdict,evidence:u.evidence,gate_reason:l.decision.reason}:{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:l.decision.reason}}),i=new Set(s.filter(l=>l.verdict==="REFUTED").map(l=>l.hypothesis_id)),a=i.size===0?e:e.filter(l=>!i.has(l.id));return{premise_verifications:s,hypotheses_to_test:a}}async function A$(e,t,n){let r=n?.apiKey,o=(()=>{if(typeof e=="string")return{failure:e,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof e=="object"&&e!==null){let H=e;if(typeof H.failure=="string")return{failure:H.failure,repoPath:H.repoPath||process.cwd(),context:H.context||"",maxHypotheses:Math.min(H.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!t?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=t.sessionId,i=Ce("diagnose"),a=i["system.md"],l=i["research.md"],c=i["hypothesis.md"],u=i["verify.md"];if(!a||!l||!c||!u)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let d=new te({apiKey:r}),p=P$(o.context),f=_$(o.failure,o.context),g=`Triage:
1177
1177
  failure_type: ${f.failure_type}
1178
1178
  error_signature: ${f.error_signature}
1179
1179
  affected_area: ${f.affected_area}`,b=`${bt.systemPrompt}
@@ -1874,7 +1874,7 @@ ${E}`})}return{fileBlocks:a,warnings:l}}async function bE(e,t,n,r,o="summary",s,
1874
1874
  `))!==-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(iK(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 lK(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 cK(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 Uc(e,t){let n=m.brand("afk")+m.dim(` (${e})`),r=t?m.warning(" \u25CF plan"):"";return n+r+m.dim(" \u203A ")}async function RE(e,t,n,r){let o=null,s=[];e.session.current.waitForInitialization().then(async S=>{je()&&(o=wl(S)),await pc(e.session.current),je()&&(s=hx())}).catch(()=>{});let i=await iE(),a=new Fc({rl:e.rl,history:i,statusLine:e.statusLine}),l,c,u,d,p,f,g,h=!1,b=!1,y=Rv({onError:S=>Y("[afk suggest] Tier-2 completion failed:",S)}),w=lK(T.AFK_SUGGEST_GHOST,e.suggestGhostConfig);try{await a.armCompositor({promptFn:()=>Uc(e.stats.model,e.stats.planMode),onCancel:r,onShiftTab:()=>{let _=e.slashCtx;_.stats.planMode&&_.stats.pendingPlanExit?(_.stats.pendingPlanExit=!1,At(_,!1,{closureSummarySkipped:!0}).catch(()=>{})):At(_).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.history;return _.getEntries?[..._.getEntries()]:[]},getDropdownTopCandidate:_=>{let D=a.autocompleteState.candidates[0];return D&&D.value.startsWith(_)&&D.value.length>_.length?D.value:null},getTranscriptTail:()=>"",getRecentCommands:()=>[],llmEnabled:()=>/^(1|true|yes|on)$/i.test(T.AFK_SUGGEST_ENABLED??"")})}}:{}});let S=a.getCompositor();Bt.install(gl({readLine:_=>a.readLine({promptFn:()=>_}).then(L=>L.text),writer:{line:(_="")=>{let L=a.getCompositor();L?L.commitAbove(_):process.stdout.write(_+`
1875
1875
  `)}},pendingCount:()=>Bt.pendingCount(),...S?{pickFromList:_=>dE(S,_),readTextOverlay:_=>pE(S,_)}:{}})),e.replRenderer.setCompositor(a.getCompositor()),e.slashCtx.getCompositor=()=>a.getCompositor();let E=a.getCompositor();if(E){let _=L=>E.commitAbove(L);e.completionWriter.fn=_,e.completionWriter.idleFn=_}e.slashCtx.setSoftStopHandler=_=>a.setSoftStopHandler(_),e.inputSurfaceRef&&(e.inputSurfaceRef.current=a),c=wE(),g=SE(),e.clearVerdictLedger=()=>g?.reset(),u=new Rl,jk(u),Vk(u),Qk(u),Xk(e.backgroundRegistry);let k=0,R=0,C=1,A=()=>e.statusLine.setExtraRows(C+k+R);g.setRowCountChangeHandler(_=>{R=_,A(),d?.redraw(),p?.redraw()}),d=new Nc(u,e.backgroundRegistry,{getAdjacentRows:()=>R}),d.setRowCountChangeHandler(_=>{k=_,A()}),p=new Yl({getExtraRows:()=>e.statusLine.getExtraRows()}),p.setRowCountChangeHandler(_=>{A()}),e.statusLine.setAfterScrollRestore(()=>{g?.repaint(),d?.redraw(),p?.redraw()}),d.start(),p.start(),g.start({stream:process.stdout});let P=50,I=[];for(u.on("complete",_=>{I.length>=P&&I.shift(),I.push(_)}),f=new Bc({writeLine:_=>e.replRenderer.writeLine(_),getCwd:()=>e.stats.cwd}),Jk(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(;I.length>0;){let x=I.shift(),O=x.status==="succeeded"?"\u2713":"\u2717",B=[];if(x.resultText){let H=x.resultText.trim().split(`
1876
1876
  `)[0]?.slice(0,80)??"";H&&B.push(H)}x.error&&B.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&&B.push(J),e.replRenderer.writeLine(An({kind:x.status==="succeeded"?"checkpoint":"diagnosis",title:`${O} ${x.id} ${x.label}`,body:B})),e.replRenderer.writeLine("")}let _=f.drainNotifications();for(let{job:x,result:O}of _){let B=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}`,H=Math.max(0,Math.round(O.durationMs/100)/10);e.replRenderer.writeLine(m.dim(` ${B} [${x.id}] ${J} \xB7 ${H}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 D,j;if(l!==void 0){let x=l;l=void 0;let O=Uc(e.stats.model,e.stats.planMode),B=kr({buffer:x.text,promptText:O,isTTY:!!process.stdout.isTTY,attachmentSummary:jo([...x.attachments])});e.replRenderer.writeLine(B),D=x.text.trim(),j=x.attachments}else{let x=await a.readLine({promptFn:()=>Uc(e.stats.model,e.stats.planMode),onSigint:r,onShiftTab:()=>{let O=e.slashCtx;O.stats.planMode&&O.stats.pendingPlanExit?(O.stats.pendingPlanExit=!1,At(O,!1,{closureSummarySkipped:!0}).catch(()=>{})):At(O).catch(()=>{}),e.statusLine.rearm()}});D=x.text.trim(),j=x.attachments}if(!D&&j.length===0)continue;if(D.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(D))){e.statusLine.rearm();continue}}let N=!1;if(D.startsWith("/")){let x=await nk(D,e.slashCtx,j);if(x.handled){if(x.result==="exit"){e.rl.close();return}if((D==="/clear"||D.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:j??[]},e.statusLine.rearm();continue}e.statusLine.rearm();continue}N=!0}i.push(D),await cK(e,D);let K=D;if(N){let x=wp(D);if(x){let O=x.name.replace(/^\//,"").split(":").pop()??"";if(O&&km(O)){let B={skillName:O,rawArgs:x.args,source:"plugin",capabilities:{compose:!0,subagents:!0}},J=e.session.current.sessionId,H=Pr(J),F=Date.now();Y(`[afk trace] preflight.start commandName=${O}`);let V=!1,be=await Ir(B,{cwd:e.stats.cwd??process.cwd(),artifactDir:H},Be=>{je()&&e.replRenderer.writeLine(m.warning(`\u26A0 preflight(${O}) failed: `)+(Be instanceof Error?Be.message:String(Be)))});V=be!==null,Y(`[afk trace] preflight.end commandName=${O} durationMs=${Date.now()-F} success=${V}`),K=xm(be?.manifestBlock,D)}}}let M=f.drainInjections();M.length>0&&(K=M+K),await bE({text:K,attachments:j},e.session.current,e.stats,{setInFlight(x){n.turnInFlight=x},async onTurnComplete(x,O){if(await t.appendTurn(x,O),e.stats.sessionId)try{Kt(e.stats)}catch(B){b||(b=!0,e.replRenderer.writeLine(m.warning("\u26A0 ")+"session autosave failed \u2014 this conversation may not be resumable: "+(B instanceof Error?B.message:String(B))))}},async onAfterTurn(){await e.contextSampler.onTurn(e.stats.totalTurns),await hk(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(Sr(e.stats,e.contextSampler))},...p?{onStageChange:x=>p.repaint(x)}:{}},e.options.thinkingUi,e.completionWriter,u,a.toRunTurnRefs(Uc(e.stats.model,e.stats.planMode)))}}finally{if(u!==void 0)for(let E of u.running())u.cancel(E.id);n.tryAbortShellForeground=null,f?.drainOnExit(),p?.stop(),d?.stop(),g?.stop(),c?.dispose();let S=E=>console.log(E);e.completionWriter.fn=S,e.completionWriter.idleFn=S,await a.dispose(),e.inputSurfaceRef&&(e.inputSurfaceRef.current=null)}}import{execFile as uK}from"node:child_process";import{dirname as dK,isAbsolute as pK,resolve as mK}from"node:path";import{promisify as fK}from"node:util";var AE=fK(uK),gK=3e3,hK=new Set(["empty","orphaned-dir","orphaned-registration","dead-owner"]);async function yK(){let t=(await AE("git",["rev-parse","--git-common-dir"])).stdout.trim();if(!t)throw new Error("Not in a git repository.");let n=pK(t)?t:mK(process.cwd(),t);return dK(n)}async function _E(e){if(e?.disabled)return{ran:!1,removedCount:0,skippedReason:"disabled"};let t;try{t=await yK()}catch{return{ran:!1,removedCount:0,skippedReason:"not-in-repo"}}let n,r=new Promise(o=>{n=setTimeout(()=>o("timeout"),gK)});try{let o=Jt({execFile:AE,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=>hK.has(c.verdict)&&i.removed.includes(c.path)).length}}catch{return{ran:!1,removedCount:0,skippedReason:"error"}}finally{n&&clearTimeout(n)}}import{promises as bK}from"node:fs";import{dirname as wK,join as PE}from"node:path";import{randomBytes as SK}from"node:crypto";var kK=["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(`
1877
- `),CE=/^[a-z0-9]+(-[a-z0-9]+){1,3}$/,Ym=30,vK=1024,TK=8e3,xK="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=CK(n,vK),o=new AbortController,s=setTimeout(()=>o.abort(),t.timeoutMs??TK),i=t.signal?IK([t.signal,o.signal]):o.signal,a;try{t.slugGenerator?a=await t.slugGenerator(r,i):a=await eo({token:t.token,model:t.model??xK,system:kK,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=RK(a);if(l===null)return t.onSkip?.("invalid-slug-output",a.slice(0,60)),null;let c=wK(t.worktreePath);return await AK(l,c)}function RK(e){let t=e.trim().toLowerCase();if(t.length===0)return null;if(CE.test(t)&&t.length<=Ym)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>Ym)break;o=i}return CE.test(o)?o:null}async function AK(e,t){if(!await _K(PE(t,e)))return e;let n=SK(2).toString("hex");return`${e.split("-").slice(0,3).join("-").slice(0,Ym-5)}-${n}`}async function _K(e){try{return await bK.access(e),!0}catch{return!1}}function CK(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 IK(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 ME(e){let t,n,r=PE(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=`${Wa(e.branchPrefix)}${o}`;try{let c=await e.deferred.create(l);return IE(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 IE(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 IE(e,t){e&&e.setCwd(t),PK(t)}function PK(e){try{process.chdir(e)}catch{}}W();import{spawn as OE}from"child_process";import{existsSync as LK,mkdirSync as FK,readFileSync as $E,unlinkSync as NK,writeFileSync as BK}from"fs";import{get as UK}from"https";import{join as DE}from"path";import{readFileSync as MK}from"fs";import{dirname as OK,join as $K}from"path";import{fileURLToPath as DK}from"url";function wn(){try{return"3.80.6"}catch{}try{let e=OK(DK(import.meta.url));for(let t of["../../package.json","../package.json"])try{let n=JSON.parse(MK($K(e,t),"utf-8"));if(typeof n.version=="string")return n.version}catch{}}catch{}return"0.0.0-unknown"}G();var jK=64*1024,HK=1440*60*1e3,WK="update-check.json",KK="pending-update.json";function LE(){return DE(qi(),WK)}function Vm(){return DE(qi(),KK)}function FE(){let e=qi();LK(e)||FK(e,{recursive:!0})}function GK(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 zK(){try{let e=$E(LE(),"utf-8"),t=JSON.parse(e);if(typeof t.latestVersion=="string"&&typeof t.checkedAt=="number")return t}catch{}return null}function qK(){try{FE();let e=`
1877
+ `),CE=/^[a-z0-9]+(-[a-z0-9]+){1,3}$/,Ym=30,vK=1024,TK=8e3,xK="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=CK(n,vK),o=new AbortController,s=setTimeout(()=>o.abort(),t.timeoutMs??TK),i=t.signal?IK([t.signal,o.signal]):o.signal,a;try{t.slugGenerator?a=await t.slugGenerator(r,i):a=await eo({token:t.token,model:t.model??xK,system:kK,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=RK(a);if(l===null)return t.onSkip?.("invalid-slug-output",a.slice(0,60)),null;let c=wK(t.worktreePath);return await AK(l,c)}function RK(e){let t=e.trim().toLowerCase();if(t.length===0)return null;if(CE.test(t)&&t.length<=Ym)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>Ym)break;o=i}return CE.test(o)?o:null}async function AK(e,t){if(!await _K(PE(t,e)))return e;let n=SK(2).toString("hex");return`${e.split("-").slice(0,3).join("-").slice(0,Ym-5)}-${n}`}async function _K(e){try{return await bK.access(e),!0}catch{return!1}}function CK(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 IK(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 ME(e){let t,n,r=PE(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=`${Wa(e.branchPrefix)}${o}`;try{let c=await e.deferred.create(l);return IE(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 IE(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 IE(e,t){e&&e.setCwd(t),PK(t)}function PK(e){try{process.chdir(e)}catch{}}W();import{spawn as OE}from"child_process";import{existsSync as LK,mkdirSync as FK,readFileSync as $E,unlinkSync as NK,writeFileSync as BK}from"fs";import{get as UK}from"https";import{join as DE}from"path";import{readFileSync as MK}from"fs";import{dirname as OK,join as $K}from"path";import{fileURLToPath as DK}from"url";function wn(){try{return"3.80.7"}catch{}try{let e=OK(DK(import.meta.url));for(let t of["../../package.json","../package.json"])try{let n=JSON.parse(MK($K(e,t),"utf-8"));if(typeof n.version=="string")return n.version}catch{}}catch{}return"0.0.0-unknown"}G();var jK=64*1024,HK=1440*60*1e3,WK="update-check.json",KK="pending-update.json";function LE(){return DE(qi(),WK)}function Vm(){return DE(qi(),KK)}function FE(){let e=qi();LK(e)||FK(e,{recursive:!0})}function GK(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 zK(){try{let e=$E(LE(),"utf-8"),t=JSON.parse(e);if(typeof t.latestVersion=="string"&&typeof t.checkedAt=="number")return t}catch{}return null}function qK(){try{FE();let e=`
1878
1878
  const https = require('https');
1879
1879
  const fs = require('fs');
1880
1880
  const url = 'https://registry.npmjs.org/agent-afk/latest';
@@ -1921,7 +1921,7 @@ ${L.slice(0,500)}`).catch(D=>{console.error("[daemon] crash notification push fa
1921
1921
  `||o===""?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdin.removeListener("data",r),process.stdout.write(`
1922
1922
  `),t(n.join("").trim())):o===""?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdout.write(`
1923
1923
  `),process.exit(1)):o==="\x7F"?n.pop():n.push(o)};process.stdin.on("data",r)})}function hR(){return fR("Anthropic API key or OAuth token: ")}async function Gc(e){let t=e??await hR();t||(console.error(gR.red("No token provided. Nothing saved.")),process.exit(1));let n=ot(),r,o;t.startsWith("sk-ant-oat")?(r="CLAUDE_CODE_OAUTH_TOKEN",o=["ANTHROPIC_API_KEY"]):(r="ANTHROPIC_API_KEY",o=["CLAUDE_CODE_OAUTH_TOKEN"]),Sn(n,r,t,o),console.log(gR.green(`\u2713 Saved ${r} to ${n}`)),console.log(m.meta("Restart any running afk daemon to pick up the new token."))}function yR(e){e.command("login [token]").description("Save an Anthropic API key or OAuth token for afk to use").action(async t=>{let n=fe(Ze());if(n==="openai-compatible"||n==="openai-codex"){console.log(ss.yellow("`afk login` is Anthropic-only.")),console.log(""),console.log("For OpenAI-backed models (gpt-*, o1*, o3*, o4*, codex-*), authenticate with one of:"),console.log(ss.cyan(" export OPENAI_API_KEY=sk-proj-...")),console.log(ss.cyan(" # or: export CODEX_API_KEY=...")),console.log(ss.cyan(" codex login --api-key sk-proj-...")),console.log(""),console.log(ss.gray("Run `afk provider auth diagnose` to see which auth source AFK will use.")),console.log(ss.gray("To save an Anthropic key for Claude models instead, re-run with AFK_MODEL=sonnet (or similar) first."));return}await Gc(t)})}import ce from"chalk";import zc from"ora";W();import{existsSync as FG}from"fs";import{join as NG}from"path";async function of(e,t={},n={}){let r=n.pluginsDir??Me(),o=n.indexPath??ae(),s=n.now??(()=>new Date),i=n.gitRunner?{runner:n.gitRunner}:{},l=ge(o).plugins[e];if(!l)throw new Error(`plugin "${e}" is not installed`);let c=NG(r,e);if(!FG(c))return{name:e,status:"missing-dir",dir:c};if(un(),l.sourceType==="local")return{name:e,status:"skipped-local"};await gc(c,i);let u,d=!1;if(t.ref)u=t.ref;else{let k=await zn(c,i),R=Jn(k);R!==null?(u=R,d=!0):u=l.ref??await Vt(c,i)}let p=`refs/remotes/origin/${u}`,f=d?null:await hc(c,p,i),g=f!==null,h=await Yt(c,i);if(g?f===h:u===l.ref)return{name:e,status:"up-to-date",ref:u,commit:h,version:Cr(c).version};await qn(c,g?p:d?`refs/tags/${u}`:u,i);let y=await Yt(c,i),w=Cr(c).version,S=s().toISOString(),E={...l,ref:u,commit:y,updatedAt:S};return Dn(e,E,o),{name:e,status:"updated",fromRef:l.ref,toRef:u,commit:y,version:w}}async function bR(e={}){let t=e.indexPath??ae(),n=ge(t),r=[];for(let o of Object.keys(n.plugins))try{r.push(await of(o,{},e))}catch(s){let i=s instanceof Error?s.message:String(s);r.push({name:o,status:"missing-dir",dir:i})}return r}W();import{existsSync as BG,lstatSync as UG,rmSync as jG,unlinkSync as HG}from"fs";import{join as WG}from"path";function wR(e,t={}){yn(e);let n=t.pluginsDir??Me(),r=t.indexPath??ae(),o=WG(n,e),s=!1;KG(o)?(HG(o),s=!0):BG(o)&&(jG(o,{recursive:!0,force:!0}),s=!0);let i=ge(r),a=Object.prototype.hasOwnProperty.call(i.plugins,e);return a&&wb(e,r),un(),{name:e,removedDir:s,removedIndexEntry:a}}function KG(e){try{return UG(e).isSymbolicLink()}catch{return!1}}W();function SR(e,t={}){let n=t.logger??console,r=t.pluginsDir??Me(),o=t.indexPath??ae(),s={...t,pluginsDir:r,indexPath:o},i=e.command("plugin").description("Manage AFK plugins (install / update / list / remove / enable / disable)");i.command("install <source> [name]").description("Install a plugin from a git URL, owner/repo shorthand, local path, or <marketplace>:<plugin>").option("-r, --ref <ref>","Install a specific tag, branch, or SHA").option("-f, --force","Replace an existing plugin with the same name").option("-y, --yes","Skip the install warning and countdown (non-interactive / CI)").action(async(a,l,c)=>{let u;try{u=ts(a)}catch(f){zc(`Installing ${a}\u2026`).start().fail("Failed"),z(f)}let d=process.stderr.isTTY===!0&&!c.yes;if(u.type==="marketplace-ref"){let f=zc(`Installing ${u.marketplace}:${u.plugin}\u2026`).start();try{let g=await ns(u.marketplace,u.plugin,{...c.ref?{ref:c.ref}:{},...c.force?{force:!0}:{}},{...s,confirm:d});f.succeed(ce.green(`Installed ${ce.bold(g.key)}`)+ce.gray(` at ${g.dir}`))}catch(g){f.fail("Failed"),z(g)}return}let p=zc(`Installing ${a}\u2026`).start();try{let f={...l?{name:l}:{},...c.ref?{ref:c.ref}:{},...c.force?{force:!0}:{}},g=await wc(a,f,{...s,confirm:d});p.succeed(ce.green(`Installed ${ce.bold(g.name)}`)+ce.gray(` at ${g.dir}${g.entry.ref?` (ref: ${g.entry.ref})`:""}`))}catch(f){p.fail("Failed"),z(f)}}),i.command("update [name]").description("Update one plugin, or all if no name is given").option("-r, --ref <ref>","Pin to a specific ref instead of the latest tag").action(async(a,l)=>{try{if(a){let c=zc(`Updating ${a}\u2026`).start(),u=await of(a,l.ref?{ref:l.ref}:{},s);zG(u,c)}else{n.log(ce.cyan("Updating all plugins\u2026"));let c=await bR(s);if(c.length===0){n.log(ce.gray(" (nothing installed)"));return}for(let u of c)n.log(" "+kR(u))}}catch(c){z(c)}}),i.command("list").description("List installed plugins with their source, version, and enabled state").option("-f, --format <format>","Output format (text|json)","text").action(a=>{let l=ge(o);if(a.format==="json"){let c=Object.entries(l.plugins).map(([u,d])=>({name:u,enabled:d.enabled,...d.ref?{ref:d.ref}:{},source:d.source}));n.log(JSON.stringify({plugins:c},null,2))}else GG(l,n)}),i.command("remove <name>").description("Remove a plugin (directory + index entry)").action(a=>{let l=wR(a,{pluginsDir:r,indexPath:o});if(!l.removedDir&&!l.removedIndexEntry){n.log(ce.gray(`No plugin named "${a}" to remove.`));return}let c=[l.removedDir?"directory":null,l.removedIndexEntry?"index entry":null].filter(Boolean);n.log(ce.green(`Removed ${a}: ${c.join(" + ")}`))}),i.command("enable <name>").description("Re-enable a previously disabled plugin").action(a=>{try{Ld(a,!0,o),n.log(ce.green(`Enabled ${a}`))}catch(l){z(l)}}),i.command("disable <name>").description("Keep the plugin on disk but skip it from SDK init").action(a=>{try{Ld(a,!1,o),n.log(ce.yellow(`Disabled ${a} (dir preserved at ${r}/${a})`))}catch(l){z(l)}})}function GG(e,t){let n=Object.keys(e.plugins).sort();if(n.length===0){t.log(ce.gray("No plugins installed.")),t.log(ce.gray(" Try: afk plugin install anthropics/claude-plugins-official"));return}t.log(ce.cyan.bold(`
1924
- Installed plugins:`));for(let r of n){let o=e.plugins[r];if(!o)continue;let s=o.enabled?ce.green("enabled "):ce.yellow("disabled"),i=o.ref?ce.blue(o.ref):ce.gray("(local)"),a=ce.gray(o.source);t.log(` ${ce.bold(r.padEnd(30))} ${s} ${i.padEnd(12)} ${a}`)}t.log("")}function kR(e){switch(e.status){case"updated":{let t=e.fromRef===e.toRef?`${e.toRef} @ ${e.commit.slice(0,7)}`:`${e.fromRef??"(none)"} \u2192 ${e.toRef}`,n=e.version?ce.gray(` [v${e.version.replace(/^v/i,"")}]`):"";return`${ce.green("\u2713")} ${ce.bold(e.name)}: ${t}${n}`}case"up-to-date":{let t=e.version?ce.gray(` [v${e.version.replace(/^v/i,"")}]`):"";return`${ce.gray("\xB7")} ${ce.bold(e.name)}: up-to-date (${e.ref})${t}`}case"skipped-local":return`${ce.gray("\xB7")} ${ce.bold(e.name)}: skipped (local source)`;case"missing-dir":return`${ce.yellow("!")} ${ce.bold(e.name)}: plugin dir missing (${e.dir})`}}function zG(e,t){let n=kR(e);e.status==="updated"?t.succeed(n):e.status==="up-to-date"||e.status==="skipped-local"?t.info(n):t.warn(n)}import oe from"chalk";import sf from"ora";W();function vR(e,t={}){let n=t.logger??console,r=t.cacheDir??Xt(),o=t.indexPath??ae(),s={...t,cacheDir:r,indexPath:o},i=e.command("marketplace").description("Manage AFK plugin marketplaces (install / list / plugins / install-plugin / remove / update)");i.command("install <source> [name]").description("Clone or symlink a marketplace into the local plugin cache").option("-r, --ref <ref>","Install a specific tag, branch, or SHA").option("-f, --force","Replace an existing marketplace with the same name").action(async(a,l,c)=>{let u=sf(`Installing marketplace ${a}\u2026`).start();try{let d={...l?{name:l}:{},...c.ref?{ref:c.ref}:{},...c.force?{force:!0}:{}},p=await kc(a,d,s),f=p.entry.ref?` (ref: ${p.entry.ref})`:"";u.succeed(oe.green(`Installed marketplace ${oe.bold(p.name)}`)+oe.gray(`${f} at ${p.dir}`)),n.log(oe.gray(` ${p.plugins.length} plugin(s) available \u2014 run \`afk marketplace plugins ${p.name}\` to list.`))}catch(d){u.fail("Failed"),z(d)}}),i.command("list").description("List installed marketplaces with their source and ref").option("-f, --format <format>","Output format (text|json)","text").action(a=>{let l=ge(o),c=Object.entries(l.marketplaces);if(a.format==="json"){n.log(JSON.stringify({marketplaces:c.map(([u,d])=>({name:u,source:d.source,sourceType:d.sourceType,...d.ref?{ref:d.ref}:{}}))},null,2));return}if(c.length===0){n.log(oe.gray("No marketplaces installed.")),n.log(oe.gray(" Try: afk marketplace install griffinwork40/awa-private"));return}n.log(oe.cyan.bold(`
1924
+ Installed plugins:`));for(let r of n){let o=e.plugins[r];if(!o)continue;let s=o.enabled?ce.green("enabled "):ce.yellow("disabled"),i=o.ref?ce.blue(o.ref):ce.gray("(local)"),a=ce.gray(o.source);t.log(` ${ce.bold(r.padEnd(30))} ${s} ${i.padEnd(12)} ${a}`)}t.log("")}function kR(e){switch(e.status){case"updated":{let t=e.fromRef===e.toRef?`${e.toRef} @ ${e.commit.slice(0,7)}`:`${e.fromRef??"(none)"} \u2192 ${e.toRef}`,n=e.version?ce.gray(` [v${e.version.replace(/^v/i,"")}]`):"";return`${ce.green("\u2713")} ${ce.bold(e.name)}: ${t}${n}`}case"up-to-date":{let t=e.version?ce.gray(` [v${e.version.replace(/^v/i,"")}]`):"";return`${ce.gray("\xB7")} ${ce.bold(e.name)}: up-to-date (${e.ref})${t}`}case"skipped-local":return`${ce.gray("\xB7")} ${ce.bold(e.name)}: skipped (local source)`;case"missing-dir":return`${ce.yellow("!")} ${ce.bold(e.name)}: plugin dir missing (${e.dir})`}}function zG(e,t){let n=kR(e);e.status==="updated"?t.succeed(n):e.status==="up-to-date"||e.status==="skipped-local"?t.info(n):t.warn(n)}import oe from"chalk";import sf from"ora";W();function vR(e,t={}){let n=t.logger??console,r=t.cacheDir??Xt(),o=t.indexPath??ae(),s={...t,cacheDir:r,indexPath:o},i=e.command("marketplace").description("Manage AFK plugin marketplaces (install / list / plugins / install-plugin / remove / update)");i.command("install <source> [name]").description("Clone or symlink a marketplace into the local plugin cache").option("-r, --ref <ref>","Install a specific tag, branch, or SHA").option("-f, --force","Replace an existing marketplace with the same name").action(async(a,l,c)=>{let u=sf(`Installing marketplace ${a}\u2026`).start();try{let d={...l?{name:l}:{},...c.ref?{ref:c.ref}:{},...c.force?{force:!0}:{}},p=await kc(a,d,s),f=p.entry.ref?` (ref: ${p.entry.ref})`:"";u.succeed(oe.green(`Installed marketplace ${oe.bold(p.name)}`)+oe.gray(`${f} at ${p.dir}`)),n.log(oe.gray(` ${p.plugins.length} plugin(s) available \u2014 run \`afk marketplace plugins ${p.name}\` to list.`))}catch(d){u.fail("Failed"),z(d)}}),i.command("list").description("List installed marketplaces with their source and ref").option("-f, --format <format>","Output format (text|json)","text").action(a=>{let l=ge(o),c=Object.entries(l.marketplaces);if(a.format==="json"){n.log(JSON.stringify({marketplaces:c.map(([u,d])=>({name:u,source:d.source,sourceType:d.sourceType,...d.ref?{ref:d.ref}:{}}))},null,2));return}if(c.length===0){n.log(oe.gray("No marketplaces installed.")),n.log(oe.gray(" Try: afk marketplace install griffinwork40/example-plugin"));return}n.log(oe.cyan.bold(`
1925
1925
  Installed marketplaces:`));for(let[u,d]of c.sort()){let p=d.ref?oe.blue(d.ref):oe.gray("(local)"),f=oe.gray(d.source);n.log(` ${oe.bold(u.padEnd(30))} ${p.padEnd(12)} ${f}`)}n.log("")}),i.command("plugins <name>").description("List plugins inside a marketplace, with [installed] / [available] markers").option("-f, --format <format>","Output format (text|json)","text").action((a,l)=>{try{let c=xc(a,s);if(l.format==="json"){n.log(JSON.stringify({marketplace:a,plugins:c},null,2));return}if(c.length===0){n.log(oe.gray(`Marketplace "${a}" lists no plugins.`));return}n.log(oe.cyan.bold(`
1926
1926
  Plugins in ${a}:`)),c.forEach((u,d)=>{let p=u.installed?oe.green("[\u2713]"):oe.gray("[ ]"),f=u.description?oe.gray(` \u2014 ${u.description}`):"";n.log(` ${p} ${oe.bold((d+1).toString().padStart(2))}. ${oe.bold(u.name)}${f}`)}),n.log(oe.gray(`
1927
1927
  Install one: afk plugin install ${a}:<plugin>`))}catch(c){z(c)}}),i.command("install-plugin <marketplace> <plugin>").description("Install a single plugin from a marketplace").option("-r, --ref <ref>","For git-sourced plugins, pin to a specific tag/branch/SHA").option("-f, --force","Replace an existing plugin with the same key").option("-y, --yes","Skip the install warning and countdown (non-interactive / CI)").action(async(a,l,c)=>{let u=process.stderr.isTTY===!0&&!c.yes,d=sf(`Installing ${a}:${l}\u2026`).start();try{let p=await ns(a,l,{...c.ref?{ref:c.ref}:{},...c.force?{force:!0}:{}},{...s,confirm:u});d.succeed(oe.green(`Installed ${oe.bold(p.key)}`)+oe.gray(` at ${p.dir}`))}catch(p){d.fail("Failed"),z(p)}}),i.command("remove <name>").description("Remove a marketplace and cascade-delete its installed plugins").action(a=>{let l=vc(a,{cacheDir:r,indexPath:o});if(!l.removedDir&&!l.removedIndexEntry&&l.removedPluginEntries.length===0){n.log(oe.gray(`No marketplace named "${a}" to remove.`));return}let c=[l.removedDir?"directory":null,l.removedIndexEntry?"index entry":null,l.removedPluginEntries.length>0?`${l.removedPluginEntries.length} plugin entry`:null].filter(Boolean);if(n.log(oe.green(`Removed ${a}: ${c.join(" + ")}`)),l.removedPluginEntries.length>0)for(let u of l.removedPluginEntries)n.log(oe.gray(` - ${u}`))}),i.command("update [name]").description("Update one marketplace, or all if no name is given").option("-r, --ref <ref>","Pin to a specific ref instead of the latest tag").action(async(a,l)=>{try{if(a){let c=sf(`Updating ${a}\u2026`).start(),u=await bi(a,l.ref?{ref:l.ref}:{},s);qG(u,c)}else{n.log(oe.cyan("Updating all marketplaces\u2026"));let c=await Dx(s);if(c.length===0){n.log(oe.gray(" (no marketplaces installed)"));return}for(let u of c)n.log(" "+TR(u))}}catch(c){z(c)}})}function TR(e){switch(e.status){case"updated":{let t=e.addedPlugins.length>0?` +${e.addedPlugins.join(", ")}`:"",n=e.removedPlugins.length>0?` -${e.removedPlugins.join(", ")}`:"",r=e.fromRef===e.toRef?`${e.toRef} @ ${e.commit.slice(0,7)}`:`${e.fromRef??"(none)"} \u2192 ${e.toRef}`,o=e.pluginVersions.filter(i=>i.version!==null).map(i=>`${i.name} ${i.version}`).join(", "),s=o?oe.gray(` [${o}]`):"";return`${oe.green("\u2713")} ${oe.bold(e.name)}: ${r}${oe.gray(t+n)}${s}`}case"up-to-date":return`${oe.gray("\xB7")} ${oe.bold(e.name)}: up-to-date (${e.ref})`;case"skipped-local":return`${oe.gray("\xB7")} ${oe.bold(e.name)}: skipped (local source)`;case"missing-dir":return`${oe.yellow("!")} ${oe.bold(e.name)}: marketplace dir missing (${e.dir})`}}function qG(e,t){let n=TR(e);e.status==="updated"?t.succeed(n):e.status==="up-to-date"||e.status==="skipped-local"?t.info(n):t.warn(n)}G();import{access as JG,constants as YG,mkdir as VG,readFile as XG}from"fs/promises";import{execSync as ZG}from"child_process";W();async function QG(){return pe()?{name:"Anthropic API Key",state:"pass",detail:"ANTHROPIC_API_KEY set"}:{name:"Anthropic API Key",state:"fail",fix:"Set ANTHROPIC_API_KEY or run `afk login`"}}async function e2(){return za()?{name:"Codex/OpenAI API Key",state:"pass",detail:"OPENAI_API_KEY or CODEX_API_KEY set"}:{name:"Codex/OpenAI API Key",state:"warn",fix:"Set OPENAI_API_KEY or CODEX_API_KEY to use Codex models"}}async function t2(){try{let t=`${ZG("npm config get prefix",{timeout:2e3,encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim().replace(/\/$/,"")}/bin`;return(T.PATH??"").split(":").map(r=>r.replace(/\/$/,"")).includes(t)?{name:"npm bin on PATH",state:"pass",detail:t}:{name:"npm bin on PATH",state:"fail",detail:t,fix:`Add ${t} to PATH: echo 'export PATH="${t}:$PATH"' >> ~/.zshrc`}}catch{return{name:"npm bin on PATH",state:"warn",detail:"could not query npm prefix"}}}async function af(e,t){let n=t();try{return await JG(n,YG.W_OK),{name:e,state:"pass",detail:n}}catch{try{return await VG(n,{recursive:!0}),{name:e,state:"pass",detail:`${n} (created)`}}catch{return{name:e,state:"fail",detail:n,fix:`Unable to create or write to ${n}`}}}}async function n2(){let e=qr();try{let t=await XG(e,"utf-8");return JSON.parse(t),{name:"Config File",state:"pass",detail:`${e} (valid JSON)`}}catch(t){return t.code==="ENOENT"?{name:"Config File",state:"pass",detail:"no config file (using defaults)"}:{name:"Config File",state:"fail",detail:e,fix:`Unable to parse config file: ${t instanceof Error?t.message:"unknown error"}`}}}async function r2(){let e=Yf();return e.length===0?null:{name:"Required env vars",state:"fail",detail:`Missing: ${e.map(t=>t.name).join(", ")}`,fix:"Set these env vars before running. See docs/env-registry.md for descriptions."}}async function o2(){let e=T.AFK_TELEGRAM_BOT_TOKEN;if(!e)return null;try{let t=new AbortController,n=setTimeout(()=>t.abort(),5e3),r=await fetch(`https://api.telegram.org/bot${e}/getMe`,{signal:t.signal});if(clearTimeout(n),r.ok){let s=(await r.json()).result?.username;return{name:"Telegram Bot",state:"pass",detail:s?`@${s}`:"connected"}}return{name:"Telegram Bot",state:"fail",fix:`Telegram API returned ${r.status}. Check AFK_TELEGRAM_BOT_TOKEN.`}}catch(t){return t.name==="AbortError"?{name:"Telegram Bot",state:"warn",detail:"connection timeout"}:{name:"Telegram Bot",state:"warn",detail:`network error: ${t instanceof Error?t.message:"unknown"}`}}}function xR(e){e.command("doctor").description("Check system health and configuration").option("-f, --format <format>","Output format (text|json)","text").action(async t=>{let n=[];n.push(await QG()),n.push(await e2()),n.push(await t2()),n.push(await af("Config Directory",Ot)),n.push(await af("State Directory",Se)),n.push(await af("Logs Directory",vn)),n.push(await n2());let r=await r2();r!==null&&n.push(r);let o=await o2();o!==null&&n.push(o);let s={passed:n.filter(i=>i.state==="pass").length,warned:n.filter(i=>i.state==="warn").length,failed:n.filter(i=>i.state==="fail").length};t.format==="json"?console.log(JSON.stringify({checks:n,summary:s},null,2)):(n.forEach(i=>{let a;i.state==="pass"?a=m.success("\u2713"):i.state==="warn"?a=m.warning("\u26A0"):a=m.error("\u2717");let l=`${a} ${i.name}`;i.detail&&(l+=` \u2014 ${i.detail}`),console.log(l),i.state==="fail"&&i.fix&&console.log(` Fix: ${i.fix}`)}),console.log(`
package/dist/index.mjs CHANGED
@@ -1061,7 +1061,7 @@ Unless the dispatcher specifies a different schema, return:
1061
1061
  **\`boundary_flag\` is required.** If nothing applies, emit \`"none"\` \u2014 do not omit the field. Treat missing as \`"none"\` is acceptable on the orchestrator side, but emit the field explicitly so downstream synthesizers and validators do not see \`null\`.
1062
1062
 
1063
1063
  If \`scope_check\` flags implementation (non-git), the orchestrator should dispatch a different sub-agent type for follow-up. Do not re-dispatch the same task through \`research-agent\`.
1064
- `,sourcePath:"agent-framework-private/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};U();U();import{existsSync as Oe,readdirSync as Nm,readFileSync as $m}from"fs";import{join as ve}from"path";U();import{existsSync as Hr,readFileSync as Mm,readdirSync as Om,statSync as Dm}from"fs";import{join as Mt,resolve as la}from"path";import{existsSync as Im,mkdirSync as ZE,readFileSync as Cm,renameSync as ex,writeFileSync as tx,unlinkSync as nx}from"fs";U();function ca(t=Yt()){if(!Im(t))return wn();try{let e=Cm(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return wn();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return wn()}catch{return wn()}}function wn(){return{version:2,plugins:{},marketplaces:{}}}var Fm=5,da="cache",Ct;function ke(t=Ve()){Ct||(Ct=new Map);let e=Ct.get(t);if(e)return[...e];if(!Hr(t))return Ct.set(t,[]),[];let n=t===Ve()?Yt():Mt(t,".index.json"),r=ca(n),o=[];return ua(t,t,0,o,new Set,r.plugins),Ct.set(t,o),[...o]}function ua(t,e,n,r,o,s){if(n>Fm||o.has(e))return;if(o.add(e),Hr(Mt(e,".claude-plugin","plugin.json"))){let a=jr(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let l=s[a.key];if(!l||l.enabled===!1)return;r.push({type:"local",path:e});return}let c=s[a.key];if(c&&c.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=Om(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let c=Mt(e,a),l;try{l=Dm(c)}catch{continue}l.isDirectory()&&ua(t,c,n+1,r,o,s)}}function jr(t,e){if(!e.startsWith(t))return null;let n=e.slice(t.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===da&&r.length>=3){let s=r[1];if(s){let i=Mt(t,da,s),c=Lm(i,e)??r[2];if(c)return{layout:"cache",key:`${s}:${c}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function Lm(t,e){let n=Mt(t,".claude-plugin","marketplace.json");if(!Hr(n))return null;let r;try{r=JSON.parse(Mm(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=la(e);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&la(t,a.source)===s)return a.name}return null}var pa=["command","agent"];function fa(t=Q()){let e=[],n=ve(t,"skills");if(Oe(n))for(let r of Sn(n)){let o=ve(n,r,"SKILL.md");Oe(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of pa){let o=ve(t,`${r}s`);if(Oe(o))for(let s of Sn(o))s.endsWith(".md")&&e.push({path:ve(o,s),type:r,source:"user"})}return e}function ma(t=Ve()){if(!Oe(t))return[];let e=[],n=ke(t);for(let r of n){let s=jr(t,r.path)?.key,i=ve(r.path,"skills");if(Oe(i))for(let a of Sn(i)){let c=ve(i,a,"SKILL.md");if(!Oe(c))continue;let l={path:c,type:"skill",source:"plugin"};s&&(l.plugin_key=s),e.push(l)}for(let a of pa){let c=ve(r.path,`${a}s`);if(Oe(c))for(let l of Sn(c)){if(!l.endsWith(".md"))continue;let d={path:ve(c,l),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function ga(t=ve(Q(),"settings.json")){if(!Oe(t))return[];try{let e=$m(t,"utf8"),r=JSON.parse(e).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function Sn(t){try{return Nm(t).filter(e=>!e.startsWith("."))}catch{return[]}}var wa=j.object({path:j.string(),type:j.enum(["skill","command","agent","hook"]),source:j.enum(["user","plugin"]),plugin_key:j.string().optional(),verdict:j.enum(["correct","misfit","outlier"]),recommended_type:j.string(),rationale:j.string(),confidence:j.enum(["high","med","low"])}),ba=j.record(j.string(),j.record(j.string(),j.number())),xx=j.object({inventory:j.object({user:ba,plugin:ba}),misfits:j.array(wa),briefs_written:j.number(),total_artifacts:j.number()}),Um=j.object({writeBriefs:j.boolean().optional(),scope:j.enum(["user","plugin","all"]).optional()}),Bm=["skill","command","agent"],Sa=["skill","command","agent","hook"];function Hm(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function jm(t){let e=()=>{let s={};for(let i of Sa)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:e(),plugin:e()};for(let s of t)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=t.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function Wm(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function Km(t){let e=t.filter(o=>o.source==="user"),n=t.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),e.length===0)r.push("(none discovered)");else for(let o of e)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1064
+ `,sourcePath:"private-framework/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};U();U();import{existsSync as Oe,readdirSync as Nm,readFileSync as $m}from"fs";import{join as ve}from"path";U();import{existsSync as Hr,readFileSync as Mm,readdirSync as Om,statSync as Dm}from"fs";import{join as Mt,resolve as la}from"path";import{existsSync as Im,mkdirSync as ZE,readFileSync as Cm,renameSync as ex,writeFileSync as tx,unlinkSync as nx}from"fs";U();function ca(t=Yt()){if(!Im(t))return wn();try{let e=Cm(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return wn();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return wn()}catch{return wn()}}function wn(){return{version:2,plugins:{},marketplaces:{}}}var Fm=5,da="cache",Ct;function ke(t=Ve()){Ct||(Ct=new Map);let e=Ct.get(t);if(e)return[...e];if(!Hr(t))return Ct.set(t,[]),[];let n=t===Ve()?Yt():Mt(t,".index.json"),r=ca(n),o=[];return ua(t,t,0,o,new Set,r.plugins),Ct.set(t,o),[...o]}function ua(t,e,n,r,o,s){if(n>Fm||o.has(e))return;if(o.add(e),Hr(Mt(e,".claude-plugin","plugin.json"))){let a=jr(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let l=s[a.key];if(!l||l.enabled===!1)return;r.push({type:"local",path:e});return}let c=s[a.key];if(c&&c.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=Om(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let c=Mt(e,a),l;try{l=Dm(c)}catch{continue}l.isDirectory()&&ua(t,c,n+1,r,o,s)}}function jr(t,e){if(!e.startsWith(t))return null;let n=e.slice(t.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===da&&r.length>=3){let s=r[1];if(s){let i=Mt(t,da,s),c=Lm(i,e)??r[2];if(c)return{layout:"cache",key:`${s}:${c}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function Lm(t,e){let n=Mt(t,".claude-plugin","marketplace.json");if(!Hr(n))return null;let r;try{r=JSON.parse(Mm(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=la(e);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&la(t,a.source)===s)return a.name}return null}var pa=["command","agent"];function fa(t=Q()){let e=[],n=ve(t,"skills");if(Oe(n))for(let r of Sn(n)){let o=ve(n,r,"SKILL.md");Oe(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of pa){let o=ve(t,`${r}s`);if(Oe(o))for(let s of Sn(o))s.endsWith(".md")&&e.push({path:ve(o,s),type:r,source:"user"})}return e}function ma(t=Ve()){if(!Oe(t))return[];let e=[],n=ke(t);for(let r of n){let s=jr(t,r.path)?.key,i=ve(r.path,"skills");if(Oe(i))for(let a of Sn(i)){let c=ve(i,a,"SKILL.md");if(!Oe(c))continue;let l={path:c,type:"skill",source:"plugin"};s&&(l.plugin_key=s),e.push(l)}for(let a of pa){let c=ve(r.path,`${a}s`);if(Oe(c))for(let l of Sn(c)){if(!l.endsWith(".md"))continue;let d={path:ve(c,l),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function ga(t=ve(Q(),"settings.json")){if(!Oe(t))return[];try{let e=$m(t,"utf8"),r=JSON.parse(e).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function Sn(t){try{return Nm(t).filter(e=>!e.startsWith("."))}catch{return[]}}var wa=j.object({path:j.string(),type:j.enum(["skill","command","agent","hook"]),source:j.enum(["user","plugin"]),plugin_key:j.string().optional(),verdict:j.enum(["correct","misfit","outlier"]),recommended_type:j.string(),rationale:j.string(),confidence:j.enum(["high","med","low"])}),ba=j.record(j.string(),j.record(j.string(),j.number())),xx=j.object({inventory:j.object({user:ba,plugin:ba}),misfits:j.array(wa),briefs_written:j.number(),total_artifacts:j.number()}),Um=j.object({writeBriefs:j.boolean().optional(),scope:j.enum(["user","plugin","all"]).optional()}),Bm=["skill","command","agent"],Sa=["skill","command","agent","hook"];function Hm(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function jm(t){let e=()=>{let s={};for(let i of Sa)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:e(),plugin:e()};for(let s of t)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=t.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function Wm(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function Km(t){let e=t.filter(o=>o.source==="user"),n=t.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),e.length===0)r.push("(none discovered)");else for(let o of e)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1065
1065
  `)}function Gm(t,e){let n=["","## Discovered hooks (audit only these)",""];if(n.push(`Settings file (use this absolute path verbatim in each verdict's \`path\` field): \`${t}\``),n.push(""),e.length===0)return n.push("(no hooks discovered)"),n.join(`
1066
1066
  `);for(let r of e){let o=`${r.event}-${r.index}`;n.push(`### Hook \`${o}\``),n.push(""),n.push("```json"),n.push(JSON.stringify(r.raw,null,2)),n.push("```"),n.push("")}return n.join(`
1067
1067
  `)}function qm(t,e){if(!e)return{kind:"failure",message:`${t}: no result`};if(e.schemaError)return{kind:"failure",message:`${t}: schema mismatch \u2014 ${e.schemaError.message}`};if(e.status!=="succeeded"){let n=e.error?` \u2014 ${e.error.message}`:"";return{kind:"failure",message:`${t}: ${e.status}${n}`}}return e.output?{kind:"success",output:e.output}:{kind:"failure",message:`${t}: no output`}}async function zm(t,e,n){let r=n?.apiKey,o=n?.callId,s=typeof t=="object"&&t!==null?t:{},i=Um.parse(s),a=i.writeBriefs??!0,c=i.scope??"all",l=Hm(c);if(!e?.sessionId)throw new Error("audit-fit requires a parent session with sessionId");let d=e.sessionId,u=q("audit-fit"),p={skill:u["01-skill-inspector.md"],command:u["02-command-inspector.md"],agent:u["03-agent-inspector.md"],hook:u["04-hook-inspector.md"]};for(let h of Sa)if(!p[h])throw new Error(`audit-fit skill missing inspector prompt for ${h}`);let f=l.runUserDiscovery?fa():[],g=l.runPluginDiscovery?ma():[],m={skill:[],command:[],agent:[]};for(let h of[...f,...g])m[h.type].push(h);let k=new N({apiKey:r}),_=()=>async h=>re.allowedTools.includes(h)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${h} not allowed for audit-fit inspectors. Allowed tools: ${re.allowedTools.join(", ")}`},y=[];for(let h of Bm){let A=m[h];if(A.length===0)continue;let I=p[h];I&&y.push({type:h,prompt:`${I}
@@ -1093,7 +1093,7 @@ ${A.rationale}
1093
1093
  ---
1094
1094
  Generated by audit-fit on ${new Date().toISOString().split(".")[0]}Z
1095
1095
  `;await ya(T,O),x++}}let P=Je();await ha(P,{recursive:!0});let C=h=>{let A=0;for(let I of Object.values(h))for(let T of Object.values(I))A+=T;return A},M=h=>{let A=b.user[h]??{},I=b.plugin[h]??{},T=O=>Object.values(O).reduce((W,K)=>W+K,0);return T(A)+T(I)},F={timestamp:new Date().toISOString(),surface:"afk",scope:c,total_artifacts:S.length,misfits_count:E.length,briefs_written:x,by_source:{user:C(b.user),plugin:C(b.plugin)},by_type:{skill:M("skill"),command:M("command"),agent:M("agent"),hook:M("hook")}},w=Wr(P,"audit-fit-telemetry.jsonl");return await ya(w,JSON.stringify(F)+`
1096
- `),{inventory:b,misfits:E,briefs_written:x,total_artifacts:S.length}}var Jm={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:zm,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};ee(Jm);import{z as R}from"zod";import{execFile as Qm}from"node:child_process";import{promisify as Zm}from"node:util";import{tmpdir as eg}from"node:os";import{join as Ea}from"node:path";function ka(t){return t.confidence<.5?{verify:!0,reason:`low confidence (${t.confidence.toFixed(2)} < ${.5})`}:t.boundary_flag&&t.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${t.boundary_flag}`}:t.coverage_gaps&&t.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${t.coverage_gaps.length===1?"":"s"}: ${t.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${t.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as Vm}from"node:url";import{dirname as Ym}from"node:path";var Xm=Vm(import.meta.url),Cx=Ym(Xm),va={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"agent-framework-private/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function _a(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}function tg(t){let e=t.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(e)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(e)?"REFUTED":"INCONCLUSIVE"}var ng=R.object({verifications:R.array(R.object({claim:R.string().optional(),verdict:R.string(),evidence:R.string().optional()}))});function rg(t){let e=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<t.length;i++){let a=t[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(e.push(t.slice(r,i+1)),r=-1))}return e}function og(t){let e=rg(t);for(let n of e){let r;try{r=JSON.parse(n)}catch{continue}let o=ng.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:tg(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${e.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${t.slice(0,300)}`)}var kn=Zm(Qm),Ta=R.object({id:R.string(),claim:R.string(),confidence:R.number().min(0).max(1),evidence_sources:R.array(R.string()),location:R.string().optional(),proposed_fix:R.string().optional(),coverage_gaps:R.array(R.string()).nullish().transform(t=>t??void 0),boundary_flag:R.string().nullish().transform(t=>t??void 0)}),sg=R.object({hypothesis_id:R.string(),claim:R.string(),verdict:R.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:R.string(),gate_reason:R.string()}),Ra=R.object({hypothesis_id:R.string(),reproducer_passed:R.boolean(),regressions:R.array(R.string()),confidence:R.number().min(0).max(1),verification_log:R.string()}),ig=R.enum(["crash","regression","logic-error","flaky","environment","unknown"]),ag=R.object({failure_type:ig,error_signature:R.string(),affected_area:R.string()}),cg=R.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),Jx=R.object({reproducer:R.string().optional(),triage:ag.optional(),hypotheses:R.array(Ta),premise_verifications:R.array(sg).optional(),winner:R.object({hypothesis_id:R.string(),verification_log:R.string(),proposed_fix:R.string()}).optional(),verification_results:R.array(Ra).optional(),outcome:cg.optional(),recommended_next_skill:R.enum(["spec"]).optional()});async function lg(t,e){let n=t.map(c=>({hypothesis:c,decision:ka(c)})).filter(c=>c.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{let c=await e(n.map(l=>l.hypothesis.claim));r=Array.isArray(c)?c:[]}catch(c){o=c instanceof Error?c.message:String(c)}let s=n.map((c,l)=>{let d=r[l];return o!==void 0?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:c.decision.reason}:d?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:d.verdict,evidence:d.evidence,gate_reason:c.decision.reason}:{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:c.decision.reason}}),i=new Set(s.filter(c=>c.verdict==="REFUTED").map(c=>c.hypothesis_id)),a=i.size===0?t:t.filter(c=>!i.has(c.id));return{premise_verifications:s,hypotheses_to_test:a}}async function dg(t,e,n){let r=n?.apiKey,o=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let G=t;if(typeof G.failure=="string")return{failure:G.failure,repoPath:G.repoPath||process.cwd(),context:G.context||"",maxHypotheses:Math.min(G.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=e.sessionId,i=q("diagnose"),a=i["system.md"],c=i["research.md"],l=i["hypothesis.md"],d=i["verify.md"];if(!a||!c||!l||!d)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let u=new N({apiKey:r}),p=mg(o.context),f=ug(o.failure,o.context),g=`Triage:
1096
+ `),{inventory:b,misfits:E,briefs_written:x,total_artifacts:S.length}}var Jm={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:zm,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};ee(Jm);import{z as R}from"zod";import{execFile as Qm}from"node:child_process";import{promisify as Zm}from"node:util";import{tmpdir as eg}from"node:os";import{join as Ea}from"node:path";function ka(t){return t.confidence<.5?{verify:!0,reason:`low confidence (${t.confidence.toFixed(2)} < ${.5})`}:t.boundary_flag&&t.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${t.boundary_flag}`}:t.coverage_gaps&&t.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${t.coverage_gaps.length===1?"":"s"}: ${t.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${t.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as Vm}from"node:url";import{dirname as Ym}from"node:path";var Xm=Vm(import.meta.url),Cx=Ym(Xm),va={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"private-framework/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function _a(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}function tg(t){let e=t.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(e)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(e)?"REFUTED":"INCONCLUSIVE"}var ng=R.object({verifications:R.array(R.object({claim:R.string().optional(),verdict:R.string(),evidence:R.string().optional()}))});function rg(t){let e=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<t.length;i++){let a=t[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(e.push(t.slice(r,i+1)),r=-1))}return e}function og(t){let e=rg(t);for(let n of e){let r;try{r=JSON.parse(n)}catch{continue}let o=ng.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:tg(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${e.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${t.slice(0,300)}`)}var kn=Zm(Qm),Ta=R.object({id:R.string(),claim:R.string(),confidence:R.number().min(0).max(1),evidence_sources:R.array(R.string()),location:R.string().optional(),proposed_fix:R.string().optional(),coverage_gaps:R.array(R.string()).nullish().transform(t=>t??void 0),boundary_flag:R.string().nullish().transform(t=>t??void 0)}),sg=R.object({hypothesis_id:R.string(),claim:R.string(),verdict:R.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:R.string(),gate_reason:R.string()}),Ra=R.object({hypothesis_id:R.string(),reproducer_passed:R.boolean(),regressions:R.array(R.string()),confidence:R.number().min(0).max(1),verification_log:R.string()}),ig=R.enum(["crash","regression","logic-error","flaky","environment","unknown"]),ag=R.object({failure_type:ig,error_signature:R.string(),affected_area:R.string()}),cg=R.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),Jx=R.object({reproducer:R.string().optional(),triage:ag.optional(),hypotheses:R.array(Ta),premise_verifications:R.array(sg).optional(),winner:R.object({hypothesis_id:R.string(),verification_log:R.string(),proposed_fix:R.string()}).optional(),verification_results:R.array(Ra).optional(),outcome:cg.optional(),recommended_next_skill:R.enum(["spec"]).optional()});async function lg(t,e){let n=t.map(c=>({hypothesis:c,decision:ka(c)})).filter(c=>c.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{let c=await e(n.map(l=>l.hypothesis.claim));r=Array.isArray(c)?c:[]}catch(c){o=c instanceof Error?c.message:String(c)}let s=n.map((c,l)=>{let d=r[l];return o!==void 0?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:c.decision.reason}:d?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:d.verdict,evidence:d.evidence,gate_reason:c.decision.reason}:{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:c.decision.reason}}),i=new Set(s.filter(c=>c.verdict==="REFUTED").map(c=>c.hypothesis_id)),a=i.size===0?t:t.filter(c=>!i.has(c.id));return{premise_verifications:s,hypotheses_to_test:a}}async function dg(t,e,n){let r=n?.apiKey,o=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let G=t;if(typeof G.failure=="string")return{failure:G.failure,repoPath:G.repoPath||process.cwd(),context:G.context||"",maxHypotheses:Math.min(G.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=e.sessionId,i=q("diagnose"),a=i["system.md"],c=i["research.md"],l=i["hypothesis.md"],d=i["verify.md"];if(!a||!c||!l||!d)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let u=new N({apiKey:r}),p=mg(o.context),f=ug(o.failure,o.context),g=`Triage:
1097
1097
  failure_type: ${f.failure_type}
1098
1098
  error_signature: ${f.error_signature}
1099
1099
  affected_area: ${f.affected_area}`,k=`${re.systemPrompt}
package/dist/telegram.mjs CHANGED
@@ -1095,7 +1095,7 @@ Unless the dispatcher specifies a different schema, return:
1095
1095
  **\`boundary_flag\` is required.** If nothing applies, emit \`"none"\` \u2014 do not omit the field. Treat missing as \`"none"\` is acceptable on the orchestrator side, but emit the field explicitly so downstream synthesizers and validators do not see \`null\`.
1096
1096
 
1097
1097
  If \`scope_check\` flags implementation (non-git), the orchestrator should dispatch a different sub-agent type for follow-up. Do not re-dispatch the same task through \`research-agent\`.
1098
- `,sourcePath:"agent-framework-private/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};H();H();import{existsSync as Fe,readdirSync as ty,readFileSync as ny}from"fs";import{join as xe}from"path";H();import{existsSync as ko,readFileSync as Yh,readdirSync as Xh,statSync as Qh}from"fs";import{join as Wt,resolve as Tc}from"path";import{existsSync as Jh,mkdirSync as aI,readFileSync as Vh,renameSync as cI,writeFileSync as lI,unlinkSync as dI}from"fs";H();function xc(t=Zt()){if(!Jh(t))return qn();try{let e=Vh(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return qn();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return qn()}catch{return qn()}}function qn(){return{version:2,plugins:{},marketplaces:{}}}var Zh=5,Rc="cache",jt;function Ae(t=et()){jt||(jt=new Map);let e=jt.get(t);if(e)return[...e];if(!ko(t))return jt.set(t,[]),[];let n=t===et()?Zt():Wt(t,".index.json"),r=xc(n),o=[];return Ic(t,t,0,o,new Set,r.plugins),jt.set(t,o),[...o]}function Ic(t,e,n,r,o,s){if(n>Zh||o.has(e))return;if(o.add(e),ko(Wt(e,".claude-plugin","plugin.json"))){let a=vo(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let l=s[a.key];if(!l||l.enabled===!1)return;r.push({type:"local",path:e});return}let c=s[a.key];if(c&&c.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=Xh(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let c=Wt(e,a),l;try{l=Qh(c)}catch{continue}l.isDirectory()&&Ic(t,c,n+1,r,o,s)}}function vo(t,e){if(!e.startsWith(t))return null;let n=e.slice(t.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===Rc&&r.length>=3){let s=r[1];if(s){let i=Wt(t,Rc,s),c=ey(i,e)??r[2];if(c)return{layout:"cache",key:`${s}:${c}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function ey(t,e){let n=Wt(t,".claude-plugin","marketplace.json");if(!ko(n))return null;let r;try{r=JSON.parse(Yh(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=Tc(e);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&Tc(t,a.source)===s)return a.name}return null}var Pc=["command","agent"];function Cc(t=te()){let e=[],n=xe(t,"skills");if(Fe(n))for(let r of zn(n)){let o=xe(n,r,"SKILL.md");Fe(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of Pc){let o=xe(t,`${r}s`);if(Fe(o))for(let s of zn(o))s.endsWith(".md")&&e.push({path:xe(o,s),type:r,source:"user"})}return e}function Mc(t=et()){if(!Fe(t))return[];let e=[],n=Ae(t);for(let r of n){let s=vo(t,r.path)?.key,i=xe(r.path,"skills");if(Fe(i))for(let a of zn(i)){let c=xe(i,a,"SKILL.md");if(!Fe(c))continue;let l={path:c,type:"skill",source:"plugin"};s&&(l.plugin_key=s),e.push(l)}for(let a of Pc){let c=xe(r.path,`${a}s`);if(Fe(c))for(let l of zn(c)){if(!l.endsWith(".md"))continue;let d={path:xe(c,l),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function Oc(t=xe(te(),"settings.json")){if(!Fe(t))return[];try{let e=ny(t,"utf8"),r=JSON.parse(e).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function zn(t){try{return ty(t).filter(e=>!e.startsWith("."))}catch{return[]}}var Nc=G.object({path:G.string(),type:G.enum(["skill","command","agent","hook"]),source:G.enum(["user","plugin"]),plugin_key:G.string().optional(),verdict:G.enum(["correct","misfit","outlier"]),recommended_type:G.string(),rationale:G.string(),confidence:G.enum(["high","med","low"])}),Lc=G.record(G.string(),G.record(G.string(),G.number())),OI=G.object({inventory:G.object({user:Lc,plugin:Lc}),misfits:G.array(Nc),briefs_written:G.number(),total_artifacts:G.number()}),ry=G.object({writeBriefs:G.boolean().optional(),scope:G.enum(["user","plugin","all"]).optional()}),oy=["skill","command","agent"],$c=["skill","command","agent","hook"];function sy(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function iy(t){let e=()=>{let s={};for(let i of $c)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:e(),plugin:e()};for(let s of t)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=t.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function ay(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function cy(t){let e=t.filter(o=>o.source==="user"),n=t.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),e.length===0)r.push("(none discovered)");else for(let o of e)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1098
+ `,sourcePath:"private-framework/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};H();H();import{existsSync as Fe,readdirSync as ty,readFileSync as ny}from"fs";import{join as xe}from"path";H();import{existsSync as ko,readFileSync as Yh,readdirSync as Xh,statSync as Qh}from"fs";import{join as Wt,resolve as Tc}from"path";import{existsSync as Jh,mkdirSync as aI,readFileSync as Vh,renameSync as cI,writeFileSync as lI,unlinkSync as dI}from"fs";H();function xc(t=Zt()){if(!Jh(t))return qn();try{let e=Vh(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return qn();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return qn()}catch{return qn()}}function qn(){return{version:2,plugins:{},marketplaces:{}}}var Zh=5,Rc="cache",jt;function Ae(t=et()){jt||(jt=new Map);let e=jt.get(t);if(e)return[...e];if(!ko(t))return jt.set(t,[]),[];let n=t===et()?Zt():Wt(t,".index.json"),r=xc(n),o=[];return Ic(t,t,0,o,new Set,r.plugins),jt.set(t,o),[...o]}function Ic(t,e,n,r,o,s){if(n>Zh||o.has(e))return;if(o.add(e),ko(Wt(e,".claude-plugin","plugin.json"))){let a=vo(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let l=s[a.key];if(!l||l.enabled===!1)return;r.push({type:"local",path:e});return}let c=s[a.key];if(c&&c.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=Xh(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let c=Wt(e,a),l;try{l=Qh(c)}catch{continue}l.isDirectory()&&Ic(t,c,n+1,r,o,s)}}function vo(t,e){if(!e.startsWith(t))return null;let n=e.slice(t.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===Rc&&r.length>=3){let s=r[1];if(s){let i=Wt(t,Rc,s),c=ey(i,e)??r[2];if(c)return{layout:"cache",key:`${s}:${c}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function ey(t,e){let n=Wt(t,".claude-plugin","marketplace.json");if(!ko(n))return null;let r;try{r=JSON.parse(Yh(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=Tc(e);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&Tc(t,a.source)===s)return a.name}return null}var Pc=["command","agent"];function Cc(t=te()){let e=[],n=xe(t,"skills");if(Fe(n))for(let r of zn(n)){let o=xe(n,r,"SKILL.md");Fe(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of Pc){let o=xe(t,`${r}s`);if(Fe(o))for(let s of zn(o))s.endsWith(".md")&&e.push({path:xe(o,s),type:r,source:"user"})}return e}function Mc(t=et()){if(!Fe(t))return[];let e=[],n=Ae(t);for(let r of n){let s=vo(t,r.path)?.key,i=xe(r.path,"skills");if(Fe(i))for(let a of zn(i)){let c=xe(i,a,"SKILL.md");if(!Fe(c))continue;let l={path:c,type:"skill",source:"plugin"};s&&(l.plugin_key=s),e.push(l)}for(let a of Pc){let c=xe(r.path,`${a}s`);if(Fe(c))for(let l of zn(c)){if(!l.endsWith(".md"))continue;let d={path:xe(c,l),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function Oc(t=xe(te(),"settings.json")){if(!Fe(t))return[];try{let e=ny(t,"utf8"),r=JSON.parse(e).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function zn(t){try{return ty(t).filter(e=>!e.startsWith("."))}catch{return[]}}var Nc=G.object({path:G.string(),type:G.enum(["skill","command","agent","hook"]),source:G.enum(["user","plugin"]),plugin_key:G.string().optional(),verdict:G.enum(["correct","misfit","outlier"]),recommended_type:G.string(),rationale:G.string(),confidence:G.enum(["high","med","low"])}),Lc=G.record(G.string(),G.record(G.string(),G.number())),OI=G.object({inventory:G.object({user:Lc,plugin:Lc}),misfits:G.array(Nc),briefs_written:G.number(),total_artifacts:G.number()}),ry=G.object({writeBriefs:G.boolean().optional(),scope:G.enum(["user","plugin","all"]).optional()}),oy=["skill","command","agent"],$c=["skill","command","agent","hook"];function sy(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function iy(t){let e=()=>{let s={};for(let i of $c)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:e(),plugin:e()};for(let s of t)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=t.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function ay(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function cy(t){let e=t.filter(o=>o.source==="user"),n=t.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),e.length===0)r.push("(none discovered)");else for(let o of e)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1099
1099
  `)}function ly(t,e){let n=["","## Discovered hooks (audit only these)",""];if(n.push(`Settings file (use this absolute path verbatim in each verdict's \`path\` field): \`${t}\``),n.push(""),e.length===0)return n.push("(no hooks discovered)"),n.join(`
1100
1100
  `);for(let r of e){let o=`${r.event}-${r.index}`;n.push(`### Hook \`${o}\``),n.push(""),n.push("```json"),n.push(JSON.stringify(r.raw,null,2)),n.push("```"),n.push("")}return n.join(`
1101
1101
  `)}function dy(t,e){if(!e)return{kind:"failure",message:`${t}: no result`};if(e.schemaError)return{kind:"failure",message:`${t}: schema mismatch \u2014 ${e.schemaError.message}`};if(e.status!=="succeeded"){let n=e.error?` \u2014 ${e.error.message}`:"";return{kind:"failure",message:`${t}: ${e.status}${n}`}}return e.output?{kind:"success",output:e.output}:{kind:"failure",message:`${t}: no output`}}async function uy(t,e,n){let r=n?.apiKey,o=n?.callId,s=typeof t=="object"&&t!==null?t:{},i=ry.parse(s),a=i.writeBriefs??!0,c=i.scope??"all",l=sy(c);if(!e?.sessionId)throw new Error("audit-fit requires a parent session with sessionId");let d=e.sessionId,u=q("audit-fit"),p={skill:u["01-skill-inspector.md"],command:u["02-command-inspector.md"],agent:u["03-agent-inspector.md"],hook:u["04-hook-inspector.md"]};for(let E of $c)if(!p[E])throw new Error(`audit-fit skill missing inspector prompt for ${E}`);let m=l.runUserDiscovery?Cc():[],h=l.runPluginDiscovery?Mc():[],g={skill:[],command:[],agent:[]};for(let E of[...m,...h])g[E.type].push(E);let b=new N({apiKey:r}),k=()=>async E=>le.allowedTools.includes(E)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${E} not allowed for audit-fit inspectors. Allowed tools: ${le.allowedTools.join(", ")}`},y=[];for(let E of oy){let R=g[E];if(R.length===0)continue;let M=p[E];M&&y.push({type:E,prompt:`${M}
@@ -1127,7 +1127,7 @@ ${R.rationale}
1127
1127
  ---
1128
1128
  Generated by audit-fit on ${new Date().toISOString().split(".")[0]}Z
1129
1129
  `;await Fc(C,F),x++}}let T=Ze();await Dc(T,{recursive:!0});let I=E=>{let R=0;for(let M of Object.values(E))for(let C of Object.values(M))R+=C;return R},P=E=>{let R=S.user[E]??{},M=S.plugin[E]??{},C=F=>Object.values(F).reduce((j,K)=>j+K,0);return C(R)+C(M)},O={timestamp:new Date().toISOString(),surface:"afk",scope:c,total_artifacts:w.length,misfits_count:v.length,briefs_written:x,by_source:{user:I(S.user),plugin:I(S.plugin)},by_type:{skill:P("skill"),command:P("command"),agent:P("agent"),hook:P("hook")}},A=_o(T,"audit-fit-telemetry.jsonl");return await Fc(A,JSON.stringify(O)+`
1130
- `),{inventory:S,misfits:v,briefs_written:x,total_artifacts:w.length}}var py={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:uy,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};oe(py);import{z as D}from"zod";import{execFile as hy}from"node:child_process";import{promisify as yy}from"node:util";import{tmpdir as by}from"node:os";import{join as jc}from"node:path";function Uc(t){return t.confidence<.5?{verify:!0,reason:`low confidence (${t.confidence.toFixed(2)} < ${.5})`}:t.boundary_flag&&t.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${t.boundary_flag}`}:t.coverage_gaps&&t.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${t.coverage_gaps.length===1?"":"s"}: ${t.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${t.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as fy}from"node:url";import{dirname as my}from"node:path";var gy=fy(import.meta.url),UI=my(gy),Hc={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"agent-framework-private/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function Bc(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}function wy(t){let e=t.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(e)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(e)?"REFUTED":"INCONCLUSIVE"}var Sy=D.object({verifications:D.array(D.object({claim:D.string().optional(),verdict:D.string(),evidence:D.string().optional()}))});function ky(t){let e=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<t.length;i++){let a=t[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(e.push(t.slice(r,i+1)),r=-1))}return e}function vy(t){let e=ky(t);for(let n of e){let r;try{r=JSON.parse(n)}catch{continue}let o=Sy.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:wy(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${e.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${t.slice(0,300)}`)}var Jn=yy(hy),Gc=D.object({id:D.string(),claim:D.string(),confidence:D.number().min(0).max(1),evidence_sources:D.array(D.string()),location:D.string().optional(),proposed_fix:D.string().optional(),coverage_gaps:D.array(D.string()).nullish().transform(t=>t??void 0),boundary_flag:D.string().nullish().transform(t=>t??void 0)}),_y=D.object({hypothesis_id:D.string(),claim:D.string(),verdict:D.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:D.string(),gate_reason:D.string()}),qc=D.object({hypothesis_id:D.string(),reproducer_passed:D.boolean(),regressions:D.array(D.string()),confidence:D.number().min(0).max(1),verification_log:D.string()}),Ey=D.enum(["crash","regression","logic-error","flaky","environment","unknown"]),Ay=D.object({failure_type:Ey,error_signature:D.string(),affected_area:D.string()}),xy=D.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),nP=D.object({reproducer:D.string().optional(),triage:Ay.optional(),hypotheses:D.array(Gc),premise_verifications:D.array(_y).optional(),winner:D.object({hypothesis_id:D.string(),verification_log:D.string(),proposed_fix:D.string()}).optional(),verification_results:D.array(qc).optional(),outcome:xy.optional(),recommended_next_skill:D.enum(["spec"]).optional()});async function Ty(t,e){let n=t.map(c=>({hypothesis:c,decision:Uc(c)})).filter(c=>c.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{let c=await e(n.map(l=>l.hypothesis.claim));r=Array.isArray(c)?c:[]}catch(c){o=c instanceof Error?c.message:String(c)}let s=n.map((c,l)=>{let d=r[l];return o!==void 0?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:c.decision.reason}:d?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:d.verdict,evidence:d.evidence,gate_reason:c.decision.reason}:{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:c.decision.reason}}),i=new Set(s.filter(c=>c.verdict==="REFUTED").map(c=>c.hypothesis_id)),a=i.size===0?t:t.filter(c=>!i.has(c.id));return{premise_verifications:s,hypotheses_to_test:a}}async function Ry(t,e,n){let r=n?.apiKey,o=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let J=t;if(typeof J.failure=="string")return{failure:J.failure,repoPath:J.repoPath||process.cwd(),context:J.context||"",maxHypotheses:Math.min(J.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=e.sessionId,i=q("diagnose"),a=i["system.md"],c=i["research.md"],l=i["hypothesis.md"],d=i["verify.md"];if(!a||!c||!l||!d)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let u=new N({apiKey:r}),p=My(o.context),m=Iy(o.failure,o.context),h=`Triage:
1130
+ `),{inventory:S,misfits:v,briefs_written:x,total_artifacts:w.length}}var py={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:uy,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};oe(py);import{z as D}from"zod";import{execFile as hy}from"node:child_process";import{promisify as yy}from"node:util";import{tmpdir as by}from"node:os";import{join as jc}from"node:path";function Uc(t){return t.confidence<.5?{verify:!0,reason:`low confidence (${t.confidence.toFixed(2)} < ${.5})`}:t.boundary_flag&&t.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${t.boundary_flag}`}:t.coverage_gaps&&t.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${t.coverage_gaps.length===1?"":"s"}: ${t.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${t.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as fy}from"node:url";import{dirname as my}from"node:path";var gy=fy(import.meta.url),UI=my(gy),Hc={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"private-framework/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function Bc(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}function wy(t){let e=t.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(e)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(e)?"REFUTED":"INCONCLUSIVE"}var Sy=D.object({verifications:D.array(D.object({claim:D.string().optional(),verdict:D.string(),evidence:D.string().optional()}))});function ky(t){let e=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<t.length;i++){let a=t[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(e.push(t.slice(r,i+1)),r=-1))}return e}function vy(t){let e=ky(t);for(let n of e){let r;try{r=JSON.parse(n)}catch{continue}let o=Sy.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:wy(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${e.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${t.slice(0,300)}`)}var Jn=yy(hy),Gc=D.object({id:D.string(),claim:D.string(),confidence:D.number().min(0).max(1),evidence_sources:D.array(D.string()),location:D.string().optional(),proposed_fix:D.string().optional(),coverage_gaps:D.array(D.string()).nullish().transform(t=>t??void 0),boundary_flag:D.string().nullish().transform(t=>t??void 0)}),_y=D.object({hypothesis_id:D.string(),claim:D.string(),verdict:D.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:D.string(),gate_reason:D.string()}),qc=D.object({hypothesis_id:D.string(),reproducer_passed:D.boolean(),regressions:D.array(D.string()),confidence:D.number().min(0).max(1),verification_log:D.string()}),Ey=D.enum(["crash","regression","logic-error","flaky","environment","unknown"]),Ay=D.object({failure_type:Ey,error_signature:D.string(),affected_area:D.string()}),xy=D.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),nP=D.object({reproducer:D.string().optional(),triage:Ay.optional(),hypotheses:D.array(Gc),premise_verifications:D.array(_y).optional(),winner:D.object({hypothesis_id:D.string(),verification_log:D.string(),proposed_fix:D.string()}).optional(),verification_results:D.array(qc).optional(),outcome:xy.optional(),recommended_next_skill:D.enum(["spec"]).optional()});async function Ty(t,e){let n=t.map(c=>({hypothesis:c,decision:Uc(c)})).filter(c=>c.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{let c=await e(n.map(l=>l.hypothesis.claim));r=Array.isArray(c)?c:[]}catch(c){o=c instanceof Error?c.message:String(c)}let s=n.map((c,l)=>{let d=r[l];return o!==void 0?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:c.decision.reason}:d?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:d.verdict,evidence:d.evidence,gate_reason:c.decision.reason}:{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:c.decision.reason}}),i=new Set(s.filter(c=>c.verdict==="REFUTED").map(c=>c.hypothesis_id)),a=i.size===0?t:t.filter(c=>!i.has(c.id));return{premise_verifications:s,hypotheses_to_test:a}}async function Ry(t,e,n){let r=n?.apiKey,o=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let J=t;if(typeof J.failure=="string")return{failure:J.failure,repoPath:J.repoPath||process.cwd(),context:J.context||"",maxHypotheses:Math.min(J.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=e.sessionId,i=q("diagnose"),a=i["system.md"],c=i["research.md"],l=i["hypothesis.md"],d=i["verify.md"];if(!a||!c||!l||!d)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let u=new N({apiKey:r}),p=My(o.context),m=Iy(o.failure,o.context),h=`Triage:
1131
1131
  failure_type: ${m.failure_type}
1132
1132
  error_signature: ${m.error_signature}
1133
1133
  affected_area: ${m.affected_area}`,b=`${le.systemPrompt}
package/dist/threads.mjs CHANGED
@@ -1076,7 +1076,7 @@ Unless the dispatcher specifies a different schema, return:
1076
1076
  **\`boundary_flag\` is required.** If nothing applies, emit \`"none"\` \u2014 do not omit the field. Treat missing as \`"none"\` is acceptable on the orchestrator side, but emit the field explicitly so downstream synthesizers and validators do not see \`null\`.
1077
1077
 
1078
1078
  If \`scope_check\` flags implementation (non-git), the orchestrator should dispatch a different sub-agent type for follow-up. Do not re-dispatch the same task through \`research-agent\`.
1079
- `,sourcePath:"agent-framework-private/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};H();H();import{existsSync as De,readdirSync as eh,readFileSync as th}from"fs";import{join as Ee}from"path";H();import{existsSync as Kr,readFileSync as Vg,readdirSync as Yg,statSync as Xg}from"fs";import{join as Mt,resolve as Ha}from"path";import{existsSync as zg,mkdirSync as VA,readFileSync as Jg,renameSync as YA,writeFileSync as XA,unlinkSync as QA}from"fs";H();function Ua(t=$t()){if(!zg(t))return Tn();try{let e=Jg(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return Tn();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return Tn()}catch{return Tn()}}function Tn(){return{version:2,plugins:{},marketplaces:{}}}var Qg=5,Ba="cache",Ot;function _e(t=Ve()){Ot||(Ot=new Map);let e=Ot.get(t);if(e)return[...e];if(!Kr(t))return Ot.set(t,[]),[];let n=t===Ve()?$t():Mt(t,".index.json"),r=Ua(n),o=[];return ja(t,t,0,o,new Set,r.plugins),Ot.set(t,o),[...o]}function ja(t,e,n,r,o,s){if(n>Qg||o.has(e))return;if(o.add(e),Kr(Mt(e,".claude-plugin","plugin.json"))){let a=Wr(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let l=s[a.key];if(!l||l.enabled===!1)return;r.push({type:"local",path:e});return}let c=s[a.key];if(c&&c.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=Yg(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let c=Mt(e,a),l;try{l=Xg(c)}catch{continue}l.isDirectory()&&ja(t,c,n+1,r,o,s)}}function Wr(t,e){if(!e.startsWith(t))return null;let n=e.slice(t.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===Ba&&r.length>=3){let s=r[1];if(s){let i=Mt(t,Ba,s),c=Zg(i,e)??r[2];if(c)return{layout:"cache",key:`${s}:${c}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function Zg(t,e){let n=Mt(t,".claude-plugin","marketplace.json");if(!Kr(n))return null;let r;try{r=JSON.parse(Vg(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=Ha(e);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&Ha(t,a.source)===s)return a.name}return null}var Ka=["command","agent"];function Wa(t=Z()){let e=[],n=Ee(t,"skills");if(De(n))for(let r of Rn(n)){let o=Ee(n,r,"SKILL.md");De(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of Ka){let o=Ee(t,`${r}s`);if(De(o))for(let s of Rn(o))s.endsWith(".md")&&e.push({path:Ee(o,s),type:r,source:"user"})}return e}function qa(t=Ve()){if(!De(t))return[];let e=[],n=_e(t);for(let r of n){let s=Wr(t,r.path)?.key,i=Ee(r.path,"skills");if(De(i))for(let a of Rn(i)){let c=Ee(i,a,"SKILL.md");if(!De(c))continue;let l={path:c,type:"skill",source:"plugin"};s&&(l.plugin_key=s),e.push(l)}for(let a of Ka){let c=Ee(r.path,`${a}s`);if(De(c))for(let l of Rn(c)){if(!l.endsWith(".md"))continue;let d={path:Ee(c,l),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function Ga(t=Ee(Z(),"settings.json")){if(!De(t))return[];try{let e=th(t,"utf8"),r=JSON.parse(e).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function Rn(t){try{return eh(t).filter(e=>!e.startsWith("."))}catch{return[]}}var Ya=B.object({path:B.string(),type:B.enum(["skill","command","agent","hook"]),source:B.enum(["user","plugin"]),plugin_key:B.string().optional(),verdict:B.enum(["correct","misfit","outlier"]),recommended_type:B.string(),rationale:B.string(),confidence:B.enum(["high","med","low"])}),Va=B.record(B.string(),B.record(B.string(),B.number())),kT=B.object({inventory:B.object({user:Va,plugin:Va}),misfits:B.array(Ya),briefs_written:B.number(),total_artifacts:B.number()}),nh=B.object({writeBriefs:B.boolean().optional(),scope:B.enum(["user","plugin","all"]).optional()}),rh=["skill","command","agent"],Xa=["skill","command","agent","hook"];function oh(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function sh(t){let e=()=>{let s={};for(let i of Xa)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:e(),plugin:e()};for(let s of t)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=t.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function ih(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function ah(t){let e=t.filter(o=>o.source==="user"),n=t.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),e.length===0)r.push("(none discovered)");else for(let o of e)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1079
+ `,sourcePath:"private-framework/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};H();H();import{existsSync as De,readdirSync as eh,readFileSync as th}from"fs";import{join as Ee}from"path";H();import{existsSync as Kr,readFileSync as Vg,readdirSync as Yg,statSync as Xg}from"fs";import{join as Mt,resolve as Ha}from"path";import{existsSync as zg,mkdirSync as VA,readFileSync as Jg,renameSync as YA,writeFileSync as XA,unlinkSync as QA}from"fs";H();function Ua(t=$t()){if(!zg(t))return Tn();try{let e=Jg(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return Tn();let r=n,o=r.plugins&&typeof r.plugins=="object"?r.plugins:{};if(r.version===1)return{version:2,plugins:o,marketplaces:{}};if(r.version===2){let s=r.marketplaces&&typeof r.marketplaces=="object"?r.marketplaces:{};return{version:2,plugins:o,marketplaces:s}}return Tn()}catch{return Tn()}}function Tn(){return{version:2,plugins:{},marketplaces:{}}}var Qg=5,Ba="cache",Ot;function _e(t=Ve()){Ot||(Ot=new Map);let e=Ot.get(t);if(e)return[...e];if(!Kr(t))return Ot.set(t,[]),[];let n=t===Ve()?$t():Mt(t,".index.json"),r=Ua(n),o=[];return ja(t,t,0,o,new Set,r.plugins),Ot.set(t,o),[...o]}function ja(t,e,n,r,o,s){if(n>Qg||o.has(e))return;if(o.add(e),Kr(Mt(e,".claude-plugin","plugin.json"))){let a=Wr(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let l=s[a.key];if(!l||l.enabled===!1)return;r.push({type:"local",path:e});return}let c=s[a.key];if(c&&c.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=Yg(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let c=Mt(e,a),l;try{l=Xg(c)}catch{continue}l.isDirectory()&&ja(t,c,n+1,r,o,s)}}function Wr(t,e){if(!e.startsWith(t))return null;let n=e.slice(t.length).replace(/^\/+/,"");if(!n)return null;let r=n.split("/").filter(s=>s.length>0);if(r.length===0)return null;if(r[0]===Ba&&r.length>=3){let s=r[1];if(s){let i=Mt(t,Ba,s),c=Zg(i,e)??r[2];if(c)return{layout:"cache",key:`${s}:${c}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function Zg(t,e){let n=Mt(t,".claude-plugin","marketplace.json");if(!Kr(n))return null;let r;try{r=JSON.parse(Vg(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=Ha(e);for(let i of o){if(!i||typeof i!="object")continue;let a=i;if(!(typeof a.name!="string"||typeof a.source!="string")&&!(!a.source.startsWith("./")&&!a.source.startsWith("../"))&&Ha(t,a.source)===s)return a.name}return null}var Ka=["command","agent"];function Wa(t=Z()){let e=[],n=Ee(t,"skills");if(De(n))for(let r of Rn(n)){let o=Ee(n,r,"SKILL.md");De(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of Ka){let o=Ee(t,`${r}s`);if(De(o))for(let s of Rn(o))s.endsWith(".md")&&e.push({path:Ee(o,s),type:r,source:"user"})}return e}function qa(t=Ve()){if(!De(t))return[];let e=[],n=_e(t);for(let r of n){let s=Wr(t,r.path)?.key,i=Ee(r.path,"skills");if(De(i))for(let a of Rn(i)){let c=Ee(i,a,"SKILL.md");if(!De(c))continue;let l={path:c,type:"skill",source:"plugin"};s&&(l.plugin_key=s),e.push(l)}for(let a of Ka){let c=Ee(r.path,`${a}s`);if(De(c))for(let l of Rn(c)){if(!l.endsWith(".md"))continue;let d={path:Ee(c,l),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function Ga(t=Ee(Z(),"settings.json")){if(!De(t))return[];try{let e=th(t,"utf8"),r=JSON.parse(e).hooks;if(!r||typeof r!="object")return[];let o=[];for(let[s,i]of Object.entries(r))if(Array.isArray(i))for(let a=0;a<i.length;a++)o.push({event:s,index:a,raw:i[a]});return o}catch{return[]}}function Rn(t){try{return eh(t).filter(e=>!e.startsWith("."))}catch{return[]}}var Ya=B.object({path:B.string(),type:B.enum(["skill","command","agent","hook"]),source:B.enum(["user","plugin"]),plugin_key:B.string().optional(),verdict:B.enum(["correct","misfit","outlier"]),recommended_type:B.string(),rationale:B.string(),confidence:B.enum(["high","med","low"])}),Va=B.record(B.string(),B.record(B.string(),B.number())),kT=B.object({inventory:B.object({user:Va,plugin:Va}),misfits:B.array(Ya),briefs_written:B.number(),total_artifacts:B.number()}),nh=B.object({writeBriefs:B.boolean().optional(),scope:B.enum(["user","plugin","all"]).optional()}),rh=["skill","command","agent"],Xa=["skill","command","agent","hook"];function oh(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function sh(t){let e=()=>{let s={};for(let i of Xa)s[i]={correct:0,misfit:0,outlier:0};return s},n={user:e(),plugin:e()};for(let s of t)n[s.source][s.type][s.verdict]+=1;let r={high:0,med:1,low:2},o=t.filter(s=>s.verdict==="misfit").slice().sort((s,i)=>r[s.confidence]-r[i.confidence]);return{inventory:n,misfits:o}}function ih(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function ah(t){let e=t.filter(o=>o.source==="user"),n=t.filter(o=>o.source==="plugin"),r=["","## Discovered artifacts (audit only these)",""];if(r.push('### User-scope artifacts (set `"source": "user"`, omit `plugin_key`)'),e.length===0)r.push("(none discovered)");else for(let o of e)r.push(`- ${o.path}`);if(r.push(""),r.push('### Plugin-scope artifacts (set `"source": "plugin"`, copy `plugin_key` from each entry)'),n.length===0)r.push("(none discovered)");else for(let o of n){let s=o.plugin_key??"<unknown>";r.push(`- ${o.path} (plugin_key: ${s})`)}return r.join(`
1080
1080
  `)}function ch(t,e){let n=["","## Discovered hooks (audit only these)",""];if(n.push(`Settings file (use this absolute path verbatim in each verdict's \`path\` field): \`${t}\``),n.push(""),e.length===0)return n.push("(no hooks discovered)"),n.join(`
1081
1081
  `);for(let r of e){let o=`${r.event}-${r.index}`;n.push(`### Hook \`${o}\``),n.push(""),n.push("```json"),n.push(JSON.stringify(r.raw,null,2)),n.push("```"),n.push("")}return n.join(`
1082
1082
  `)}function lh(t,e){if(!e)return{kind:"failure",message:`${t}: no result`};if(e.schemaError)return{kind:"failure",message:`${t}: schema mismatch \u2014 ${e.schemaError.message}`};if(e.status!=="succeeded"){let n=e.error?` \u2014 ${e.error.message}`:"";return{kind:"failure",message:`${t}: ${e.status}${n}`}}return e.output?{kind:"success",output:e.output}:{kind:"failure",message:`${t}: no output`}}async function dh(t,e,n){let r=n?.apiKey,o=n?.callId,s=typeof t=="object"&&t!==null?t:{},i=nh.parse(s),a=i.writeBriefs??!0,c=i.scope??"all",l=oh(c);if(!e?.sessionId)throw new Error("audit-fit requires a parent session with sessionId");let d=e.sessionId,u=j("audit-fit"),p={skill:u["01-skill-inspector.md"],command:u["02-command-inspector.md"],agent:u["03-agent-inspector.md"],hook:u["04-hook-inspector.md"]};for(let I of Xa)if(!p[I])throw new Error(`audit-fit skill missing inspector prompt for ${I}`);let f=l.runUserDiscovery?Wa():[],g=l.runPluginDiscovery?qa():[],m={skill:[],command:[],agent:[]};for(let I of[...f,...g])m[I.type].push(I);let y=new D({apiKey:r}),S=()=>async I=>ae.allowedTools.includes(I)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${I} not allowed for audit-fit inspectors. Allowed tools: ${ae.allowedTools.join(", ")}`},b=[];for(let I of rh){let C=m[I];if(C.length===0)continue;let F=p[I];F&&b.push({type:I,prompt:`${F}
@@ -1108,7 +1108,7 @@ ${C.rationale}
1108
1108
  ---
1109
1109
  Generated by audit-fit on ${new Date().toISOString().split(".")[0]}Z
1110
1110
  `;await Ja(T,L),E++}}let A=Je();await za(A,{recursive:!0});let x=I=>{let C=0;for(let F of Object.values(I))for(let T of Object.values(F))C+=T;return C},R=I=>{let C=v.user[I]??{},F=v.plugin[I]??{},T=L=>Object.values(L).reduce((W,z)=>W+z,0);return T(C)+T(F)},O={timestamp:new Date().toISOString(),surface:"afk",scope:c,total_artifacts:h.length,misfits_count:k.length,briefs_written:E,by_source:{user:x(v.user),plugin:x(v.plugin)},by_type:{skill:R("skill"),command:R("command"),agent:R("agent"),hook:R("hook")}},_=qr(A,"audit-fit-telemetry.jsonl");return await Ja(_,JSON.stringify(O)+`
1111
- `),{inventory:v,misfits:k,briefs_written:E,total_artifacts:h.length}}var uh={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:dh,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};ne(uh);import{z as P}from"zod";import{execFile as gh}from"node:child_process";import{promisify as hh}from"node:util";import{tmpdir as yh}from"node:os";import{join as tc}from"node:path";function Qa(t){return t.confidence<.5?{verify:!0,reason:`low confidence (${t.confidence.toFixed(2)} < ${.5})`}:t.boundary_flag&&t.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${t.boundary_flag}`}:t.coverage_gaps&&t.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${t.coverage_gaps.length===1?"":"s"}: ${t.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${t.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as ph}from"node:url";import{dirname as fh}from"node:path";var mh=ph(import.meta.url),TT=fh(mh),Za={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"agent-framework-private/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function ec(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}function bh(t){let e=t.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(e)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(e)?"REFUTED":"INCONCLUSIVE"}var wh=P.object({verifications:P.array(P.object({claim:P.string().optional(),verdict:P.string(),evidence:P.string().optional()}))});function Sh(t){let e=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<t.length;i++){let a=t[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(e.push(t.slice(r,i+1)),r=-1))}return e}function kh(t){let e=Sh(t);for(let n of e){let r;try{r=JSON.parse(n)}catch{continue}let o=wh.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:bh(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${e.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${t.slice(0,300)}`)}var In=hh(gh),oc=P.object({id:P.string(),claim:P.string(),confidence:P.number().min(0).max(1),evidence_sources:P.array(P.string()),location:P.string().optional(),proposed_fix:P.string().optional(),coverage_gaps:P.array(P.string()).nullish().transform(t=>t??void 0),boundary_flag:P.string().nullish().transform(t=>t??void 0)}),vh=P.object({hypothesis_id:P.string(),claim:P.string(),verdict:P.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:P.string(),gate_reason:P.string()}),sc=P.object({hypothesis_id:P.string(),reproducer_passed:P.boolean(),regressions:P.array(P.string()),confidence:P.number().min(0).max(1),verification_log:P.string()}),_h=P.enum(["crash","regression","logic-error","flaky","environment","unknown"]),Eh=P.object({failure_type:_h,error_signature:P.string(),affected_area:P.string()}),xh=P.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),WT=P.object({reproducer:P.string().optional(),triage:Eh.optional(),hypotheses:P.array(oc),premise_verifications:P.array(vh).optional(),winner:P.object({hypothesis_id:P.string(),verification_log:P.string(),proposed_fix:P.string()}).optional(),verification_results:P.array(sc).optional(),outcome:xh.optional(),recommended_next_skill:P.enum(["spec"]).optional()});async function Ah(t,e){let n=t.map(c=>({hypothesis:c,decision:Qa(c)})).filter(c=>c.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{let c=await e(n.map(l=>l.hypothesis.claim));r=Array.isArray(c)?c:[]}catch(c){o=c instanceof Error?c.message:String(c)}let s=n.map((c,l)=>{let d=r[l];return o!==void 0?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:c.decision.reason}:d?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:d.verdict,evidence:d.evidence,gate_reason:c.decision.reason}:{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:c.decision.reason}}),i=new Set(s.filter(c=>c.verdict==="REFUTED").map(c=>c.hypothesis_id)),a=i.size===0?t:t.filter(c=>!i.has(c.id));return{premise_verifications:s,hypotheses_to_test:a}}async function Th(t,e,n){let r=n?.apiKey,o=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let q=t;if(typeof q.failure=="string")return{failure:q.failure,repoPath:q.repoPath||process.cwd(),context:q.context||"",maxHypotheses:Math.min(q.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=e.sessionId,i=j("diagnose"),a=i["system.md"],c=i["research.md"],l=i["hypothesis.md"],d=i["verify.md"];if(!a||!c||!l||!d)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let u=new D({apiKey:r}),p=Ch(o.context),f=Rh(o.failure,o.context),g=`Triage:
1111
+ `),{inventory:v,misfits:k,briefs_written:E,total_artifacts:h.length}}var uh={name:"audit-fit",description:"Audit ~/.afk artifacts (skills, commands, agents, hooks) for correct type categorization. Walks user-scope dirs (~/.afk/{skills,commands,agents}/) and every plugin installed under ~/.afk/plugins/ (flat and marketplace-cache layouts), plus ~/.afk/settings.json for hooks. Dispatches per-type inspectors in parallel, applies decision heuristics (progressive-disclosure value, isolation need, deterministic vs. reasoning), flags misfits. Generates migration briefs only for user-scope misfits (plugin misfits are inventory-only \u2014 refactoring vendored plugin code is the maintainer's job). Optional `scope` input filters to `user`, `plugin`, or `all` (default). Use for inventory audits after bulk authoring, imports, or periodic hygiene.",handler:dh,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"],audience:"internal"};ne(uh);import{z as P}from"zod";import{execFile as gh}from"node:child_process";import{promisify as hh}from"node:util";import{tmpdir as yh}from"node:os";import{join as tc}from"node:path";function Qa(t){return t.confidence<.5?{verify:!0,reason:`low confidence (${t.confidence.toFixed(2)} < ${.5})`}:t.boundary_flag&&t.boundary_flag.length>0?{verify:!0,reason:`boundary flag set: ${t.boundary_flag}`}:t.coverage_gaps&&t.coverage_gaps.length>0?{verify:!0,reason:`coverage gap${t.coverage_gaps.length===1?"":"s"}: ${t.coverage_gaps.length} unresolved`}:{verify:!1,reason:`confidence ${t.confidence.toFixed(2)} with no gaps or boundary`}}import{fileURLToPath as ph}from"node:url";import{dirname as fh}from"node:path";var mh=ph(import.meta.url),TT=fh(mh),Za={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"private-framework/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function ec(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}function bh(t){let e=t.trim().toUpperCase();return["VERIFIED","CONFIRMED","CONFIRM","SUPPORTED","TRUE","PASS","PASSED"].includes(e)?"VERIFIED":["REFUTED","REFUTE","DISAGREE","DISAGREED","CONTRADICTED","FALSE","FAIL","FAILED"].includes(e)?"REFUTED":"INCONCLUSIVE"}var wh=P.object({verifications:P.array(P.object({claim:P.string().optional(),verdict:P.string(),evidence:P.string().optional()}))});function Sh(t){let e=[],n=0,r=-1,o=!1,s=!1;for(let i=0;i<t.length;i++){let a=t[i];if(o){s?s=!1:a==="\\"?s=!0:a==='"'&&(o=!1);continue}a==='"'?o=!0:a==="{"?(n===0&&(r=i),n++):a==="}"&&n>0&&(n--,n===0&&r!==-1&&(e.push(t.slice(r,i+1)),r=-1))}return e}function kh(t){let e=Sh(t);for(let n of e){let r;try{r=JSON.parse(n)}catch{continue}let o=wh.safeParse(r);if(o.success)return o.data.verifications.map(s=>({claim:s.claim??"",verdict:bh(s.verdict),evidence:s.evidence??""}))}throw new Error(`shadow-verify did not return a parseable {"verifications":[...]} envelope (${e.length} JSON-like span(s) found, none matched the schema); raw output (first 300 chars): ${t.slice(0,300)}`)}var In=hh(gh),oc=P.object({id:P.string(),claim:P.string(),confidence:P.number().min(0).max(1),evidence_sources:P.array(P.string()),location:P.string().optional(),proposed_fix:P.string().optional(),coverage_gaps:P.array(P.string()).nullish().transform(t=>t??void 0),boundary_flag:P.string().nullish().transform(t=>t??void 0)}),vh=P.object({hypothesis_id:P.string(),claim:P.string(),verdict:P.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:P.string(),gate_reason:P.string()}),sc=P.object({hypothesis_id:P.string(),reproducer_passed:P.boolean(),regressions:P.array(P.string()),confidence:P.number().min(0).max(1),verification_log:P.string()}),_h=P.enum(["crash","regression","logic-error","flaky","environment","unknown"]),Eh=P.object({failure_type:_h,error_signature:P.string(),affected_area:P.string()}),xh=P.enum(["clear_winner","multiple_plausible","dissent","all_inconclusive","no_hypotheses"]),WT=P.object({reproducer:P.string().optional(),triage:Eh.optional(),hypotheses:P.array(oc),premise_verifications:P.array(vh).optional(),winner:P.object({hypothesis_id:P.string(),verification_log:P.string(),proposed_fix:P.string()}).optional(),verification_results:P.array(sc).optional(),outcome:xh.optional(),recommended_next_skill:P.enum(["spec"]).optional()});async function Ah(t,e){let n=t.map(c=>({hypothesis:c,decision:Qa(c)})).filter(c=>c.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{let c=await e(n.map(l=>l.hypothesis.claim));r=Array.isArray(c)?c:[]}catch(c){o=c instanceof Error?c.message:String(c)}let s=n.map((c,l)=>{let d=r[l];return o!==void 0?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:`shadow-verify dispatch failed: ${o}`,gate_reason:c.decision.reason}:d?{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:d.verdict,evidence:d.evidence,gate_reason:c.decision.reason}:{hypothesis_id:c.hypothesis.id,claim:c.hypothesis.claim,verdict:"INCONCLUSIVE",evidence:"no verifier result for this claim",gate_reason:c.decision.reason}}),i=new Set(s.filter(c=>c.verdict==="REFUTED").map(c=>c.hypothesis_id)),a=i.size===0?t:t.filter(c=>!i.has(c.id));return{premise_verifications:s,hypotheses_to_test:a}}async function Th(t,e,n){let r=n?.apiKey,o=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let q=t;if(typeof q.failure=="string")return{failure:q.failure,repoPath:q.repoPath||process.cwd(),context:q.context||"",maxHypotheses:Math.min(q.maxHypotheses||4,4)}}throw new Error("diagnose handler requires input.failure (string) or a string argument")})();if(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let s=e.sessionId,i=j("diagnose"),a=i["system.md"],c=i["research.md"],l=i["hypothesis.md"],d=i["verify.md"];if(!a||!c||!l||!d)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let u=new D({apiKey:r}),p=Ch(o.context),f=Rh(o.failure,o.context),g=`Triage:
1112
1112
  failure_type: ${f.failure_type}
1113
1113
  error_signature: ${f.error_signature}
1114
1114
  affected_area: ${f.affected_area}`,y=`${ae.systemPrompt}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-afk",
3
- "version": "3.80.6",
3
+ "version": "3.80.7",
4
4
  "description": "CLI tool for interacting with AI agents via multiple interfaces",
5
5
  "main": "dist/index.mjs",
6
6
  "type": "module",
@@ -1,403 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
3
- import { createHash } from 'node:crypto';
4
- import { join, dirname } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = dirname(__filename);
9
-
10
- // Pinned hashes for the 12 bundled skills shipped under awa-bundled/. These
11
- // files mirror — but are NOT byte-equal to — corresponding skills in the
12
- // upstream awa-private repo. Permanent intentional differences include:
13
- //
14
- // - Namespace prefixes (`/awa-dev:contract` upstream → `/contract` here)
15
- // - Sub-agent dispatch identifiers (`awa-private:research-agent` → `research-agent`)
16
- // - Occasional wording divergence between maintainers
17
- //
18
- // Because byte-equality is a false invariant, this file enforces only the
19
- // pinned-hash snapshot: any unauthored edit to a bundled SKILL.md fails the
20
- // test until the developer explicitly bumps the hash here. That bump is the
21
- // forcing function for cross-repo discipline:
22
- //
23
- // *** Workflow when bumping a pinned hash ***
24
- // 1. Identify what changed in the bundled SKILL.md.
25
- // 2. Check whether the same change applies upstream in
26
- // awa-private/plugins/{awa-dev,awa-private}/skills/<name>/SKILL.md.
27
- // 3. If yes → open a parallel PR in awa-private. Land both before either
28
- // is released.
29
- // 4. If no → document why the change is bundled-only in the PR description.
30
- // 5. Only then update the hash below.
31
- //
32
- // This convention exists because in November 2026 a critical /ship guardrail
33
- // (the "Branch lock" + "Never push to main" Hard Rules in commit 63f3ed3)
34
- // was added to the bundled mirror but never back-ported to awa-private. The
35
- // deployed plugin therefore lacked the guardrail until the next sync. This
36
- // test cannot prevent that on its own — but the hash-bump moment forces the
37
- // developer to look at both copies.
38
- const PINNED_HASHES = {
39
- contract: '2c8a3779f225902f2a8b0af74bfc66c1cdbae58f863d1205c66ce44a14e275b5',
40
- 'devils-advocate':
41
- '84275b097fa3ed270b0b71c87e2dad0366794fd7efc7a47d29abaa85da97f974',
42
- gather: 'ec2964fb1f47970fffba6bafacb4dc4f0c76291a7cc0da92ff069a0a986decb4',
43
- 'ground-claim':
44
- '64a4fa0b63467a9a7ae6e61afd68813ff59bfb46a8c5e072feafa15473e36f2a',
45
- 'ground-state':
46
- 'ae4c167296e96b640a54cd4cd317e5810894cffff6dac3c022b1433dff003105',
47
- // intent-lock is bundled-only (not present in upstream awa-private or
48
- // awa-dev). Hash bumps need no parallel PR — document the change in the
49
- // commit message instead.
50
- 'intent-lock':
51
- '7a466075e5a64c1145b97aa24b9a6990a3ee1dc818b93c158433e53d7416aef0',
52
- parallelize:
53
- '74b1a7cf866d630dce0d33323663a8b818f149b5f4d4ef60feba1aeb3472e49b',
54
- // refactor is bundled-only (no upstream awa-private counterpart); verbatim
55
- // copy of the user-scope /refactor at ~/.afk/skills/.
56
- refactor: '23ab4836653159deeafbca45e516af8d43e8c5275535613e36f7bcb2d77de64e',
57
- research: '0d04d0a05891ed1b63679e5a0237b743364a6165731a8f694c5584ed7661505f',
58
- review: '816ea27cf665be23c67cf887d639d40e1435954f80ceeb43740bcd7f39c205e7',
59
- 'shadow-verify':
60
- '8bce741e55be049a196ed6c71efd0acd271f272a8e2202917c3f1243b875eb33',
61
- ship: '4b9a0e40372c36f953ad6d37347e1682950c9825ca5e312fae4e9b320cde975f',
62
- // simplify is bundled-only (no upstream awa-private counterpart).
63
- simplify:
64
- 'b863890eead7011c90d4f93b65e5a1533c8f88292728ec771f8b128e9535d996',
65
- spec: 'c08f3b4fbe1f585b1e8354a000e0d2d3a48455ad322c7a27112d509aa9698fe7',
66
- } as const;
67
-
68
- type SkillName = keyof typeof PINNED_HASHES;
69
-
70
- const SKILLS = Object.keys(PINNED_HASHES) as SkillName[];
71
-
72
- // ── Namespace-normalized drift detection ──────────────────────────────────────
73
- //
74
- // Workspace root is four levels above __dirname (src/bundled-plugins/awa-bundled).
75
- // awa-private is a sibling of agent-afk at the workspace root level.
76
- // This mirrors the pattern used in src/skills/_agents/vendored.test.ts.
77
- const WORKSPACE_ROOT = join(__dirname, '../../../..');
78
-
79
- // Upstream source paths relative to WORKSPACE_ROOT.
80
- // intent-lock is bundled-only — no upstream comparison row.
81
- const UPSTREAM_PATHS: Partial<Record<SkillName, string>> = {
82
- contract: 'awa-private/plugins/awa-dev/skills/contract/SKILL.md',
83
- gather: 'awa-private/plugins/awa-dev/skills/gather/SKILL.md',
84
- 'ground-claim': 'awa-private/plugins/awa-dev/skills/ground-claim/SKILL.md',
85
- 'ground-state': 'awa-private/plugins/awa-dev/skills/ground-state/SKILL.md',
86
- research: 'awa-private/plugins/awa-dev/skills/research/SKILL.md',
87
- ship: 'awa-private/plugins/awa-dev/skills/ship/SKILL.md',
88
- spec: 'awa-private/plugins/awa-dev/skills/spec/SKILL.md',
89
- 'devils-advocate':
90
- 'awa-private/plugins/awa-private/skills/devils-advocate/SKILL.md',
91
- parallelize: 'awa-private/plugins/awa-private/skills/parallelize/SKILL.md',
92
- review: 'awa-private/plugins/awa-private/skills/review/SKILL.md',
93
- 'shadow-verify':
94
- 'awa-private/plugins/awa-private/skills/shadow-verify/SKILL.md',
95
- };
96
-
97
- // Normalize both copies before comparing, removing all permanent intentional
98
- // namespace shifts:
99
- //
100
- // /awa-dev:contract → /contract
101
- // /awa-private:ship → /ship
102
- // `awa-dev:ground-state` → `ground-state`
103
- // "awa-private:research-agent" → "research-agent"
104
- //
105
- // After normalization, any remaining diff is either real drift (a change
106
- // landed in one mirror but not the other) or an explicitly allowlisted
107
- // intentional divergence documented in INTENTIONAL_DIFFS below.
108
- function normalize(content: string): string {
109
- return content
110
- .replace(/\/awa-dev:/g, '/')
111
- .replace(/\/awa-private:/g, '/')
112
- .replace(/`awa-dev:/g, '`')
113
- .replace(/`awa-private:/g, '`')
114
- .replace(/"awa-dev:/g, '"')
115
- .replace(/"awa-private:/g, '"');
116
- }
117
-
118
- // INTENTIONAL_DIFFS: per-skill array of RegExp patterns. A normalized diff
119
- // line matching any pattern for that skill is silently accepted — the line is
120
- // removed from BOTH sides before comparison (each pattern is applied to both
121
- // the bundled and upstream line arrays independently).
122
- //
123
- // *** Adding an entry here requires an inline comment justifying why the
124
- // divergence is intentional. "It seems fine" is NOT sufficient — if you
125
- // cannot defensibly justify it, surface it as unclassified drift in the PR
126
- // body instead. ***
127
- const INTENTIONAL_DIFFS: Partial<Record<SkillName, RegExp[]>> = {
128
- // devils-advocate, parallelize, shadow-verify:
129
- // Both sides contain a "Sub-agent contract" invocation line immediately
130
- // after the frontmatter block, but they use different plugin namespaces:
131
- //
132
- // Bundled: /contract (resolves to the co-bundled contract skill)
133
- // Upstream: /agent-workflow-amplifiers:contract (third-party plugin ns)
134
- //
135
- // The `normalize()` function only strips `awa-dev:` and `awa-private:`
136
- // prefixes; it intentionally does NOT touch `agent-workflow-amplifiers:`
137
- // because that is a distinct third-party plugin, not a namespace shift of
138
- // the same plugin. Both copies invoke the same logical skill — the
139
- // difference is which plugin registry entry resolves the name. This is
140
- // intentional structural divergence: bundled uses self-contained routing;
141
- // upstream relies on the agent-workflow-amplifiers plugin being installed.
142
- //
143
- // Pattern rationale: we match both the bare `/contract` line (bundled side)
144
- // and the namespaced `/agent-workflow-amplifiers:contract` line (upstream
145
- // side) so both are removed before the equality check.
146
- 'devils-advocate': [
147
- // Bundled side: bare /contract invocation (no plugin prefix).
148
- /^\/contract$/,
149
- // Upstream side: /agent-workflow-amplifiers:contract invocation.
150
- /\/agent-workflow-amplifiers:contract/,
151
- ],
152
- parallelize: [
153
- // Same structural divergence as devils-advocate — different contract
154
- // skill namespace on bundled vs upstream.
155
- /^\/contract$/,
156
- /\/agent-workflow-amplifiers:contract/,
157
- ],
158
- 'shadow-verify': [
159
- // Same structural divergence as devils-advocate.
160
- /^\/contract$/,
161
- /\/agent-workflow-amplifiers:contract/,
162
- ],
163
-
164
- // research — 1-line divergence, #441 back-port gap:
165
- // "if the research-agent is not available" (bundled) vs
166
- // "if the private plugin is not installed" (upstream).
167
- // Bundled users have no concept of "private plugin" — the research-agent
168
- // IS bundled, so "not available" is the correct user-facing phrase. The
169
- // upstream wording assumed plugin-based deployment context. This divergence
170
- // is intentional for bundled context; upstream should ideally adopt a
171
- // context-neutral phrasing. Flagged for #441 reconciliation.
172
- research: [
173
- /if the research-agent is not available/,
174
- /if the private plugin is not installed/,
175
- ],
176
-
177
- // ship — 3 divergences, all #441 back-port gaps:
178
- //
179
- // 1. Phase 3 heading:
180
- // Bundled: "Phase 3 — Draft commit message."
181
- // Upstream: "Phase 3 — Draft commit message (user-approval gate)."
182
- // The "(user-approval gate)" annotation was added in upstream but not
183
- // back-ported to bundled. Both copies have the same behavior (no
184
- // approval gate); the annotation is a clarifying label. Real drift,
185
- // flagged for #441 back-port.
186
- //
187
- // 2. Phase 3 body prose:
188
- // Bundled: "Print the draft message + file list to the user as
189
- // info-only output, then **immediately** invoke Phase 4.
190
- // **This is not a gate. Do not ask "does this look good?" Do not
191
- // wait for approval.** The user surface is one continuous turn:
192
- // draft → commit → push → PR URL."
193
- // Upstream: "Surface the draft message + file list to the user for
194
- // visibility, then proceed immediately to commit. Do not wait for
195
- // approval."
196
- // Upstream simplified the prose; semantics are identical. Real drift
197
- // (editorial improvement in upstream not back-ported). Flagged for #441.
198
- //
199
- // 3. Phase 5 Never-push-main bullet order:
200
- // Bundled: bullet appears after "Non-fast-forward rejection" bullet.
201
- // Upstream: bullet appears before "Upstream unset" bullet (earlier).
202
- // Same safety rule, different list position. Real drift (harmless
203
- // reordering). Flagged for #441 back-port.
204
- ship: [
205
- // Heading divergence (1 above).
206
- /Phase 3 — Draft commit message\./,
207
- /Phase 3 — Draft commit message \(user-approval gate\)\./,
208
- // Prose divergence (2 above) — match the diverging body paragraph.
209
- /Print the draft message \+ file list to the user as info-only output/,
210
- /then \*\*immediately\*\* invoke Phase 4\./,
211
- /\*\*This is not a gate\. Do not ask "does this look good\?" Do not wait for approval\.\*\*/,
212
- /The user surface is one continuous turn: draft → commit → push → PR URL\./,
213
- /Surface the draft message \+ file list to the user for visibility/,
214
- /then proceed immediately to commit\. Do not wait for approval\./,
215
- // Bullet ordering divergence (3 above).
216
- /\*\*Never\*\* `git push origin main` \(or `master`\)\. Pushing the feature branch is the only allowed form\./,
217
- ],
218
-
219
- // review — namespace-only divergence (back-port landed; #441 closed):
220
- // The bundled review is now the de-namespaced mirror of upstream
221
- // awa-private review. The previously-allowlisted #441 drift —
222
- // Wave 1.5 (citation + absence-claim verification), reviewed-ref
223
- // capture / SHA pinning, the citation-requirement block, the severity
224
- // sort-order block, epistemic scope disclosure, and the ref:<sha>
225
- // finding-schema fields — has been back-ported into bundled; and the
226
- // api-compat reachability + absence-claim grounding gates were ported
227
- // the other way into upstream (griffinwork40/awa-private#40). Both
228
- // copies now carry the full superset, so the only remaining divergence
229
- // is the same contract-namespace shift as devils-advocate / parallelize
230
- // / shadow-verify: bundled uses /contract (self-contained routing),
231
- // upstream uses /agent-workflow-amplifiers:contract (third-party ns).
232
- review: [
233
- // Bundled side: bare /contract invocation (no plugin prefix).
234
- /^\/contract$/,
235
- // Upstream side: /agent-workflow-amplifiers:contract invocation.
236
- /\/agent-workflow-amplifiers:contract/,
237
- ],
238
- };
239
-
240
- // ── Helpers ───────────────────────────────────────────────────────────────────
241
-
242
- function computeHash(content: string): string {
243
- return createHash('sha256').update(content).digest('hex');
244
- }
245
-
246
- function bundledPath(name: SkillName): string {
247
- return join(__dirname, 'skills', name, 'SKILL.md');
248
- }
249
-
250
- function readBundled(name: SkillName): string {
251
- return readFileSync(bundledPath(name), 'utf8');
252
- }
253
-
254
- function upstreamAbsPath(name: SkillName): string | null {
255
- const rel = UPSTREAM_PATHS[name];
256
- if (!rel) return null;
257
- return join(WORKSPACE_ROOT, rel);
258
- }
259
-
260
- function upstreamAvailable(name: SkillName): boolean {
261
- const abs = upstreamAbsPath(name);
262
- return abs !== null && existsSync(abs);
263
- }
264
-
265
- // isAllowlisted returns true if the given line matches any pattern in the
266
- // skill's INTENTIONAL_DIFFS entry.
267
- function isAllowlisted(line: string, name: SkillName): boolean {
268
- const patterns = INTENTIONAL_DIFFS[name] ?? [];
269
- return patterns.some((re) => re.test(line));
270
- }
271
-
272
- // diffLines computes the symmetric difference between two ordered line arrays:
273
- // lines that are in `aLines` but not `bLines` (bundled-only), and lines that
274
- // are in `bLines` but not `aLines` (upstream-only). Returns the two sets.
275
- // This is intentionally set-based (not position-sensitive) to avoid false
276
- // positives from harmless reorderings of identical content.
277
- function diffLines(
278
- aLines: string[],
279
- bLines: string[],
280
- ): { bundledOnly: string[]; upstreamOnly: string[] } {
281
- const aCount = new Map<string, number>();
282
- const bCount = new Map<string, number>();
283
- for (const l of aLines) aCount.set(l, (aCount.get(l) ?? 0) + 1);
284
- for (const l of bLines) bCount.set(l, (bCount.get(l) ?? 0) + 1);
285
-
286
- const bundledOnly: string[] = [];
287
- const upstreamOnly: string[] = [];
288
-
289
- for (const [l, cnt] of aCount) {
290
- const excess = cnt - (bCount.get(l) ?? 0);
291
- for (let i = 0; i < excess; i++) bundledOnly.push(l);
292
- }
293
- for (const [l, cnt] of bCount) {
294
- const excess = cnt - (aCount.get(l) ?? 0);
295
- for (let i = 0; i < excess; i++) upstreamOnly.push(l);
296
- }
297
-
298
- return { bundledOnly, upstreamOnly };
299
- }
300
-
301
- // ── Test suites ───────────────────────────────────────────────────────────────
302
-
303
- describe('bundled skills', () => {
304
- describe('pinned-hash snapshot tests', () => {
305
- for (const name of SKILLS) {
306
- it(`${name} bundled copy matches pinned hash`, () => {
307
- const content = readBundled(name);
308
- const hash = computeHash(content);
309
- expect(hash).toBe(PINNED_HASHES[name]);
310
- });
311
- }
312
- });
313
-
314
- describe('skill inventory invariants', () => {
315
- it('covers every bundled skill directory', () => {
316
- // Sentinel: if a new skill is added to awa-bundled/skills/ but not
317
- // PINNED_HASHES, this test fails — forcing the author to register it.
318
- const skillsDir = join(__dirname, 'skills');
319
- const entries = readdirSync(skillsDir)
320
- .filter((name) => statSync(join(skillsDir, name)).isDirectory())
321
- .sort();
322
- const registered = [...SKILLS].sort();
323
- expect(entries).toEqual(registered);
324
- });
325
- });
326
-
327
- // ── Namespace-normalized drift comparison ──────────────────────────────────
328
- //
329
- // Each test below compares a bundled SKILL.md against its upstream
330
- // counterpart after normalization (namespace prefixes stripped) and
331
- // allowlisting (known intentional divergences removed).
332
- //
333
- // The test is skipped — NOT failed — when awa-private is not co-located
334
- // (e.g. standalone CI clone). The pinned-hash tests above still guard
335
- // against local-only edits. These tests guard against the cross-repo case:
336
- // a change landing in one mirror without being back-ported to the other.
337
- //
338
- // Workflow when a test fails here:
339
- // 1. Is the diff intentional? Add a justified entry to INTENTIONAL_DIFFS.
340
- // 2. Is it real drift? Land the back-port and re-run. Then bump the hash.
341
- // 3. Is it unclassifiable? Surface it as unclassified drift in the PR body.
342
- describe('namespace-normalized drift comparison (skipped if awa-private not co-located)', () => {
343
- // Invariant: for every mirrorable skill, after normalize() and after
344
- // removing allowlisted lines, the bundled and upstream copies must be
345
- // line-for-line identical. Any remaining difference is a back-port gap.
346
-
347
- const mirrorableSkills = SKILLS.filter(
348
- (s) => s !== 'intent-lock' && s !== 'simplify' && s !== 'refactor',
349
- );
350
-
351
- for (const name of mirrorableSkills) {
352
- it.skipIf(!upstreamAvailable(name))(
353
- `${name}: normalized bundled matches normalized upstream (after allowlist)`,
354
- () => {
355
- // Contract: upstreamAbsPath is non-null when upstreamAvailable() is true.
356
- const abs = upstreamAbsPath(name) as string;
357
- const bundledRaw = readBundled(name);
358
- const upstreamRaw = readFileSync(abs, 'utf8');
359
-
360
- const bundledLines = normalize(bundledRaw).split('\n');
361
- const upstreamLines = normalize(upstreamRaw).split('\n');
362
-
363
- // Compute symmetric difference: lines unique to each side.
364
- // Context lines (identical on both sides) are ignored — we only care
365
- // about lines that changed.
366
- const { bundledOnly, upstreamOnly } = diffLines(
367
- bundledLines,
368
- upstreamLines,
369
- );
370
-
371
- // Remove allowlisted divergences from each side.
372
- const unexpectedBundledOnly = bundledOnly.filter(
373
- (l) => !isAllowlisted(l, name),
374
- );
375
- const unexpectedUpstreamOnly = upstreamOnly.filter(
376
- (l) => !isAllowlisted(l, name),
377
- );
378
-
379
- if (
380
- unexpectedBundledOnly.length > 0 ||
381
- unexpectedUpstreamOnly.length > 0
382
- ) {
383
- const lines: string[] = [
384
- `--- bundled (normalized, non-allowlisted unique lines)`,
385
- `+++ upstream (normalized, non-allowlisted unique lines)`,
386
- ];
387
- for (const l of unexpectedBundledOnly) lines.push(`-${l}`);
388
- for (const l of unexpectedUpstreamOnly) lines.push(`+${l}`);
389
-
390
- throw new Error(
391
- `Namespace-normalized drift detected in ${name}.\n` +
392
- ` Bundled: ${bundledPath(name)}\n` +
393
- ` Upstream: ${abs}\n` +
394
- ` If the diff is intentional, add a justified entry to INTENTIONAL_DIFFS['${name}'].\n` +
395
- ` If it is real drift, back-port the change and bump the pinned hash.\n\n` +
396
- lines.join('\n'),
397
- );
398
- }
399
- },
400
- );
401
- }
402
- });
403
- });