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
|
|
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:"
|
|
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:"
|
|
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.
|
|
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/
|
|
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:"
|
|
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:"
|
|
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:"
|
|
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:"
|
|
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:"
|
|
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:"
|
|
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,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
|
-
});
|