agent-afk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +675 -0
- package/dist/cli.mjs +1607 -0
- package/dist/index.mjs +1252 -0
- package/dist/telegram.mjs +1270 -0
- package/package.json +77 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1252 @@
|
|
|
1
|
+
function Ir(){return process.env.AFK_DEBUG==="1"||process.env.DEBUG==="1"}function A(...t){Ir()&&console.log(...t)}var B=class extends Error{constructor(e){super(e),this.name="AbortError"}},Me=class extends Error{constructor(n,r){super(n);this.timeoutMs=r;this.name="TimeoutError"}timeoutMs},U=class extends Error{constructor(n,r,o,s){super(n);this.event=r;this.reason=o;this.name="HookBlockedError",s?.cause!==void 0&&(this.cause=s.cause)}event;reason;cause};var ge=class extends Error{constructor(n,r,o){super(o??`${n} provider does not support AgentConfig.${r}.`);this.provider=n;this.field=r;this.name="UnsupportedProviderConfigError"}provider;field};var ae=class{queue=[];waiters=[];closed=!1;error=null;push(e){if(this.closed)throw new Error("Cannot push to closed queue");if(process.env.AFK_CODEX_DEBUG&&console.log("[queue] push:",e.type),A("\u{1F4E6} MessageQueue.push: event type=",e.type,"waiters=",this.waiters.length,"queue size=",this.queue.length),this.waiters.length>0){let n=this.waiters.shift();A("\u{1F4E6} MessageQueue.push: Resolving waiter immediately"),n?.({value:e,done:!1})}else this.queue.push(e),A("\u{1F4E6} MessageQueue.push: Added to queue, new size=",this.queue.length)}complete(){for(this.closed=!0;this.waiters.length>0;)this.waiters.shift()?.({value:void 0,done:!0})}fail(e){this.error=e,this.closed=!0;let n={type:"error",error:e};if(this.waiters.length>0)for(this.waiters.shift()?.({value:n,done:!1});this.waiters.length>0;)this.waiters.shift()?.({value:void 0,done:!0});else this.queue.push(n)}isClosed(){return this.closed}hasError(){return this.error}size(){return this.queue.length}async*[Symbol.asyncIterator](){for(A("\u{1F4E6} MessageQueue: Iterator started");;){if(this.queue.length>0){let n=this.queue.shift();if(A("\u{1F4E6} MessageQueue: Yielding queued event, type=",n?.type),n&&(yield n,n.type==="error")){A("\u{1F4E6} MessageQueue: Stopping after error");return}continue}if(this.closed){A("\u{1F4E6} MessageQueue: Closed and empty, stopping");return}A("\u{1F4E6} MessageQueue: Waiting for next event...");let e=await new Promise(n=>{this.waiters.push(n)});if(A("\u{1F4E6} MessageQueue: Got result, done=",e.done,"type=",e.value?.type),e.done)return;if(yield e.value,e.value.type==="error"){A("\u{1F4E6} MessageQueue: Stopping after error");return}}}};import xi from"@anthropic-ai/sdk";var Mr="claude-code-20250219,oauth-2025-04-20",Rr="claude-cli/1.0.0 (external, cli)",_r="x-anthropic-billing-header: cc_version=1.0.0.test; cc_entrypoint=cli; cch=00000;";function At(t){return t.startsWith("sk-ant-oat01-")?"oauth":"api-key"}function Tt(t,e){return e==="oauth"?{authToken:t}:{apiKey:t}}function et(t,e,n){return t!=="oauth"?{}:{"anthropic-beta":Mr,"x-app":"cli","User-Agent":Rr,"X-Claude-Code-Session-Id":e,"x-client-request-id":n}}function Ct(t){return t!=="oauth"?null:[{type:"text",text:_r}]}import{randomUUID as ft}from"node:crypto";function Re(){let t=process.env.AFK_DISABLE_PROMPT_CACHE;if(t===void 0||t.length===0)return!0;let e=t.toLowerCase();return!(e==="1"||e==="true"||e==="yes"||e==="on")}function _e(){let t=process.env.AFK_PROMPT_CACHE_TTL;return t==="5m"?"5m":"1h"}function It(t,e){if(t.length===0)return t;let n=t[t.length-1],r=Rt(n,e);return r===n?t:[...t.slice(0,-1),r]}function Mt(t,e){if(t.length===0)return t;let n=t[t.length-1],r=Dr(n,e);return r===n?t:[...t.slice(0,-1),r]}function Dr(t,e){let n=t.content;if(typeof n=="string")return n.length===0?t:{...t,content:[{type:"text",text:n,cache_control:{type:"ephemeral",ttl:e}}]};if(!Array.isArray(n)||n.length===0)return t;let r=n[n.length-1],o=Rt(r,e);return o===r?t:{...t,content:[...n.slice(0,-1),o]}}function Rt(t,e){return t.type==="thinking"||t.type==="redacted_thinking"?t:{...t,cache_control:{type:"ephemeral",ttl:e}}}import{randomUUID as Fr}from"node:crypto";function _t(t,e){if(!t)return{stopReason:e??null};let n={inputTokens:t.input_tokens,outputTokens:t.output_tokens,stopReason:e??null};return t.cache_read_input_tokens!=null&&(n.cachedInputTokens=t.cache_read_input_tokens),t.cache_creation_input_tokens!=null&&(n.cacheCreationTokens=t.cache_creation_input_tokens),n.totalTokens=(t.input_tokens??0)+(t.output_tokens??0),n}function Dt(t,e){let n=(c,d)=>{if(!(c==null&&d==null))return(c??0)+(d??0)},r={stopReason:e.stopReason??t.stopReason??null},o=n(t.inputTokens,e.inputTokens);o!==void 0&&(r.inputTokens=o);let s=n(t.outputTokens,e.outputTokens);s!==void 0&&(r.outputTokens=s);let i=n(t.cachedInputTokens,e.cachedInputTokens);i!==void 0&&(r.cachedInputTokens=i);let a=n(t.cacheCreationTokens,e.cacheCreationTokens);a!==void 0&&(r.cacheCreationTokens=a);let l=n(t.totalTokens,e.totalTokens);return l!==void 0&&(r.totalTokens=l),r}function Or(t){let e=t.trim();if(e.length===0)return{};try{return JSON.parse(e)}catch{return{}}}function $r(t,e,n){let r=[],o=[];for(let a of t)a&&(a.kind==="text"?(r.push({type:"text",text:a.text}),o.push(a.text)):a.kind==="thinking"?r.push({type:"thinking",thinking:a.thinking,signature:a.signature}):r.push({type:"tool_use",id:a.id,name:a.name,input:Or(a.partialJson)}));let s=a=>a.type==="tool_use",i=r.filter(s);return{stopReason:e,assistantBlocks:r,toolUseBlocks:i,usage:n,text:o.join("")}}async function*Ot(t,e){let n=[],r=null,o=null,s=!1;try{for await(let i of t){switch(i.type){case"message_start":{let a=i.message?.usage;a&&(o={...a});break}case"content_block_start":{let a=i.content_block;a.type==="text"?n[i.index]={kind:"text",text:""}:a.type==="thinking"?n[i.index]={kind:"thinking",thinking:"",signature:""}:a.type==="tool_use"&&(n[i.index]={kind:"tool_use",id:a.id,name:a.name,partialJson:""});break}case"content_block_delta":{let a=n[i.index],l=i.delta;l.type==="text_delta"?(a&&a.kind==="text"&&(a.text+=l.text),yield{kind:"event",event:{type:"delta.text",text:l.text,sessionId:e.sessionId}}):l.type==="input_json_delta"?a&&a.kind==="tool_use"&&(a.partialJson+=l.partial_json):l.type==="thinking_delta"?(a&&a.kind==="thinking"&&(a.thinking+=l.thinking),yield{kind:"event",event:{type:"delta.reasoning",text:l.thinking,sessionId:e.sessionId}}):l.type==="signature_delta"&&a&&a.kind==="thinking"&&(a.signature=l.signature);break}case"content_block_stop":{let a=n[i.index];a&&a.kind==="tool_use"&&(yield{kind:"event",event:{type:"tool.use",summary:a.name,toolUseIds:[a.id],sessionId:e.sessionId}});break}case"message_delta":{i.delta&&i.delta.stop_reason!==void 0&&(r=i.delta.stop_reason);let a=i.usage;a&&(o!==null?(o.output_tokens=a.output_tokens,a.cache_creation_input_tokens!=null&&(o.cache_creation_input_tokens=a.cache_creation_input_tokens),a.cache_read_input_tokens!=null&&(o.cache_read_input_tokens=a.cache_read_input_tokens),a.input_tokens!=null&&(o.input_tokens=a.input_tokens)):o={cache_creation:null,cache_creation_input_tokens:a.cache_creation_input_tokens??null,cache_read_input_tokens:a.cache_read_input_tokens??null,inference_geo:null,input_tokens:a.input_tokens??0,output_tokens:a.output_tokens,server_tool_use:null,service_tier:null});break}case"message_stop":{s=!0;break}default:break}if(s)break}}catch(i){yield{kind:"event",event:{type:"error",error:i instanceof Error?i:new Error(String(i))}};return}yield{kind:"turn-result",result:$r(n,r,o)}}var jr=0;function Hr(t){if(!t||typeof t!="object")return"";let e=t,n=e.file_path??e.path??e.filePath;if(typeof n=="string")return" "+n;let r=e.command??e.cmd;if(typeof r=="string"){let s=r.split(`
|
|
2
|
+
`)[0];return" "+(s.length>80?s.slice(0,77)+"\u2026":s)}let o=e.query??e.pattern??e.url??e.description;return typeof o=="string"?" "+o:""}async function*$t(t){let e=t.maxToolUseIterations??jr,n={stopReason:null},r=0,o=Fr(),s=Date.now();for(;;){if(t.signal.aborted)return;let i=Re()?Mt(t.messages,_e()):t.messages,a={model:t.model,max_tokens:t.maxTokens,messages:i,stream:!0,...t.system!==null?{system:t.system}:{},...t.tools!==null&&t.tools.length>0?{tools:t.tools}:{}},l;try{l=await Promise.resolve(t.client.messages.create(a,{headers:t.headers,signal:t.signal}))}catch(f){if(t.signal.aborted)return;yield{type:"error",error:f instanceof Error?f:new Error(String(f))};return}let c=null,d=!1;try{for await(let f of Ot(l,t.ctx))if(f.kind==="event"){if(f.event.type==="error"){yield f.event,d=!0;break}yield f.event}else{c=f.result;break}}catch(f){if(t.signal.aborted)return;yield{type:"error",error:f instanceof Error?f:new Error(String(f))};return}if(d)return;if(c===null){yield{type:"turn.completed",usage:n,sessionId:t.ctx.sessionId};return}if(n=Dt(n,_t(c.usage,c.stopReason)),c.stopReason!=="tool_use"){c.text.length>0&&(yield{type:"assistant.message",text:c.text,sessionId:t.ctx.sessionId},c.text.length<=200&&(yield{type:"suggestion",suggestion:c.text,sessionId:t.ctx.sessionId})),yield{type:"turn.completed",usage:n,sessionId:t.ctx.sessionId};return}t.messages.push({role:"assistant",content:c.assistantBlocks});let u=[];for(let f of c.toolUseBlocks)u.push({id:f.id,name:f.name,input:f.input,signal:t.signal}),yield{type:"tool.use.start",toolUseId:f.id,toolName:f.name,toolInput:Hr(f.input),sessionId:t.ctx.sessionId};if(t.signal.aborted)return;let p;if(t.toolDispatcher.executeBatch)p=await t.toolDispatcher.executeBatch(u);else{p=[];for(let f of u){if(t.signal.aborted){p.push({content:"Tool call aborted",isError:!0});continue}try{p.push(await t.toolDispatcher.execute(f))}catch(v){let x=v instanceof Error?v.message:String(v);p.push({content:`Tool execution threw: ${x}`,isError:!0})}}}let m=[];for(let f=0;f<u.length;f++){let v=u[f],x=p[f];yield{type:"tool.output",toolUseId:v.id,content:x.content,...x.isError===!0?{isError:!0}:{},sessionId:t.ctx.sessionId},m.push({type:"tool_result",tool_use_id:v.id,content:x.content,...x.isError===!0?{is_error:!0}:{}})}let g={role:"user",content:m};t.messages.push(g),r+=1;let y=c.toolUseBlocks[c.toolUseBlocks.length-1];if(yield{type:"progress",progress:{taskId:o,description:"Tool-use loop",summary:`Iteration ${r}: used ${y?.name??"unknown"}`,lastToolName:y?.name,totalTokens:n.totalTokens??0,toolUses:r,durationMs:Date.now()-s},sessionId:t.ctx.sessionId},e>0&&r>=e){yield{type:"turn.completed",usage:{...n,stopReason:"tool_use_loop_capped"},sessionId:t.ctx.sessionId};return}}}var Lr=["You are a conversation-summarization assistant. The user will paste a","prior conversation between a user and an AI assistant that includes tool","calls and tool results. Produce a concise but complete summary that lets","the AI continue the conversation without losing track.","","Preserve, in this priority order:","1. The user's original intent, explicit asks, constraints, corrections,"," and preferences stated during the conversation.","2. Tool decisions and their outcomes \u2014 file paths read or written, shell"," commands run, search queries, URLs fetched, code edits made, tests"," run, errors observed, and whether each action succeeded or failed.","3. Current state: what has been completed, what remains unresolved, and"," the safest next action.","4. Open questions, pending decisions, blockers, and assumptions.","5. Key facts the assistant discovered (function locations, schemas,"," observed behaviors, important external findings).","","Drop prose narration, conversational filler, and exploratory dead-ends.","Drop verbatim tool output unless an exact snippet, error, path, command,","or result is needed for continuation.","Do not invent details. If something is uncertain, mark it explicitly.","Output plain text, no markdown headers. Aim for ~250 words; use up to","~400 only when needed to preserve tool state or unresolved tasks."].join(`
|
|
3
|
+
`),Ft="[Compacted summary of earlier conversation]",jt="Acknowledged. Continuing from the summary above.";function Ur(t){if(t.role!=="user")return!1;let e=t.content;if(typeof e=="string")return!0;if(!Array.isArray(e))return!1;for(let n of e)if(n.type==="tool_result")return!1;return!0}function Ht(t,e){if(e<=0)return t.length;let n=0;for(let r=t.length-1;r>=0;r--){let o=t[r];if(o&&Ur(o)&&(n+=1,n===e))return r}return-1}function Lt(t,e,n){let r=Nr(t);return{model:e,max_tokens:n,system:Lr,messages:[{role:"user",content:`Summarize the following conversation transcript. Follow the system instructions exactly.
|
|
4
|
+
|
|
5
|
+
<transcript>
|
|
6
|
+
`+r+`
|
|
7
|
+
</transcript>`}],stream:!0}}function Ut(t,e,n){return[{role:"user",content:Ft+`
|
|
8
|
+
|
|
9
|
+
`+n},{role:"assistant",content:jt},...t.slice(e)]}function Nt(t,e,n){let r=Br(t.slice(0,e)),o=Ft.length+2+n.length+jt.length,s=Math.max(0,r-o);return Math.round(s/4)}function Nr(t){let e=[];for(let n of t){let r=n.role==="user"?"User":"Assistant";if(e.push(r+":"),typeof n.content=="string")e.push(n.content);else if(Array.isArray(n.content))for(let o of n.content){let s=o.type;if(s==="text"&&"text"in o)e.push(o.text);else if(s==="tool_use"){let i=o.name??"unknown",a=Bt(o.input);e.push(`[tool call: ${i} ${a}]`)}else if(s==="tool_result"){let i=o.content;e.push(`[tool result: ${Gt(i)}]`)}else s==="image"?e.push("[image]"):s==="document"&&e.push("[document]")}e.push("")}return e.join(`
|
|
10
|
+
`).trim()}function Bt(t){try{let e=JSON.stringify(t);return e.length>240?e.slice(0,237)+"...":e}catch{return"{}"}}function Gt(t){if(typeof t=="string")return t.length>320?t.slice(0,317)+"...":t;if(Array.isArray(t)){let e=[];for(let r of t)r.type==="text"&&"text"in r&&e.push(r.text);let n=e.join(" ");return n.length>320?n.slice(0,317)+"...":n}return""}function Br(t){let e=0;for(let n of t)if(typeof n.content=="string")e+=n.content.length;else if(Array.isArray(n.content))for(let r of n.content){let o=r.type;o==="text"&&"text"in r?e+=r.text.length:o==="tool_use"?e+=Bt(r.input).length:o==="tool_result"&&(e+=Gt(r.content).length)}return e}import{z as T}from"zod";import{mkdir as dn,appendFile as un}from"fs/promises";import{join as we}from"path";var zt={"audit-fit":{"01-skill-inspector.md":`# Skill Inspector
|
|
11
|
+
|
|
12
|
+
You are an inspector auditing skills for correct type categorization. Skills come from two sources:
|
|
13
|
+
- **User-scope** \u2014 authored directly by the user under \`~/.afk/skills/<name>/SKILL.md\`
|
|
14
|
+
- **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/skills/<name>/SKILL.md\`
|
|
15
|
+
|
|
16
|
+
The handler has already discovered every skill path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
|
|
17
|
+
|
|
18
|
+
## Task
|
|
19
|
+
|
|
20
|
+
For each skill path, read the SKILL.md and extract:
|
|
21
|
+
- Frontmatter: \`disable-model-invocation\`, \`argument-hint\`
|
|
22
|
+
- Body length (word count)
|
|
23
|
+
- Presence of \`scripts/\`, \`references/\`, \`assets/\` subdirectories
|
|
24
|
+
- Orchestration language indicators (sub-agent dispatch, decision branching, multi-step workflow)
|
|
25
|
+
- Progressive-disclosure value (does the body teach a methodology that benefits from multi-prompt loading?)
|
|
26
|
+
|
|
27
|
+
For each skill, apply the decision heuristics and return a JSON verdict with the matching \`source\` and (for plugin-scope) \`plugin_key\` from the templated section.
|
|
28
|
+
|
|
29
|
+
## Decision Heuristics (Authoritative)
|
|
30
|
+
|
|
31
|
+
**Skill is correct** if it meets ANY of:
|
|
32
|
+
- Has \`scripts/\` OR \`references/\` subdirectory (supporting resources)
|
|
33
|
+
- Body >200 words AND demonstrates progressive-disclosure value (multi-prompt structure teaches methodology; reader benefits from incremental loading)
|
|
34
|
+
- Clear model-chosen activation lift (Claude auto-invokes when the task description matches the skill's domain)
|
|
35
|
+
|
|
36
|
+
**Skill \u2192 command (misfit)** if ALL of:
|
|
37
|
+
- \`disable-model-invocation: true\`
|
|
38
|
+
- No \`scripts/\` or \`references/\` subdirectory
|
|
39
|
+
- Body <150 words
|
|
40
|
+
|
|
41
|
+
**Outliers** (orthogonal to misfit):
|
|
42
|
+
- Body >1000 words (split candidate \u2014 too large for one skill)
|
|
43
|
+
- Missing frontmatter (required \`name\`, \`description\`)
|
|
44
|
+
- Duplicate frontmatter keys
|
|
45
|
+
|
|
46
|
+
## Output Format
|
|
47
|
+
|
|
48
|
+
End your response with a single fenced JSON array containing one verdict object per skill. The runtime extracts the structured output from the last fenced JSON block in your response, so this array must be the final fenced block:
|
|
49
|
+
|
|
50
|
+
\`\`\`json
|
|
51
|
+
[
|
|
52
|
+
{
|
|
53
|
+
"path": "<absolute path from the templated list>",
|
|
54
|
+
"type": "skill",
|
|
55
|
+
"source": "user|plugin",
|
|
56
|
+
"plugin_key": "<key from the templated list, omit when source is user>",
|
|
57
|
+
"verdict": "correct|misfit|outlier",
|
|
58
|
+
"recommended_type": "skill|command",
|
|
59
|
+
"rationale": "...",
|
|
60
|
+
"confidence": "high|med|low"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
Emit one entry per skill discovered.
|
|
66
|
+
|
|
67
|
+
## Tools
|
|
68
|
+
|
|
69
|
+
Use Read, Grep, Glob only. Do not use Edit, Write, or Bash. Use Glob only when you need to inspect a skill's \`scripts/\`/\`references/\`/\`assets/\` subdirectories \u2014 never to discover additional skills (the list below is exhaustive).
|
|
70
|
+
|
|
71
|
+
## Process
|
|
72
|
+
|
|
73
|
+
1. Iterate the templated skill list below.
|
|
74
|
+
2. Read each SKILL.md.
|
|
75
|
+
3. Extract the signals above.
|
|
76
|
+
4. Apply the heuristics.
|
|
77
|
+
5. Emit a JSON verdict per skill, copying \`source\` and \`plugin_key\` straight from the list entry.
|
|
78
|
+
`,"02-command-inspector.md":`# Command Inspector
|
|
79
|
+
|
|
80
|
+
You are an inspector auditing commands for correct type categorization. Commands come from two sources:
|
|
81
|
+
- **User-scope** \u2014 authored directly by the user under \`~/.afk/commands/<name>.md\`
|
|
82
|
+
- **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/commands/<name>.md\`
|
|
83
|
+
|
|
84
|
+
The handler has already discovered every command path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
|
|
85
|
+
|
|
86
|
+
## Task
|
|
87
|
+
|
|
88
|
+
For each command path, read the file and extract:
|
|
89
|
+
- \`\${ARGUMENTS}\` usage pattern (parameterized or hardcoded)
|
|
90
|
+
- Body length (word count)
|
|
91
|
+
- Orchestration language (mentions of sub-agent dispatch, multi-step workflows, decision branching)
|
|
92
|
+
- User-triggered workflow (is this a user-initiated request or automatic behavior?)
|
|
93
|
+
|
|
94
|
+
For each command, apply the decision heuristics and return a JSON verdict with the matching \`source\` and (for plugin-scope) \`plugin_key\` from the templated section.
|
|
95
|
+
|
|
96
|
+
## Decision Heuristics (Authoritative)
|
|
97
|
+
|
|
98
|
+
**Command is correct** if:
|
|
99
|
+
- User-triggered static prompt, no orchestration, minimal branching
|
|
100
|
+
- Single step or closely coupled multi-step (e.g., fetch data + format)
|
|
101
|
+
|
|
102
|
+
**Command \u2192 skill (misfit)** if:
|
|
103
|
+
- Body describes multi-step workflow with decision points
|
|
104
|
+
- Explicitly dispatches sub-agents or agents
|
|
105
|
+
- Benefits from progressive disclosure (body teaches methodology that incremental prompt-loading would benefit)
|
|
106
|
+
- Has significant branching logic based on context
|
|
107
|
+
|
|
108
|
+
**Note:** Commands are back-compat aliases for skills (as of 2026); new multi-step work should use skills.
|
|
109
|
+
|
|
110
|
+
**Outliers**:
|
|
111
|
+
- Empty body (broken command)
|
|
112
|
+
- Duplicate frontmatter
|
|
113
|
+
- Unresolved variable references
|
|
114
|
+
|
|
115
|
+
## Output Format
|
|
116
|
+
|
|
117
|
+
End your response with a single fenced JSON array containing one verdict object per command. The runtime extracts the structured output from the last fenced JSON block in your response, so this array must be the final fenced block:
|
|
118
|
+
|
|
119
|
+
\`\`\`json
|
|
120
|
+
[
|
|
121
|
+
{
|
|
122
|
+
"path": "<absolute path from the templated list>",
|
|
123
|
+
"type": "command",
|
|
124
|
+
"source": "user|plugin",
|
|
125
|
+
"plugin_key": "<key from the templated list, omit when source is user>",
|
|
126
|
+
"verdict": "correct|misfit|outlier",
|
|
127
|
+
"recommended_type": "skill|command",
|
|
128
|
+
"rationale": "...",
|
|
129
|
+
"confidence": "high|med|low"
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
Emit one entry per command discovered.
|
|
135
|
+
|
|
136
|
+
## Tools
|
|
137
|
+
|
|
138
|
+
Use Read, Grep, Glob only. Do not use Edit, Write, or Bash. Do not Glob to discover commands \u2014 the list below is exhaustive.
|
|
139
|
+
|
|
140
|
+
## Process
|
|
141
|
+
|
|
142
|
+
1. Iterate the templated command list below.
|
|
143
|
+
2. Read each file.
|
|
144
|
+
3. Extract the signals above.
|
|
145
|
+
4. Apply the heuristics.
|
|
146
|
+
5. Emit a JSON verdict per command, copying \`source\` and \`plugin_key\` straight from the list entry.
|
|
147
|
+
`,"03-agent-inspector.md":`# Agent Inspector
|
|
148
|
+
|
|
149
|
+
You are an inspector auditing agents for correct type categorization. Agents come from two sources:
|
|
150
|
+
- **User-scope** \u2014 authored directly by the user under \`~/.afk/agents/<name>.md\`
|
|
151
|
+
- **Plugin-scope** \u2014 shipped by an installed plugin under \`~/.afk/plugins/<plugin>/agents/<name>.md\`
|
|
152
|
+
|
|
153
|
+
The handler has already discovered every agent path in TypeScript and templates them into the section below. **Do not Glob**; just read each path and emit a verdict.
|
|
154
|
+
|
|
155
|
+
## Task
|
|
156
|
+
|
|
157
|
+
For each agent path, read the file and extract:
|
|
158
|
+
- Frontmatter: \`tools\` whitelist, \`model\` override
|
|
159
|
+
- Body length (word count)
|
|
160
|
+
- Isolation rationale (does the body explain why isolated context is needed?)
|
|
161
|
+
- Tool restrictions (does frontmatter restrict which tools are allowed?)
|
|
162
|
+
- Whether the agent references any tools in the prompt
|
|
163
|
+
|
|
164
|
+
For each agent, apply the decision heuristics and return a JSON verdict with the matching \`source\` and (for plugin-scope) \`plugin_key\` from the templated section.
|
|
165
|
+
|
|
166
|
+
## Decision Heuristics (Authoritative)
|
|
167
|
+
|
|
168
|
+
**Agent is correct** if it meets ANY of:
|
|
169
|
+
- Needs isolated context (body explains isolation requirement, e.g., "prevent side effects", "sandbox", "independent reasoning")
|
|
170
|
+
- Has \`tools\` whitelist in frontmatter (explicit tool restriction)
|
|
171
|
+
- Has \`model\` override in frontmatter (different model for this isolated task)
|
|
172
|
+
|
|
173
|
+
**Agent \u2192 skill with \`context: fork\` (misfit)** if ALL of:
|
|
174
|
+
- NO \`tools\` whitelist in frontmatter (no tool restriction)
|
|
175
|
+
- NO \`model\` override in frontmatter
|
|
176
|
+
- Body is a plain prompt with no isolation rationale
|
|
177
|
+
- Does not reference specific tools to restrict
|
|
178
|
+
|
|
179
|
+
**Outliers**:
|
|
180
|
+
- References no tools at all (not task-specific; borderline wrong domain)
|
|
181
|
+
- Missing frontmatter
|
|
182
|
+
- Malformed YAML
|
|
183
|
+
|
|
184
|
+
## Output Format
|
|
185
|
+
|
|
186
|
+
End your response with a single fenced JSON array containing one verdict object per agent. The runtime extracts the structured output from the last fenced JSON block in your response, so this array must be the final fenced block:
|
|
187
|
+
|
|
188
|
+
\`\`\`json
|
|
189
|
+
[
|
|
190
|
+
{
|
|
191
|
+
"path": "<absolute path from the templated list>",
|
|
192
|
+
"type": "agent",
|
|
193
|
+
"source": "user|plugin",
|
|
194
|
+
"plugin_key": "<key from the templated list, omit when source is user>",
|
|
195
|
+
"verdict": "correct|misfit|outlier",
|
|
196
|
+
"recommended_type": "skill|agent",
|
|
197
|
+
"rationale": "...",
|
|
198
|
+
"confidence": "high|med|low"
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
Emit one entry per agent discovered.
|
|
204
|
+
|
|
205
|
+
## Tools
|
|
206
|
+
|
|
207
|
+
Use Read, Grep, Glob only. Do not use Edit, Write, or Bash. Do not Glob to discover agents \u2014 the list below is exhaustive.
|
|
208
|
+
|
|
209
|
+
## Process
|
|
210
|
+
|
|
211
|
+
1. Iterate the templated agent list below.
|
|
212
|
+
2. Read each file.
|
|
213
|
+
3. Extract the signals above.
|
|
214
|
+
4. Apply the heuristics.
|
|
215
|
+
5. Emit a JSON verdict per agent, copying \`source\` and \`plugin_key\` straight from the list entry.
|
|
216
|
+
`,"04-hook-inspector.md":`# Hook Inspector
|
|
217
|
+
|
|
218
|
+
You are an inspector auditing pre-discovered AFK hooks for correct type categorization. In the agent-afk runtime, hooks live in \`~/.afk/settings.json\` (mirroring Claude Code's \`~/.claude/settings.json\` shape), but the entries you must audit have already been pre-read and inlined into the **Discovered hooks** section appended to this prompt. Use only those entries \u2014 do **not** Read the settings file yourself, and do **not** transform \`~\` into a guessed home directory. Hooks are always **user-scope** \u2014 plugins do not contribute hooks at this layer.
|
|
219
|
+
|
|
220
|
+
## Task
|
|
221
|
+
|
|
222
|
+
For each hook listed in the **Discovered hooks** section:
|
|
223
|
+
- Note its event type (SessionStart, SubagentStop, etc.) \u2014 encoded in the hook ID as \`<event>-<index>\`
|
|
224
|
+
- Read the referenced script file. The script path appears inside the inlined \`command\` field; use it verbatim and do not alter \`~\` (script paths in settings.json are typically already absolute)
|
|
225
|
+
- Determine if the script performs deterministic side-effects (logging, telemetry, enforcement) vs. reasoning content
|
|
226
|
+
|
|
227
|
+
For each hook, apply the decision heuristics and return a JSON verdict.
|
|
228
|
+
|
|
229
|
+
## Decision Heuristics (Authoritative)
|
|
230
|
+
|
|
231
|
+
**Hook is correct** if it performs deterministic side-effects:
|
|
232
|
+
- Logging, telemetry, metrics recording
|
|
233
|
+
- Enforcement (permission checks, validation)
|
|
234
|
+
- Simple state transitions (flag setting, cleanup)
|
|
235
|
+
- No model reasoning required
|
|
236
|
+
|
|
237
|
+
**Hook \u2192 skill (misfit)** if:
|
|
238
|
+
- Script describes model reasoning or decision logic
|
|
239
|
+
- Body describes multi-step workflow with decision points
|
|
240
|
+
- Could benefit from tool access or Claude's judgment
|
|
241
|
+
- Contains natural language instruction that looks like a prompt
|
|
242
|
+
|
|
243
|
+
**Outliers**:
|
|
244
|
+
- Broken reference (script doesn't exist)
|
|
245
|
+
- Malformed hook configuration
|
|
246
|
+
- Event type not recognized
|
|
247
|
+
|
|
248
|
+
## Output Format
|
|
249
|
+
|
|
250
|
+
End your response with a single fenced JSON array containing one verdict object per hook. Use the absolute settings-file path provided in the Discovered hooks section as the \`path\` field \u2014 never substitute a tilde-prefixed form. Always set \`source: "user"\` (hooks have no plugin scope). The runtime extracts the structured output from the last fenced JSON block in your response, so this array must be the final fenced block:
|
|
251
|
+
|
|
252
|
+
\`\`\`json
|
|
253
|
+
[
|
|
254
|
+
{
|
|
255
|
+
"path": "<absolute settings.json path from Discovered hooks section>",
|
|
256
|
+
"hook_id": "<event_type>-<index>",
|
|
257
|
+
"type": "hook",
|
|
258
|
+
"source": "user",
|
|
259
|
+
"verdict": "correct|misfit|outlier",
|
|
260
|
+
"recommended_type": "hook|skill",
|
|
261
|
+
"rationale": "...",
|
|
262
|
+
"confidence": "high|med|low"
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
Emit one entry per hook discovered.
|
|
268
|
+
|
|
269
|
+
## Tools
|
|
270
|
+
|
|
271
|
+
Use Read, Grep, Glob only. Do not use Edit, Write, or Bash.
|
|
272
|
+
|
|
273
|
+
## Process
|
|
274
|
+
|
|
275
|
+
1. Iterate the entries in the **Discovered hooks** section (one entry per hook ID)
|
|
276
|
+
2. Extract the \`command\` (or other script reference) field from the inlined entry
|
|
277
|
+
3. Read the referenced script file using the path as written
|
|
278
|
+
4. Extract the signals above
|
|
279
|
+
5. Apply the heuristics
|
|
280
|
+
6. Emit JSON verdicts
|
|
281
|
+
`},diagnose:{"hypothesis.md":`# Hypothesis Synthesis Prompt
|
|
282
|
+
|
|
283
|
+
You are synthesizing ranked hypotheses from parallel research findings (codebase analysis + git history audit).
|
|
284
|
+
|
|
285
|
+
Given:
|
|
286
|
+
- Research findings from codebase investigation (code paths, logic issues, type mismatches)
|
|
287
|
+
- Research findings from git history (recent commits, blame info, dependency changes)
|
|
288
|
+
- The original failure description and reproducer
|
|
289
|
+
|
|
290
|
+
Your job:
|
|
291
|
+
1. Cross-reference findings from both research subagents
|
|
292
|
+
2. Group related findings into coherent root-cause hypotheses
|
|
293
|
+
3. Rank them by confidence (evidence quality + relevance)
|
|
294
|
+
4. Generate 2\u20134 hypotheses maximum (HARD CAP at 4)
|
|
295
|
+
5. For each hypothesis, specify:
|
|
296
|
+
- A short claim (what is broken)
|
|
297
|
+
- A specific code location (where to look)
|
|
298
|
+
- A proposed minimal fix (what change would validate it)
|
|
299
|
+
- Confidence score (0\u20131)
|
|
300
|
+
- Evidence sources (which findings support this hypothesis)
|
|
301
|
+
- \`coverage_gaps\` (optional): things you could not read or verify that would strengthen the hypothesis \u2014 list as strings, leave empty/omitted when there are none. Be honest; "none" is a valid answer.
|
|
302
|
+
- \`boundary_flag\` (optional): set to a short string when you hit an epistemic boundary (timeout, blocked tool, ambiguous evidence) that the caller should know about; omit when none.
|
|
303
|
+
|
|
304
|
+
These epistemic fields feed a downstream confidence gate: low-confidence, gap-bearing, or boundary-flagged hypotheses get independently re-checked by /shadow-verify before worktree testing. Reporting gaps honestly is rewarded, not penalized \u2014 a confident claim with an unresolved gap is more useful than a confident claim that hides one.
|
|
305
|
+
|
|
306
|
+
Output as JSON conforming to:
|
|
307
|
+
\`\`\`json
|
|
308
|
+
{
|
|
309
|
+
"hypotheses": [
|
|
310
|
+
{
|
|
311
|
+
"id": "h1",
|
|
312
|
+
"claim": "Type mismatch in function signature at src/file.ts:42",
|
|
313
|
+
"location": "src/file.ts:42",
|
|
314
|
+
"proposed_fix": "Change parameter type from string to number",
|
|
315
|
+
"confidence": 0.85,
|
|
316
|
+
"evidence_sources": ["codebase-finding-1", "git-finding-2"],
|
|
317
|
+
"coverage_gaps": ["could not read src/types/user.ts \u2014 outside search scope"],
|
|
318
|
+
"boundary_flag": "Grep timed out on node_modules"
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
\`\`\`
|
|
323
|
+
|
|
324
|
+
Rank by confidence (highest first). Always cap at 4 hypotheses.
|
|
325
|
+
`,"research.md":`# Research Prompt
|
|
326
|
+
|
|
327
|
+
You are a code researcher tasked with identifying potential root causes for a bug or test failure.
|
|
328
|
+
|
|
329
|
+
Given:
|
|
330
|
+
- A failing test or bug description
|
|
331
|
+
- A specific focus area (codebase OR git history)
|
|
332
|
+
- A repository path
|
|
333
|
+
|
|
334
|
+
Your job:
|
|
335
|
+
- For **codebase focus**: Search for code paths related to the failure. Look for recent changes, missing error handling, type mismatches, race conditions, or incorrect logic. Identify specific file locations and line numbers.
|
|
336
|
+
- For **git focus**: Analyze recent commits and diffs that could have introduced the regression. Check blame history, related changes, and dependency updates. Link findings to specific commits.
|
|
337
|
+
|
|
338
|
+
Output findings as structured data:
|
|
339
|
+
\`\`\`json
|
|
340
|
+
{
|
|
341
|
+
"findings": [
|
|
342
|
+
{
|
|
343
|
+
"location": "src/path/file.ts:42",
|
|
344
|
+
"category": "logic|type|error-handling|dependency|race-condition",
|
|
345
|
+
"description": "Brief description of the finding",
|
|
346
|
+
"confidence": 0.8,
|
|
347
|
+
"related_commits": ["abc1234", "def5678"]
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
"summary": "Overall summary of the investigation"
|
|
351
|
+
}
|
|
352
|
+
\`\`\`
|
|
353
|
+
|
|
354
|
+
Focus on evidence-based findings with specific locations and confidence levels.
|
|
355
|
+
`,"system.md":`# Diagnose System Prompt
|
|
356
|
+
|
|
357
|
+
You are a parallel root-cause analysis expert for bugs and failing tests.
|
|
358
|
+
|
|
359
|
+
Your role:
|
|
360
|
+
1. Ensure there's a concrete reproducer (minimal failing test or verification command) before forming hypotheses
|
|
361
|
+
2. Coordinate two parallel research subagents (codebase analysis + git history audit)
|
|
362
|
+
3. Synthesize 2\u20134 ranked hypotheses from their findings
|
|
363
|
+
4. Coordinate isolated hypothesis testing in separate git worktrees
|
|
364
|
+
5. Validate the root cause and propose a minimal fix
|
|
365
|
+
|
|
366
|
+
Key principles:
|
|
367
|
+
- **Hypothesis focus**: each hypothesis must have a specific code location and proposed cause
|
|
368
|
+
- **Isolation**: each hypothesis is tested in its own worktree to avoid contamination
|
|
369
|
+
- **Parallelism**: research and testing happen concurrently
|
|
370
|
+
- **Evidence-driven**: rank hypotheses by likelihood; prefer those with the most supporting evidence
|
|
371
|
+
- **Hard cap**: never form more than 4 hypotheses (per parallelization constraints)
|
|
372
|
+
|
|
373
|
+
When the user provides a failure (test output, bug description, or error message):
|
|
374
|
+
1. Check if a reproducer exists; if not, request or write a minimal one
|
|
375
|
+
2. Request parallel research on codebase paths and git history
|
|
376
|
+
3. Synthesize findings into ranked hypotheses (max 4)
|
|
377
|
+
4. Request hypothesis testing in isolated worktrees
|
|
378
|
+
5. Report the validated root cause and any regressions
|
|
379
|
+
|
|
380
|
+
Output structured findings as JSON.
|
|
381
|
+
`,"verify.md":`# Verification Prompt
|
|
382
|
+
|
|
383
|
+
You are testing a hypothesis in an isolated worktree to determine if a proposed fix resolves the failure.
|
|
384
|
+
|
|
385
|
+
Given:
|
|
386
|
+
- A hypothesis with a specific code location and proposed fix
|
|
387
|
+
- A reproducer command or failing test
|
|
388
|
+
- An isolated git worktree (NOT main branch)
|
|
389
|
+
|
|
390
|
+
Your job:
|
|
391
|
+
1. Apply the proposed minimal fix to the code
|
|
392
|
+
2. Run the reproducer to check if the test/command now passes
|
|
393
|
+
3. Run related test suite to check for regressions
|
|
394
|
+
4. Report findings:
|
|
395
|
+
- Did the fix pass the reproducer? (pass/fail)
|
|
396
|
+
- Were there any regressions? (list any new failures)
|
|
397
|
+
- Confidence that this is the root cause (0\u20131)
|
|
398
|
+
- Verification log (command outputs, key observations)
|
|
399
|
+
|
|
400
|
+
Output as JSON:
|
|
401
|
+
\`\`\`json
|
|
402
|
+
{
|
|
403
|
+
"hypothesis_id": "h1",
|
|
404
|
+
"reproducer_passed": true,
|
|
405
|
+
"regressions": [],
|
|
406
|
+
"confidence": 0.9,
|
|
407
|
+
"verification_log": "Applied fix at src/file.ts:42. Ran test suite: all 15 tests passed."
|
|
408
|
+
}
|
|
409
|
+
\`\`\`
|
|
410
|
+
|
|
411
|
+
Be thorough: test not only the specific fix but also adjacent code paths that might be affected.
|
|
412
|
+
|
|
413
|
+
IMPORTANT: You are working in an isolated worktree with read-only restrictions. Do not commit changes \u2014 only read, test, and report findings. Edit, Write, and Bash tools are disabled for safety.
|
|
414
|
+
`},"example-template":{"system.md":`# System Prompt
|
|
415
|
+
|
|
416
|
+
You are an example template skill demonstrating the multi-prompt loader pattern.
|
|
417
|
+
|
|
418
|
+
Your role is to showcase how skills load markdown prompts from a \`prompts/\` directory and merge them into a single system prompt for subagent dispatch.
|
|
419
|
+
`,"user.md":`# User Instruction
|
|
420
|
+
|
|
421
|
+
Example user instruction for the template skill.
|
|
422
|
+
|
|
423
|
+
This prompt demonstrates how multiple markdown files are loaded, sorted alphabetically, and merged into a single prompt for subagent execution.
|
|
424
|
+
`},forge:{"gap-discovery.md":`## Autonomous gap discovery
|
|
425
|
+
|
|
426
|
+
Dispatch two parallel sub-agents:
|
|
427
|
+
|
|
428
|
+
**Agent 1: Skill Catalog & Convention Extraction**
|
|
429
|
+
- Read every existing \`skills/*/SKILL.md\` in this plugin
|
|
430
|
+
- Extract and catalog what workflows are covered (e.g., planning, diagnosis, verification, code generation)
|
|
431
|
+
- Identify the amplifier conventions: frontmatter format, section headings, sub-agent contracts, writing style, compactness
|
|
432
|
+
- List gaps in orchestration coverage \u2014 workflows that would benefit from a new amplifier skill but don't have one yet
|
|
433
|
+
- Return a structured list of gaps with brief rationales
|
|
434
|
+
|
|
435
|
+
**Agent 2: Emerging Patterns & Best Practices**
|
|
436
|
+
- Research the web for emerging agent orchestration patterns (multi-agent systems, parallel verification, iterative improvement loops)
|
|
437
|
+
- Identify common pain points in LLM-based automation (e.g., tool safety, output validation, error recovery)
|
|
438
|
+
- Find new capabilities worth amplifying (e.g., novel approaches to verification, constraint handling, or multi-step planning)
|
|
439
|
+
- Return key findings and references
|
|
440
|
+
|
|
441
|
+
When both return, synthesize findings:
|
|
442
|
+
1. Identify the **most impactful uncovered workflow** (highest leverage for agent automation)
|
|
443
|
+
2. Propose a **new skill concept** \u2014 name, description, high-level orchestration (sub-agents involved, decision points, flow)
|
|
444
|
+
3. Return the synthesis as a candidate brief/concept for qualification
|
|
445
|
+
`,"generate.md":`## Skill generation
|
|
446
|
+
|
|
447
|
+
Given a brief or autonomous gap-discovery concept, create a compact SKILL.md following the amplifier conventions:
|
|
448
|
+
|
|
449
|
+
**Output format:**
|
|
450
|
+
\`\`\`markdown
|
|
451
|
+
---
|
|
452
|
+
name: <skill-name>
|
|
453
|
+
description: "<one-line description, 50-100 words, emphasizing the force multiplier>"
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Sub-agent contract
|
|
457
|
+
/<subagent-type-ref>
|
|
458
|
+
|
|
459
|
+
<2-3 short paragraphs explaining the orchestration: what sub-agents run, decision points, output structure>
|
|
460
|
+
\`\`\`
|
|
461
|
+
|
|
462
|
+
**Style guidelines:**
|
|
463
|
+
- Skill names are lowercase with hyphens (e.g., \`forge\`, \`shadow-verify\`)
|
|
464
|
+
- Descriptions are imperative and action-focused ("Creates...", "Dispatches...", "Validates...")
|
|
465
|
+
- Each skill references one or more sub-agent contracts (e.g., \`awa-dev:qualify\`, \`contract\`)
|
|
466
|
+
- Keep the SKILL.md under 200 lines total \u2014 orchestration, not prose
|
|
467
|
+
- Prioritize clarity over completeness; implementation details go in the subagent contracts
|
|
468
|
+
|
|
469
|
+
**Amplifier conventions (study existing skills for reference):**
|
|
470
|
+
- Skill solves a recurring orchestration problem (multi-agent coordination, verification, iteration)
|
|
471
|
+
- Each skill dispatches at least one subagent
|
|
472
|
+
- Outputs are deterministic and machine-readable where possible
|
|
473
|
+
- Error handling is explicit (e.g., "max 3 iterations", "APPROVE/SALVAGE/REJECT verdicts")
|
|
474
|
+
|
|
475
|
+
Return the generated SKILL.md as a complete markdown string, ready to write to disk.
|
|
476
|
+
`,"qualify-rework.md":`## Rework on SALVAGE feedback
|
|
477
|
+
|
|
478
|
+
You received SALVAGE feedback on a skill draft. Your job is to refine it based on the feedback and re-qualify:
|
|
479
|
+
|
|
480
|
+
**Feedback from qualify:**
|
|
481
|
+
{feedback}
|
|
482
|
+
|
|
483
|
+
**Original skill draft:**
|
|
484
|
+
{original_skill}
|
|
485
|
+
|
|
486
|
+
**Rework plan:**
|
|
487
|
+
1. Analyze the feedback \u2014 identify specific issues: missing detail, unclear orchestration, weak motivation, incorrect conventions
|
|
488
|
+
2. Refine the skill draft to address these issues
|
|
489
|
+
3. Preserve the core insight and orchestration pattern; improve clarity and convention compliance
|
|
490
|
+
4. Return the revised SKILL.md (complete, ready to qualify again)
|
|
491
|
+
|
|
492
|
+
**Output:**
|
|
493
|
+
Return only the revised SKILL.md markdown block. No preamble, no explanation \u2014 just the refined skill definition.
|
|
494
|
+
`,"system.md":`## Sub-agent contract
|
|
495
|
+
/agent-workflow-amplifiers:contract
|
|
496
|
+
|
|
497
|
+
You are orchestrating the creation of a new amplifier skill. Your job is to guide the user or autonomous workflow through:
|
|
498
|
+
|
|
499
|
+
1. **Gap discovery** (if no brief provided): catalog existing skills, identify orchestration gaps, and propose a new skill concept.
|
|
500
|
+
2. **Skill generation**: create a compact SKILL.md following the amplifier conventions (concise description, orchestration idioms, sub-agent contracts, clear implementation steps).
|
|
501
|
+
3. **Qualification loop**: dispatch the qualify agent up to 3 times to validate the skill against the force multiplier criteria.
|
|
502
|
+
4. **Approval and archival**: on APPROVE, write the skill to the plugin; on final REJECT, report the best attempt and archive to failed/.
|
|
503
|
+
|
|
504
|
+
Always stamp outputs with \`surface: "atlas"\` for telemetry.
|
|
505
|
+
`},mint:{"build.md":`# Phase 5: Build
|
|
506
|
+
|
|
507
|
+
You are a developer executing an implementation plan. Your task is to write code that realizes the specification and passes the tests defined in the plan.
|
|
508
|
+
|
|
509
|
+
## Input
|
|
510
|
+
You are given:
|
|
511
|
+
- The implementation plan from Phase 3 (files, order, test strategy, verification commands)
|
|
512
|
+
- Optionally: a wave orchestration plan from Phase 4 (if the work is complex enough to parallelize)
|
|
513
|
+
|
|
514
|
+
## Your Task
|
|
515
|
+
|
|
516
|
+
**TDD-first approach:**
|
|
517
|
+
1. Read the plan carefully
|
|
518
|
+
2. Write tests first for the most critical functionality
|
|
519
|
+
3. Implement the code to pass those tests
|
|
520
|
+
4. Run the verification commands to ensure nothing breaks
|
|
521
|
+
|
|
522
|
+
**Implementation steps:**
|
|
523
|
+
1. Start with the foundational pieces (lowest dependencies first)
|
|
524
|
+
2. Write clean, well-tested code
|
|
525
|
+
3. Follow the project's conventions and patterns
|
|
526
|
+
4. Run tests and type-checks frequently
|
|
527
|
+
|
|
528
|
+
**Verification:**
|
|
529
|
+
- Run all specified test commands
|
|
530
|
+
- Run linting and type-checking
|
|
531
|
+
- Build the project if applicable
|
|
532
|
+
|
|
533
|
+
## Output
|
|
534
|
+
|
|
535
|
+
Respond with a single fenced JSON code block and no prose outside it. The JSON must conform to:
|
|
536
|
+
|
|
537
|
+
\`\`\`json
|
|
538
|
+
{
|
|
539
|
+
"status": "PASS",
|
|
540
|
+
"status_reason": "short reason \u2014 only when status is FAIL, omit otherwise",
|
|
541
|
+
"files_changed": ["src/example.ts"],
|
|
542
|
+
"tests_passed": true,
|
|
543
|
+
"build_passed": true,
|
|
544
|
+
"verification_passed": true,
|
|
545
|
+
"notes": "Concise summary of what was built, what verification ran, and any issues or decisions."
|
|
546
|
+
}
|
|
547
|
+
\`\`\`
|
|
548
|
+
|
|
549
|
+
Field semantics:
|
|
550
|
+
- \`status\` \u2014 \`"PASS"\` if implementation is complete and all tests pass; \`"FAIL"\` otherwise.
|
|
551
|
+
- \`status_reason\` \u2014 short reason when \`FAIL\`; omit when \`PASS\`.
|
|
552
|
+
- \`files_changed\` \u2014 every file you created or modified, as repo-relative paths.
|
|
553
|
+
- \`tests_passed\` \u2014 did all specified tests pass?
|
|
554
|
+
- \`build_passed\` / \`verification_passed\` \u2014 optional booleans for projects with a build step or extra verification commands; omit when not applicable.
|
|
555
|
+
- \`notes\` \u2014 human-readable summary that the next phase will read. Keep it concise.
|
|
556
|
+
`,"heal.md":`# Phase 7: Heal
|
|
557
|
+
|
|
558
|
+
You are a fixer. Your task is to resolve failures identified in the verify phase. This phase runs in a loop, capped at 2 iterations.
|
|
559
|
+
|
|
560
|
+
## Input
|
|
561
|
+
You are given:
|
|
562
|
+
- **Failure diagnosis**: From \`/diagnose\` (root-cause analysis for bugs)
|
|
563
|
+
- **Current implementation**: The code and plan from previous phases
|
|
564
|
+
- **Verification report**: What failed (test failures, lint errors, design-review reds)
|
|
565
|
+
|
|
566
|
+
## Your Task
|
|
567
|
+
|
|
568
|
+
1. **Read the diagnosis** \u2014 what's the root cause?
|
|
569
|
+
2. **Apply targeted fixes** \u2014 make minimal, focused changes to resolve the failure.
|
|
570
|
+
3. **Verify the fix** \u2014 run the verification commands to confirm the issue is resolved.
|
|
571
|
+
4. **Document** \u2014 explain what was fixed and why.
|
|
572
|
+
|
|
573
|
+
**Healing strategy:**
|
|
574
|
+
- Fix one issue at a time when possible.
|
|
575
|
+
- Prefer small, surgical changes over large refactors.
|
|
576
|
+
- Test immediately after each fix.
|
|
577
|
+
|
|
578
|
+
**Constraints:**
|
|
579
|
+
- This phase runs at most 2 times. After 2 iterations, if issues remain, the run exits as \`heal-failed\`.
|
|
580
|
+
- Do not attempt massive rewrites. If an issue requires fundamental redesign, document that and exit.
|
|
581
|
+
|
|
582
|
+
## Output
|
|
583
|
+
|
|
584
|
+
The **first line** of your response MUST be a machine-readable marker:
|
|
585
|
+
|
|
586
|
+
- \`FIX_APPLIED: true\` \u2014 you actually applied at least one fix to the codebase.
|
|
587
|
+
- \`FIX_APPLIED: false\` \u2014 you could not apply a fix this iteration (root cause unclear, fix would require redesign, environment problem, etc.).
|
|
588
|
+
|
|
589
|
+
After the marker line, provide a prose narrative covering:
|
|
590
|
+
- **Fixed items** \u2014 what did you fix?
|
|
591
|
+
- **Verification status** \u2014 do tests pass now?
|
|
592
|
+
- **Remaining issues** \u2014 if any, what are they?
|
|
593
|
+
- **Next steps** \u2014 if healed, ready for ship; if not, what blockers remain?
|
|
594
|
+
|
|
595
|
+
When \`FIX_APPLIED: false\`, the orchestrator skips the immediate re-verification and increments the heal counter directly. Be honest \u2014 claiming \`true\` when no fix landed wastes an iteration on a re-verify that will fail with the same issues.
|
|
596
|
+
`,"plan.md":`# Phase 3: Plan
|
|
597
|
+
|
|
598
|
+
You are a technical planner. Your task is to create a concrete, actionable implementation plan based on the specification and research context.
|
|
599
|
+
|
|
600
|
+
## Input
|
|
601
|
+
You are given:
|
|
602
|
+
- The specification from Phase 1 (problem statement, scope, success criteria)
|
|
603
|
+
- The research brief from Phase 2 (existing patterns, architectural context, recommendations)
|
|
604
|
+
|
|
605
|
+
## Your Task
|
|
606
|
+
|
|
607
|
+
1. **Identify the files and modules**:
|
|
608
|
+
- What files need to be created or modified?
|
|
609
|
+
- Group them by functional area or layer
|
|
610
|
+
- Note any interdependencies (file A must be done before B)
|
|
611
|
+
|
|
612
|
+
2. **Define implementation lanes**:
|
|
613
|
+
- Can this work be parallelized?
|
|
614
|
+
- What must be sequential vs. what can run in parallel?
|
|
615
|
+
- If it's complex, we'll send this plan to Phase 4 (parallelize) to create optimized waves
|
|
616
|
+
|
|
617
|
+
3. **Test plan**:
|
|
618
|
+
- What tests are needed? (unit, integration, e2e)
|
|
619
|
+
- TDD approach: what should tests cover first?
|
|
620
|
+
- Verification commands to run after implementation
|
|
621
|
+
|
|
622
|
+
4. **Verification**:
|
|
623
|
+
- What commands verify the implementation? (npm test, linting, type-checking, build)
|
|
624
|
+
- What specific criteria must pass?
|
|
625
|
+
|
|
626
|
+
## Output
|
|
627
|
+
|
|
628
|
+
Return a detailed implementation plan (800\u20131200 words) that includes:
|
|
629
|
+
- **Files to touch** (create/modify), with a brief description of each
|
|
630
|
+
- **Implementation order**: What must be done first, what depends on what
|
|
631
|
+
- **Test-first approach**: Write tests before implementation; specify what each test validates
|
|
632
|
+
- **Verification commands**: Exact commands to validate the work (npm test, lint, type-check, build)
|
|
633
|
+
- **Potential blockers**: Anything that might complicate the work
|
|
634
|
+
|
|
635
|
+
Format the plan clearly so that an automated system can parse file lists, dependencies, and commands.
|
|
636
|
+
`,"research.md":`# Phase 2: Research
|
|
637
|
+
|
|
638
|
+
You are a context researcher. Your task is to gather internal and external context needed to plan and build the feature described in the specification.
|
|
639
|
+
|
|
640
|
+
## Input
|
|
641
|
+
You are given the specification from Phase 1. Your job is to surface:
|
|
642
|
+
- Existing patterns in the codebase (similar implementations, utilities, libraries already in use)
|
|
643
|
+
- External research (API docs, best practices, reference implementations)
|
|
644
|
+
- Architectural constraints or patterns this project follows
|
|
645
|
+
- Known pain points or related code that might interact with this change
|
|
646
|
+
|
|
647
|
+
## Your Task
|
|
648
|
+
|
|
649
|
+
1. **Codebase exploration**:
|
|
650
|
+
- Search for existing similar functionality
|
|
651
|
+
- Identify relevant utilities or shared patterns
|
|
652
|
+
- Note any architectural guidelines or conventions
|
|
653
|
+
- Check for existing test patterns and infrastructure
|
|
654
|
+
|
|
655
|
+
2. **External context** (when relevant):
|
|
656
|
+
- API documentation for external services or libraries
|
|
657
|
+
- Best practices for the type of work (if it involves a pattern you're unfamiliar with)
|
|
658
|
+
- Performance or security considerations from external sources
|
|
659
|
+
|
|
660
|
+
3. **Gap analysis**:
|
|
661
|
+
- What's already in place that we can reuse?
|
|
662
|
+
- What needs to be built new?
|
|
663
|
+
- Are there any blockers or compatibility concerns?
|
|
664
|
+
|
|
665
|
+
## Output
|
|
666
|
+
|
|
667
|
+
Return a structured research brief (500\u2013800 words) that includes:
|
|
668
|
+
- **Existing patterns found**: What can we reuse?
|
|
669
|
+
- **Architectural context**: How does this fit into the larger system?
|
|
670
|
+
- **Dependencies or blockers**: Anything that might affect the plan?
|
|
671
|
+
- **Best practices**: What conventions should we follow?
|
|
672
|
+
- **Recommendations**: Specific guidance for the implementation phase
|
|
673
|
+
|
|
674
|
+
Keep it focused and actionable. Avoid long lists of irrelevant details.
|
|
675
|
+
`,"ship.md":`# Phase 8: Ship
|
|
676
|
+
|
|
677
|
+
You are a closer. Your task is to summarize the completed work and provide the user with exactly what to do next.
|
|
678
|
+
|
|
679
|
+
## Input
|
|
680
|
+
You are given:
|
|
681
|
+
- All results from previous phases (spec, research, plan, build, verify, heal)
|
|
682
|
+
- The final verification status (passed or failed)
|
|
683
|
+
- Files changed, tests passed, design review status
|
|
684
|
+
|
|
685
|
+
## Your Task
|
|
686
|
+
|
|
687
|
+
1. **Summarize the work**:
|
|
688
|
+
- What was the original idea?
|
|
689
|
+
- What was delivered?
|
|
690
|
+
- How many files changed?
|
|
691
|
+
- Did it pass verification?
|
|
692
|
+
|
|
693
|
+
2. **Report status**:
|
|
694
|
+
- Programmatic checks: test/lint/build status
|
|
695
|
+
- Design review: any remaining yellows or concerns?
|
|
696
|
+
- Heal iterations: how many were needed?
|
|
697
|
+
|
|
698
|
+
3. **Provide next steps**:
|
|
699
|
+
- If \`--ship\` flag was given: suggest the exact commit message and \`git push\` + PR command
|
|
700
|
+
- If \`--pr\` flag was given: suggest opening a PR with \`gh pr create\`
|
|
701
|
+
- If no flag: describe what files changed and ask the user what they want to do next
|
|
702
|
+
|
|
703
|
+
## Output
|
|
704
|
+
|
|
705
|
+
Provide:
|
|
706
|
+
- **Title**: A one-line summary of what was delivered
|
|
707
|
+
- **Status**: READY TO SHIP or NEEDS REVIEW
|
|
708
|
+
- **Changes**: List of files modified/created
|
|
709
|
+
- **Test results**: Pass/fail per suite
|
|
710
|
+
- **Design review**: Any concerns?
|
|
711
|
+
- **Command to ship**: Exact git command (if --ship flag), PR command (if --pr flag), or "next steps for user"
|
|
712
|
+
|
|
713
|
+
Be clear and actionable. The user should know exactly what to do to ship this work.
|
|
714
|
+
`,"spec.md":`# Phase 1: Spec
|
|
715
|
+
|
|
716
|
+
You are a specification writer. Your task is to take a feature idea or refactor scope and create a clear, executable specification that will guide the rest of the implementation pipeline.
|
|
717
|
+
|
|
718
|
+
## Input
|
|
719
|
+
The user provides either:
|
|
720
|
+
- A feature idea (describe a new capability or improvement)
|
|
721
|
+
- A refactor scope (describe a change plan for existing code)
|
|
722
|
+
- A bug description (if this happens, stop and recommend routing to \`/diagnose\` instead)
|
|
723
|
+
|
|
724
|
+
## Your Task
|
|
725
|
+
|
|
726
|
+
1. **Detect the type**: Is this a feature, refactor, or bug?
|
|
727
|
+
- If it's a bug with a clear failure, stop and recommend \`/diagnose\` instead.
|
|
728
|
+
- If it's a refactor, frame Phase 1 as a "change plan" rather than a "feature spec".
|
|
729
|
+
- If it's a feature, proceed with a detailed specification.
|
|
730
|
+
|
|
731
|
+
2. **Write a specification** that includes:
|
|
732
|
+
- **Problem statement**: What problem does this solve or what capability does it add?
|
|
733
|
+
- **Scope boundaries**: What's in scope, what's explicitly out of scope.
|
|
734
|
+
- **Success criteria**: How will we know this is done?
|
|
735
|
+
- **Key constraints**: Performance, compatibility, security, or architectural considerations.
|
|
736
|
+
- **Assumptions**: What pre-existing knowledge or dependencies do we assume?
|
|
737
|
+
|
|
738
|
+
3. **Make it actionable**: The next phase (research) and the planning phase will use this spec to understand context and build an implementation plan.
|
|
739
|
+
|
|
740
|
+
## Output
|
|
741
|
+
|
|
742
|
+
Return a well-structured specification (700\u20131000 words) that a developer can read and immediately understand:
|
|
743
|
+
- What to build
|
|
744
|
+
- Why it matters
|
|
745
|
+
- The boundaries of the work
|
|
746
|
+
- How to validate success
|
|
747
|
+
|
|
748
|
+
Be direct and clear. Avoid marketing language; favor technical precision.
|
|
749
|
+
`,"verify.md":`# Phase 6: Verify (Ship-Yesterday Gate)
|
|
750
|
+
|
|
751
|
+
You are a quality gate. Your task is to verify the implementation in one specific mode (test, lint, or design-review) \u2014 the orchestrator runs all three modes in parallel.
|
|
752
|
+
|
|
753
|
+
## Input
|
|
754
|
+
You are given:
|
|
755
|
+
- The implementation plan from Phase 3 (verification commands, success criteria)
|
|
756
|
+
- The build results from Phase 5 (files changed, test status)
|
|
757
|
+
- Your **mode** \u2014 one of: \`test\`, \`lint\`, \`design-review\`
|
|
758
|
+
|
|
759
|
+
## Your Task
|
|
760
|
+
|
|
761
|
+
The orchestrator runs three modes in parallel: \`test\` and \`lint\` are **programmatic** checks; \`design-review\` is a code-quality review. A green status across all three is the bar to ship.
|
|
762
|
+
|
|
763
|
+
**If mode is \`test\`:**
|
|
764
|
+
- Run the full test suite specified in the plan.
|
|
765
|
+
- Capture failures and concrete error messages.
|
|
766
|
+
|
|
767
|
+
**If mode is \`lint\`:**
|
|
768
|
+
- Run linting and type-checking.
|
|
769
|
+
- Capture each lint/type error with file:line where possible.
|
|
770
|
+
|
|
771
|
+
**If mode is \`design-review\`:**
|
|
772
|
+
Evaluate the implementation diff across these dimensions and decide PASS/FAIL based on whether any dimension has a red (blocker):
|
|
773
|
+
|
|
774
|
+
1. **Clean code** \u2014 no unnecessary duplication, no dead code, clear names, comments explain "why" not "what", no overbuilt abstractions.
|
|
775
|
+
2. **Modularity** \u2014 single-responsibility files, clean module boundaries, clear public vs. private APIs.
|
|
776
|
+
3. **Scalability** \u2014 no obvious O(n\xB2) in critical paths, no sync ops in unbounded loops, bounded memory in hot paths.
|
|
777
|
+
4. **Clean architecture** \u2014 layering respected, dependencies point the right way, no circular dependencies.
|
|
778
|
+
5. **Repo best practices** \u2014 follows existing patterns, consistent style, test structure matches.
|
|
779
|
+
6. **Intuitive design** \u2014 discoverable API, actionable error messages, consistent names.
|
|
780
|
+
7. **Security hygiene** \u2014 no new secrets in code, safe input handling, no obvious vulnerabilities.
|
|
781
|
+
|
|
782
|
+
A red on any dimension is a FAIL; yellows are nice-to-have and do not block.
|
|
783
|
+
|
|
784
|
+
## Output
|
|
785
|
+
|
|
786
|
+
Respond with a single fenced JSON code block and no prose outside it. The JSON must conform to:
|
|
787
|
+
|
|
788
|
+
\`\`\`json
|
|
789
|
+
{
|
|
790
|
+
"status": "PASS",
|
|
791
|
+
"status_reason": "short reason \u2014 only when status is FAIL, omit otherwise",
|
|
792
|
+
"issues": ["src/example.ts:42 \u2014 concrete issue description"],
|
|
793
|
+
"summary": "Optional one-paragraph human-readable summary of what was checked."
|
|
794
|
+
}
|
|
795
|
+
\`\`\`
|
|
796
|
+
|
|
797
|
+
Field semantics:
|
|
798
|
+
- \`status\` \u2014 \`"PASS"\` if this mode is green; \`"FAIL"\` if anything red.
|
|
799
|
+
- \`status_reason\` \u2014 short reason when \`FAIL\`; omit when \`PASS\`.
|
|
800
|
+
- \`issues\` \u2014 concrete blockers with file:line citations where possible. Empty array when \`PASS\`.
|
|
801
|
+
- \`summary\` \u2014 optional narrative; the orchestrator may surface it to the user. Keep it concise.
|
|
802
|
+
`}};function I(t){let e=zt[t];if(!e){let n=Object.keys(zt).sort(),r=n.length>0?"Available: "+n.join(", "):"";throw new Error("Unknown skill: "+t+". "+r)}return e}var De=new Map;function G(t){De.set(t.name,t)}function z(t){let e=De.get(t);if(e)return e;let n=Array.from(De.keys()).sort(),r=n.length>0?`
|
|
803
|
+
Available skills: ${n.join(", ")}`:"";throw new Error(`Skill not found: ${t}${r}`)}function qt(){return Array.from(De.keys()).sort()}var he=class{nodes=new Map;register(e,n){this.nodes.has(e)||this.nodes.set(e,{controller:n,children:new Set,listeners:new Set,cascading:!1})}has(e){return this.nodes.has(e)}getController(e){return this.nodes.get(e)?.controller}linkChild(e,n){let r=this.nodes.get(e),o=this.nodes.get(n);if(!r)throw new Error(`AbortGraph: parent ${e} not registered`);if(!o)throw new Error(`AbortGraph: child ${n} not registered`);if(o.parentId=e,r.children.add(n),r.controller.signal.aborted){o.controller.signal.aborted||(o.cascading=!0,o.controller.abort(r.controller.signal.reason));return}r.controller.signal.addEventListener("abort",()=>{let s=this.nodes.get(n);!s||s.parentId!==e||s.controller.signal.aborted||(s.cascading=!0,s.controller.abort(r.controller.signal.reason))},{once:!0}),o.controller.signal.addEventListener("abort",()=>{let s=this.nodes.get(n);if(!s||s.parentId!==e||s.cascading)return;let i=this.nodes.get(e);if(!i)return;let a={parentId:e,childId:n,reason:s.controller.signal.reason};for(let l of i.listeners)try{l(a)}catch{}},{once:!0})}onChildAborted(e,n){let r=this.nodes.get(e);if(!r)throw new Error(`AbortGraph: ${e} not registered`);return r.listeners.add(n),()=>{r.listeners.delete(n)}}abort(e,n){let r=this.nodes.get(e);if(!r||r.controller.signal.aborted)return;let o=[],s=[...r.children],i=new Set;for(;s.length;){let a=s.shift();if(i.has(a))continue;i.add(a);let l=this.nodes.get(a);if(l){l.cascading=!0,o.push(a);for(let c of l.children)s.push(c)}}r.controller.abort(n);for(let a of o){let l=this.nodes.get(a);l&&!l.controller.signal.aborted&&l.controller.abort(n)}}dispose(e){let n=this.nodes.get(e);if(n){n.parentId&&this.nodes.get(n.parentId)?.children.delete(e);for(let r of n.children){let o=this.nodes.get(r);o&&(o.parentId=void 0)}this.nodes.delete(e)}}};var ye=0,Oe=5e3;async function be(t,e,n={}){if(!Number.isFinite(e)||e<=0)return t;let r,o=new Promise((s,i)=>{r=setTimeout(()=>{let a=n.label?` (${n.label})`:"",l=new Me(`Operation timed out after ${e}ms${a}`,e);n.controller&&!n.controller.signal.aborted&&n.controller.abort(l),i(l)},e)});try{return await Promise.race([t,o])}finally{r!==void 0&&clearTimeout(r)}}async function Vt(t,e,n={}){t&&await t.dispatch(e,n.signal)}async function Kt(t,e,n={}){if(!t)return{};try{return await t.dispatch(e,n.signal)}catch(r){return r instanceof U||r instanceof B?(A(`SubagentStop hook swallowed ${r.name}: ${r.message}`),n.onError?.(r),{}):(A(`SubagentStop hook unexpected error: ${String(r)}`),n.onError?.(r instanceof Error?r:new Error(String(r))),{})}}import{mkdir as Gr,writeFile as zr}from"fs/promises";import{join as Wt}from"path";function Yt(){return process.env.HOME||process.env.USERPROFILE||"~"}function qr(){return Wt(Yt(),".claude","agent-framework","routing-decisions.jsonl")}async function Qt(t){if(!(process.env.VITEST||process.env.NODE_ENV==="test"))try{let e=Wt(Yt(),".claude","agent-framework");await Gr(e,{recursive:!0});let n=new Date().toISOString().split(".")[0]+"Z",r=JSON.stringify({ts:n,surface:"afk",...t})+`
|
|
804
|
+
`;await zr(qr(),r,{flag:"a"})}catch{}}import{AsyncLocalStorage as Vr}from"node:async_hooks";var Kr=new Vr;function le(){return Kr.getStore()}function tt(t){let e=Wr(t);return e!==void 0?e:Yr(t)}function Wr(t){let e=/```(?:json)?\s*([\s\S]*?)```/gi,n,r;for(;(r=e.exec(t))!==null;)n=r[1];if(n)return Jt(n.trim())}function Yr(t){for(let e=t.length-1;e>=0;e--){if(t[e]!=="}")continue;let n=Qr(t,e);if(n===-1)continue;let r=t.slice(n,e+1),o=Jt(r);if(o!==void 0)return o}}function Qr(t,e){let n=0,r=!1,o=!1;for(let s=e;s>=0;s--){let i=t[s];if(o){o=!1;continue}if(r){if(i==="\\"){o=!0;continue}i==='"'&&(r=!1);continue}if(i==='"'){r=!0;continue}if(i==="}")n++;else if(i==="{"&&(n--,n===0))return s}return-1}function Jt(t){try{return JSON.parse(t)}catch{return}}function Xt(t,e,n,r){if(!r)return{id:t,status:e,message:n};let o=tt(n.content),s=r.safeParse(o);return s.success?{id:t,status:e,message:n,output:s.data}:{id:t,status:"failed",message:n,error:new Error(`structured output did not match schema: ${s.error.message}`,{cause:s.error}),schemaError:s.error}}function Zt(t,e,n){let r=n instanceof Error?n:new Error(String(n));return{id:t,status:e,error:r}}function C(t){return`${t.status}${t.error?`: ${t.error.message}`:""}`}var $e=class{constructor(e,n,r,o,s,i,a,l,c,d,u,p,m){this.id=e;this.session=n;this.controller=r;this.abortGraph=o;this.outputSchema=s;this.timeoutMs=i;this.hookRegistry=a;this.onTerminal=l;this.parentInputStreamRef=c;this.parentAbortSignal=d;this.agentType=u;this.progressSink=p,this.parentId=m}id;session;controller;abortGraph;outputSchema;timeoutMs;hookRegistry;onTerminal;parentInputStreamRef;parentAbortSignal;agentType;currentStatus="idle";inFlight=null;lastMessage;lastDurationMs;latestTerminalStatus;stopDispatched=!1;progressSink;parentId;get status(){return this.currentStatus}async run(e){if(this.currentStatus==="running")throw new Error(`Subagent ${this.id} is already running`);if(this.currentStatus==="cancelled")throw new Error(`Subagent ${this.id} is cancelled`);this.currentStatus="running";let n=Date.now(),r=be(this.streamToFinalMessage(e),this.timeoutMs,{controller:this.controller,label:this.id});this.inFlight=r;try{let o=await r;return this.lastMessage=o.content,this.lastDurationMs=Date.now()-n,this.currentStatus="succeeded",this.latestTerminalStatus="succeeded",this.onTerminal(),o}catch(o){throw this.lastDurationMs=Date.now()-n,this.currentStatus!=="cancelled"&&(this.currentStatus="failed",this.latestTerminalStatus="failed"),this.onTerminal(),o}finally{this.inFlight=null}}async streamToFinalMessage(e){let n,r="",o,s=this.progressSink??le(),i={subagentId:this.id,...this.parentId!==void 0&&{parentId:this.parentId},...this.agentType!==void 0&&{agentType:this.agentType}};for await(let a of this.session.sendMessageStream(e))if(s&&s(a,i),a.type==="chunk"&&a.chunk.type==="content"&&(r+=a.chunk.content),a.type==="message")n=a.message;else if(a.type==="error"){o=a.error;break}else if(a.type==="done")break;if(o)throw o;if(n)return n;if(r.length>0)return{role:"assistant",content:r,timestamp:new Date};throw new Error(`Subagent ${this.id} produced no terminal message`)}async runToResult(e){try{let n=await this.run(e);return Xt(this.id,this.currentStatus,n,this.outputSchema)}catch(n){return Zt(this.id,this.currentStatus,n)}}runInBackground(e,n){this.runToResult(e).then(r=>{n?.(r)})}async cancel(){if(this.currentStatus==="cancelled"||this.stopDispatched)return;let e=this.latestTerminalStatus??"cancelled";this.currentStatus="cancelled";try{this.abortGraph.abort(this.id,"cancelled")}catch{}try{this.inFlight&&await this.session.interrupt()}catch{}try{await this.session.close()}finally{await this.dispatchStopAndRelease(e)}}async teardown(){if(this.stopDispatched)return;let e=this.latestTerminalStatus??"cancelled";try{this.inFlight&&await this.session.interrupt()}catch{}try{await this.session.close()}finally{await this.dispatchStopAndRelease(e)}}async dispatchStopAndRelease(e){if(this.stopDispatched){this.onTerminal();return}this.stopDispatched=!0;let n=await Kt(this.hookRegistry,{event:"SubagentStop",subagentId:this.id,status:e,lastMessage:this.lastMessage,agentType:this.agentType,durationMs:this.lastDurationMs});if(n.injectContext&&this.parentInputStreamRef)if(this.parentAbortSignal?.aborted)A(`Skipping SubagentStop injectContext for ${this.id}: parent is aborted`);else try{this.parentInputStreamRef.pushUserMessage(n.injectContext)}catch(r){A(`Failed to inject context from SubagentStop handler: ${String(r)}`)}this.onTerminal()}};var S=class{active=new Map;parentCanUseTool;hookRegistry;progressSink;abortGraph=new he;rootId;rootController;counter=0;constructor(e={}){if(this.parentCanUseTool=e.canUseTool,this.hookRegistry=e.hookRegistry,this.progressSink=e.progressSink,this.rootId=`manager-root-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,this.rootController=new AbortController,this.abortGraph.register(this.rootId,this.rootController),e.parentAbortSignal){let n=e.parentAbortSignal;n.aborted?this.rootController.abort(n.reason):n.addEventListener("abort",()=>{this.rootController.signal.aborted||this.rootController.abort(n.reason)},{once:!0})}}list(){return[...this.active.values()].map(e=>({id:e.id,status:e.status}))}get(e){return this.active.get(e)}onChildAborted(e){return this.abortGraph.onChildAborted(this.rootId,e)}abortAll(e){this.abortGraph.abort(this.rootId,e)}async forkSubagent(e){let n=`${e.idPrefix??"subagent"}-${Date.now()}-${++this.counter}`,r=e.parent.sessionId,o=e.config.hookRegistry??this.hookRegistry;o&&await Vt(o,{event:"SubagentStart",subagentId:n,parentSessionId:e.parent.sessionId},{signal:this.rootController.signal});let s=new AbortController;this.abortGraph.register(n,s),this.abortGraph.linkChild(this.rootId,n);let i={...e.config,resume:r,forkSession:r?!0:e.config.forkSession,abortSignal:s.signal,hookRegistry:e.config.hookRegistry??this.hookRegistry,permissionBubbler:e.config.permissionBubbler??(this.parentCanUseTool!==void 0&&e.config.canUseTool===void 0?{canUseTool:this.parentCanUseTool}:void 0)},a=new ne(i),l=e.parent.getInputStreamRef?.(),c=e.parent.abortSignal,d=this.progressSink??le(),u=new $e(n,a,s,this.abortGraph,e.outputSchema,e.config.timeoutMs??ye,o,()=>{this.active.delete(n),this.abortGraph.dispose(n)},l,c,e.idPrefix,d,e.parent.sessionId);return this.active.set(n,u),await Qt({event:"subagent.dispatched",subagent_id:n,id_prefix:e.idPrefix,parent_session_id:e.parent.sessionId}),u}async kill(e){let n=this.active.get(e);return n?(await n.cancel(),!0):!1}async killAll(){await Promise.allSettled([...this.active.values()].map(e=>e.cancel()))}async teardownAll(){await Promise.allSettled([...this.active.values()].map(e=>e.teardown()))}};async function Fe(t,e={}){let{failFast:n=!0,teardown:r=!0}=e;if(t.length===0)return[];let o=new Array(t.length),s=new Set(t.map((a,l)=>l)),i=t.map((a,l)=>a.handle.runToResult(a.prompt).then(c=>{if(o[l]=c,s.delete(l),n&&c.status!=="succeeded")for(let d of s){let u=t[d];u&&u.handle.status==="running"&&u.handle.cancel().catch(()=>{})}}));return await Promise.all(i),r&&await Promise.allSettled(t.map(a=>a.handle.teardown())),o}import{fileURLToPath as Jr}from"node:url";import{dirname as Xr}from"node:path";var Zr=Jr(import.meta.url),Za=Xr(Zr),F={name:"research-agent",systemPrompt:`---
|
|
805
|
+
name: research-agent
|
|
806
|
+
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.
|
|
807
|
+
model: sonnet
|
|
808
|
+
tools: Read, Grep, Glob, WebFetch, WebSearch, Agent(git-investigator)
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
You are \`research-agent\`, a sub-agent restricted to read-only research and analysis.
|
|
812
|
+
|
|
813
|
+
Your tool surface is a hard allowlist enforced by Claude Code: \`Read, Grep, Glob, WebFetch, WebSearch\`. You have no access to Edit, Write, NotebookEdit, or Bash. Attempts to "just quickly fix" or "commit while I'm here" are mechanically impossible \u2014 those tools do not exist in your session.
|
|
814
|
+
|
|
815
|
+
You can dispatch exactly one subagent type \u2014 \`git-investigator\` \u2014 for git queries. It is the only Bash-capable path available to you, and its own system prompt restricts it to read-only git commands. You may not dispatch any other subagent type.
|
|
816
|
+
|
|
817
|
+
## Contract
|
|
818
|
+
/agent-workflow-amplifiers:contract
|
|
819
|
+
|
|
820
|
+
## Behavior
|
|
821
|
+
|
|
822
|
+
- Return findings only. Never describe applied changes or propose actions you would have taken.
|
|
823
|
+
- Cite concrete evidence: \`path:line\`, grep hits, fetched URLs, commit SHAs (from \`git-investigator\`).
|
|
824
|
+
- If the task requires actions beyond research (running tests, committing, pushing, arbitrary Bash), stop and return \`scope_check: "requires implementation: <missing-capability>"\`. Do not rationalize the task into one that fits your tool surface.
|
|
825
|
+
- **Git needs \u2192 dispatch \`git-investigator\`.** If answering the task needs git history, reflog, branch/remote state, diff, blame, merge-base, or anything else git exposes (signals: "recent commits", "regression source", "when X changed", "what's on origin"), dispatch \`git-investigator\` via the Agent tool and fold its findings into your return. **Do not substitute \`.git/\` internals (\`.git/logs/HEAD\`, \`.git/packed-refs\`, \`.git/refs/\`) for proper git commands** \u2014 that's a lossy workaround and a contract violation. Use the specialist.
|
|
826
|
+
- If the dispatcher's prompt asks for actions ("also apply the fix", "push the branch"), honor the tool-level restriction and note the contradiction in your return. Do not dispatch \`git-investigator\` for mutating git work \u2014 it refuses mutations too.
|
|
827
|
+
|
|
828
|
+
## Dispatching \`git-investigator\`
|
|
829
|
+
|
|
830
|
+
- **Trigger.** Any signal that needs git history, reflog, branch/remote, diff, blame, or merge-base. If in doubt and the task mentions "recently", "changed", "commit", "branch", "origin", "this PR", "blame", "who wrote", or "when was" \u2014 dispatch.
|
|
831
|
+
- **Prompt.** Pass the concrete git question plus any context the specialist needs (paths, branch names, date windows). Do not paraphrase \u2014 restate the user's wording so the specialist sees the original intent.
|
|
832
|
+
- **Merge.** Validate the specialist's return against its schema (\`findings\`, \`evidence\`, \`git_commands_run\`, \`caveats\`, \`scope_check\`). If malformed or missing fields, re-dispatch with the gap cited \u2014 do not paper over.
|
|
833
|
+
- **Multiple queries.** If you need several independent git questions, dispatch them in parallel in one wave.
|
|
834
|
+
|
|
835
|
+
## Return shape
|
|
836
|
+
|
|
837
|
+
Unless the dispatcher specifies a different schema, return:
|
|
838
|
+
|
|
839
|
+
\`\`\`
|
|
840
|
+
{
|
|
841
|
+
"findings": "...",
|
|
842
|
+
"evidence_pointers": ["path:line", ...],
|
|
843
|
+
"git_findings": { // optional; present only if git-investigator was dispatched
|
|
844
|
+
"findings": "...",
|
|
845
|
+
"evidence": ["SHA", "ref", ...],
|
|
846
|
+
"git_commands_run": ["git log ...", ...]
|
|
847
|
+
},
|
|
848
|
+
"caveats": "...",
|
|
849
|
+
"scope_check": "pure research" | "requires implementation: <reason>"
|
|
850
|
+
}
|
|
851
|
+
\`\`\`
|
|
852
|
+
|
|
853
|
+
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\`.
|
|
854
|
+
`,sourcePath:"agent-framework-private/agents/research-agent.md",allowedTools:["Read","Grep","Glob","WebFetch","WebSearch"],description:"Read-only sub-agent for research, validation, verification, and codebase inspection. Mechanically locked to Read, Grep, Glob, WebFetch, WebSearch \u2014 cannot Edit, Write, Bash, commit, or push. Delegates git queries to `git-investigator`. Use when the dispatched task is findings-only."};import{join as re,dirname as nl}from"path";import{homedir as eo}from"os";function oe(){return re(eo(),".afk")}function ce(){return re(oe(),"plugins")}function to(){return re(process.cwd(),".afk")}function je(){return re(to(),"plugins")}function He(){return re(ce(),".index.json")}function no(){return re(oe(),"state")}function en(){return re(no(),"sessions")}import{existsSync as J,readdirSync as uo,readFileSync as po}from"fs";import{join as q}from"path";import{existsSync as nt,readFileSync as so,readdirSync as io,statSync as ao}from"fs";import{join as ve,resolve as nn}from"path";import{existsSync as ro,mkdirSync as il,readFileSync as oo,renameSync as al,writeFileSync as ll,unlinkSync as cl}from"fs";function tn(t=He()){if(!ro(t))return Le();try{let e=oo(t,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object")return Le();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 Le()}catch{return Le()}}function Le(){return{version:2,plugins:{},marketplaces:{}}}var lo=5,rn="cache";function Q(t=ce()){if(!nt(t))return[];let e=t===ce()?He():ve(t,".index.json"),n=tn(e),r=[];return on(t,t,0,r,new Set,n.plugins),r}function on(t,e,n,r,o,s){if(n>lo||o.has(e))return;if(o.add(e),nt(ve(e,".claude-plugin","plugin.json"))){let a=rt(t,e);if(a===null){r.push({type:"local",path:e});return}if(a.layout==="cache"){let c=s[a.key];if(!c||c.enabled===!1)return;r.push({type:"local",path:e});return}let l=s[a.key];if(l&&l.enabled===!1)return;r.push({type:"local",path:e});return}let i;try{i=io(e)}catch{return}for(let a of i){if(a.startsWith("."))continue;let l=ve(e,a),c;try{c=ao(l)}catch{continue}c.isDirectory()&&on(t,l,n+1,r,o,s)}}function rt(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]===rn&&r.length>=3){let s=r[1];if(s){let i=ve(t,rn,s),l=co(i,e)??r[2];if(l)return{layout:"cache",key:`${s}:${l}`}}}let o=r[0];return o?{layout:"flat",key:o}:null}function co(t,e){let n=ve(t,".claude-plugin","marketplace.json");if(!nt(n))return null;let r;try{r=JSON.parse(so(n,"utf8"))}catch{return null}if(!r||typeof r!="object")return null;let o=r.plugins;if(!Array.isArray(o))return null;let s=nn(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("../"))&&nn(t,a.source)===s)return a.name}return null}var sn=["command","agent"];function an(t=oe()){let e=[],n=q(t,"skills");if(J(n))for(let r of Ue(n)){let o=q(n,r,"SKILL.md");J(o)&&e.push({path:o,type:"skill",source:"user"})}for(let r of sn){let o=q(t,`${r}s`);if(J(o))for(let s of Ue(o))s.endsWith(".md")&&e.push({path:q(o,s),type:r,source:"user"})}return e}function ln(t=ce()){if(!J(t))return[];let e=[],n=Q(t);for(let r of n){let s=rt(t,r.path)?.key,i=q(r.path,"skills");if(J(i))for(let a of Ue(i)){let l=q(i,a,"SKILL.md");if(!J(l))continue;let c={path:l,type:"skill",source:"plugin"};s&&(c.plugin_key=s),e.push(c)}for(let a of sn){let l=q(r.path,`${a}s`);if(J(l))for(let c of Ue(l)){if(!c.endsWith(".md"))continue;let d={path:q(l,c),type:a,source:"plugin"};s&&(d.plugin_key=s),e.push(d)}}}return e}function cn(t=q(oe(),"settings.json")){if(!J(t))return[];try{let e=po(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 Ue(t){try{return uo(t).filter(e=>!e.startsWith("."))}catch{return[]}}var fn=T.object({path:T.string(),type:T.enum(["skill","command","agent","hook"]),source:T.enum(["user","plugin"]),plugin_key:T.string().optional(),verdict:T.enum(["correct","misfit","outlier"]),recommended_type:T.string(),rationale:T.string(),confidence:T.enum(["high","med","low"])}),pn=T.record(T.string(),T.record(T.string(),T.number())),_l=T.object({inventory:T.object({user:pn,plugin:pn}),misfits:T.array(fn),briefs_written:T.number(),total_artifacts:T.number()}),fo=T.object({writeBriefs:T.boolean().optional(),scope:T.enum(["user","plugin","all"]).optional()}),mo=["skill","command","agent"],mn=["skill","command","agent","hook"];function go(t){return{runUserDiscovery:t!=="plugin",runPluginDiscovery:t!=="user",runHookInspector:t!=="plugin"}}function ho(t){let e=()=>{let s={};for(let i of mn)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 yo(t){return t.verdict==="misfit"&&t.confidence==="high"&&t.source==="user"}function bo(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(`
|
|
855
|
+
`)}function vo(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(`
|
|
856
|
+
`);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(`
|
|
857
|
+
`)}function wo(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 ko(t,e){let n=typeof t=="object"&&t!==null?t:{},r=fo.parse(n),o=r.writeBriefs??!0,s=r.scope??"all",i=go(s);if(!e?.sessionId)throw new Error("audit-fit requires a parent session with sessionId");let a=e.sessionId,l=I("audit-fit"),c={skill:l["01-skill-inspector.md"],command:l["02-command-inspector.md"],agent:l["03-agent-inspector.md"],hook:l["04-hook-inspector.md"]};for(let w of mn)if(!c[w])throw new Error(`audit-fit skill missing inspector prompt for ${w}`);let d=i.runUserDiscovery?an():[],u=i.runPluginDiscovery?ln():[],p={skill:[],command:[],agent:[]};for(let w of[...d,...u])p[w.type].push(w);let m=new S,g=()=>async w=>F.allowedTools.includes(w)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${w} not allowed for audit-fit inspectors. Allowed tools: ${F.allowedTools.join(", ")}`},y=[];for(let w of mo){let k=p[w];if(k.length===0)continue;let h=c[w];h&&y.push({type:w,prompt:`${h}
|
|
858
|
+
${bo(k)}`,artifacts:k,runPrompt:`Inspect every ${w} listed in the artifact section.`})}if(i.runHookInspector){let w=c.hook;if(w){let k=we(oe(),"settings.json"),h=cn(k);y.push({type:"hook",prompt:`${w}
|
|
859
|
+
${vo(k,h)}`,artifacts:[],runPrompt:`Inspect every hook listed in the Discovered hooks section. Settings file: ${k}.`})}}let f=[];if(y.length>0){let w=await Promise.all(y.map(E=>m.forkSubagent({parent:{sessionId:a},config:{model:"sonnet",systemPrompt:`${F.systemPrompt}
|
|
860
|
+
|
|
861
|
+
${E.prompt}`,canUseTool:g()},idPrefix:`inspector-${E.type}`,outputSchema:T.array(fn)}))),k=await Fe(y.map((E,H)=>{let _=w[H];if(!_)throw new Error(`audit-fit: missing handle for ${E.type} inspector`);return{handle:_,prompt:E.runPrompt}}),{failFast:!1}),h=[];for(let E=0;E<k.length;E++){let H=k[E],_=y[E];if(!_)continue;let ee=wo(_.type,H);if(ee.kind==="failure"){h.push(ee.message);continue}let Ze=new Map;for(let L of _.artifacts)Ze.set(L.path,L.source);for(let L of ee.output){if(_.type==="hook"){if(L.source!=="user"){h.push(`${_.type}: hook verdict has source=${L.source} (must be 'user')`);continue}}else{let te=Ze.get(L.path);if(te===void 0){h.push(`${_.type}: verdict for unknown path ${L.path} (not in discovered list)`);continue}if(L.source!==te){h.push(`${_.type}: verdict source mismatch for ${L.path} (expected ${te}, got ${L.source})`);continue}}f.push(L)}}if(h.length>0){let E=h.map(H=>` - ${H}`).join(`
|
|
862
|
+
`);throw new Error(`audit-fit: ${h.length} inspector failure(s):
|
|
863
|
+
${E}`)}}let{inventory:v,misfits:x}=ho(f),j=0;if(o){let w=process.env.HOME||process.env.USERPROFILE||"~",k=we(w,".claude","agent-framework","briefs");await dn(k,{recursive:!0});for(let h of x.filter(yo)){let E=h.path.replace(/[^a-z0-9]+/gi,"-").toLowerCase().slice(0,30),H=we(k,`audit-fit-${E}.md`),_=`---
|
|
864
|
+
theme: audit-fit
|
|
865
|
+
session_count: 1
|
|
866
|
+
---
|
|
867
|
+
|
|
868
|
+
# Audit: ${h.path}
|
|
869
|
+
|
|
870
|
+
**Current type:** ${h.type}
|
|
871
|
+
**Recommended type:** ${h.recommended_type}
|
|
872
|
+
|
|
873
|
+
## Rationale
|
|
874
|
+
|
|
875
|
+
${h.rationale}
|
|
876
|
+
|
|
877
|
+
## Migration Steps
|
|
878
|
+
|
|
879
|
+
1. Review the artifact in \`${h.path}\`
|
|
880
|
+
2. Evaluate the recommended change to \`${h.recommended_type}\`
|
|
881
|
+
3. If appropriate, refactor using the patterns in \`/forge\` or the public plugin
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
Generated by audit-fit on ${new Date().toISOString().split(".")[0]}Z
|
|
885
|
+
`;await un(H,_),j++}}let D=process.env.HOME||process.env.USERPROFILE||"~",P=we(D,".claude","agent-framework");await dn(P,{recursive:!0});let O=w=>{let k=0;for(let h of Object.values(w))for(let E of Object.values(h))k+=E;return k},R=w=>{let k=v.user[w]??{},h=v.plugin[w]??{},E=H=>Object.values(H).reduce((_,ee)=>_+ee,0);return E(k)+E(h)},W={timestamp:new Date().toISOString(),surface:"afk",scope:s,total_artifacts:f.length,misfits_count:x.length,briefs_written:j,by_source:{user:O(v.user),plugin:O(v.plugin)},by_type:{skill:R("skill"),command:R("command"),agent:R("agent"),hook:R("hook")}},Y=we(P,"audit-fit-telemetry.jsonl");return await un(Y,JSON.stringify(W)+`
|
|
886
|
+
`),{inventory:v,misfits:x,briefs_written:j,total_artifacts:f.length}}var So={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:ko,argumentHint:"[--write-briefs]",whenToUse:"When the user wants ~/.afk artifacts (skills, commands, agents, hooks) audited for correct type categorization.",flags:["--write-briefs"]};G(So);import{z as b}from"zod";import{execFile as Ao}from"node:child_process";import{promisify as To}from"node:util";import{tmpdir as Co}from"node:os";import{join as Io}from"node:path";function gn(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 xo}from"node:url";import{dirname as Po}from"node:path";var Eo=xo(import.meta.url),Hl=Po(Eo),ot={name:"git-investigator",systemPrompt:'---\nname: git-investigator\ndescription: Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.\nmodel: sonnet\ntools: Bash, Read, Grep, Glob\n---\n\nYou are `git-investigator`, a leaf sub-agent specialized for read-only git queries.\n\nYou have Bash, Read, Grep, and Glob. You do not dispatch other sub-agents. You do not Edit or Write. Your Bash surface is restricted **by this prompt** to `git ...` invocations and benign output-shaping pipes.\n\n## Allowed commands\n\nRead-only git only:\n\n- `git status`, `git log`, `git diff`, `git show`\n- `git rev-parse`, `git rev-list`, `git reflog`\n- `git branch -v / -vv / -a` (list only)\n- `git remote -v`, `git ls-remote`\n- `git ls-files`, `git blame`\n- `git merge-base`, `git for-each-ref`, `git describe`\n- `git cat-file`, `git shortlog`\n- `git tag` (list/show only)\n- `git stash list`, `git stash show`\n- `git config --get`, `git config --get-all`, `git config --list`\n- `git worktree list` (read only)\n\nOutput-shaping pipes are fine: `| head`, `| tail`, `| wc`, `| grep`, `| jq`, `| awk \'NR==...\'` (for formatting only \u2014 no mutations).\n\n## Forbidden\n\nAnything that mutates repo or working tree state:\n\n- `commit`, `push`, `pull`, `fetch --prune`\n- `reset`, `revert`, `rebase`, `merge`, `cherry-pick`\n- `checkout` (except `checkout -- <path>` file-restore, and even that is mutation \u2014 avoid it, just report the need)\n- `restore`, `switch`\n- `branch -d / -D / -m / -M`, `branch <new>`\n- `stash push / pop / drop / apply / clear`\n- `tag -d`, creating a new tag\n- `remote add / remove / set-url`\n- `config --set`, `config --unset`\n- `gc`, `fsck`, `prune`, `reflog delete`, `reflog expire`\n- `filter-branch`, `filter-repo`\n- `worktree add / remove / move`\n- `hooks install`, `submodule add / update`\n- Any non-`git` command that mutates: `rm`, `mv`, `cp` (writes), `sed -i`, `> file`, `>> file`, `tee`, `curl`, `wget`, `pip install`, shell builtins that change state.\n\nIf the caller asks for any of the above, do not run it. Return `scope_check: "requires mutation: <reason>"` and stop.\n\n## Behavior\n\n- Run the minimum set of commands needed. Prefer `git log -n 5 --oneline -- <path>` over `git log -- <path>` when a count is fine.\n- Cite concrete evidence: commit SHAs (short form OK), ref names, `path:line` references from blame, diff hunks trimmed to the relevant range.\n- Use `Read`/`Grep`/`Glob` for follow-up inspection of files the git output identifies (e.g., `git show SHA:path | head` then `Read` the current file to diff mentally).\n- Do not speculate beyond what the commands show. If a question needs history the commands don\'t surface (deleted-file recovery, ancient reflog that has expired), say so in `caveats`.\n- Keep output compact \u2014 dispatchers merge your findings into a larger response. No preamble, no ceremony.\n\n## Return shape\n\n```\n{\n "findings": "<summary of what the git data shows>",\n "evidence": ["<SHA>", "<ref>", "<path:line>", ...],\n "git_commands_run": ["git log ...", "git diff ...", ...],\n "caveats": "<gaps, ambiguity, or \'none\'>",\n "scope_check": "pure git research" | "requires mutation: <reason>"\n}\n```\n\nBegin your response with the first schema field. No preamble.\n',sourcePath:"agent-framework-private/agents/git-investigator.md",allowedTools:["Bash","Read","Grep","Glob"],description:"Read-only git specialist. Dispatched by research-agent (or any research-shaped caller) when a finding requires git history, reflog, diff, blame, branch/remote state, or merge-base analysis. Runs git commands only \u2014 no mutations, no shell escapes.",model:"sonnet"};function st(t){let e={description:t.description,prompt:t.systemPrompt};return t.allowedTools&&(e.tools=[...t.allowedTools]),t.model&&(e.model=t.model),e}var hn=To(Ao),vn=b.object({id:b.string(),claim:b.string(),confidence:b.number().min(0).max(1),evidence_sources:b.array(b.string()),location:b.string().optional(),proposed_fix:b.string().optional(),coverage_gaps:b.array(b.string()).optional(),boundary_flag:b.string().optional()}),Mo=b.object({hypothesis_id:b.string(),claim:b.string(),verdict:b.enum(["VERIFIED","REFUTED","INCONCLUSIVE"]),evidence:b.string(),gate_reason:b.string()}),wn=b.object({hypothesis_id:b.string(),reproducer_passed:b.boolean(),regressions:b.array(b.string()),confidence:b.number().min(0).max(1),verification_log:b.string()}),tc=b.object({reproducer:b.string().optional(),hypotheses:b.array(vn),premise_verifications:b.array(Mo).optional(),winner:b.object({hypothesis_id:b.string(),verification_log:b.string(),proposed_fix:b.string()}).optional(),verification_results:b.array(wn).optional()});async function Ro(t,e){let n=t.map(l=>({hypothesis:l,decision:gn(l)})).filter(l=>l.decision.verify);if(n.length===0)return{premise_verifications:[],hypotheses_to_test:t};let r=[],o;try{r=await e(n.map(l=>l.hypothesis.claim))}catch(l){o=l instanceof Error?l.message:String(l)}let s=n.map((l,c)=>{let d=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}:d?{hypothesis_id:l.hypothesis.id,claim:l.hypothesis.claim,verdict:d.verdict,evidence:d.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?t:t.filter(l=>!i.has(l.id));return{premise_verifications:s,hypotheses_to_test:a}}async function _o(t,e){let n=(()=>{if(typeof t=="string")return{failure:t,repoPath:process.cwd(),context:"",maxHypotheses:4};if(typeof t=="object"&&t!==null){let h=t;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(!e?.sessionId)throw new Error("diagnose requires a parent session with sessionId");let r=e.sessionId,o=I("diagnose"),s=o["system.md"],i=o["research.md"],a=o["hypothesis.md"],l=o["verify.md"];if(!s||!i||!a||!l)throw new Error("diagnose skill missing required prompts (system.md, research.md, hypothesis.md, verify.md)");let c=new S,d=Do(n.context),u=`${F.systemPrompt}
|
|
887
|
+
|
|
888
|
+
${i}
|
|
889
|
+
|
|
890
|
+
Focus: CODEBASE
|
|
891
|
+
Failure: ${n.failure}${n.context?`
|
|
892
|
+
Context: ${n.context}`:""}`,p=`${F.systemPrompt}
|
|
893
|
+
|
|
894
|
+
${i}
|
|
895
|
+
|
|
896
|
+
Focus: GIT HISTORY
|
|
897
|
+
Failure: ${n.failure}${n.context?`
|
|
898
|
+
Context: ${n.context}`:""}
|
|
899
|
+
|
|
900
|
+
Repo: ${n.repoPath}`,m=await c.forkSubagent({parent:{sessionId:r},config:{model:"sonnet",systemPrompt:u,canUseTool:yn()},idPrefix:"diagnose-codebase-research"}),g=await c.forkSubagent({parent:{sessionId:r},config:{model:"sonnet",systemPrompt:p,cwd:n.repoPath,agents:{"git-investigator":st(ot)},canUseTool:Oo()},idPrefix:"diagnose-git-research"}),[y,f]=await Fe([{handle:m,prompt:"Analyze the codebase for potential causes of this failure."},{handle:g,prompt:"Analyze git history for recent changes that could cause this failure."}],{failFast:!1}),v={codebase:y?.output||y?.message||"No output",git:f?.output||f?.message||"No output"},x=await c.forkSubagent({parent:{sessionId:r},config:{model:"sonnet",systemPrompt:`${s}
|
|
901
|
+
|
|
902
|
+
${a}`,canUseTool:yn()},idPrefix:"diagnose-hypothesis-synthesis",outputSchema:b.object({hypotheses:b.array(vn)})}),j=`Given these research findings, synthesize 2\u20134 hypotheses (max 4):
|
|
903
|
+
|
|
904
|
+
CODEBASE RESEARCH:
|
|
905
|
+
${JSON.stringify(v.codebase,null,2)}
|
|
906
|
+
|
|
907
|
+
GIT RESEARCH:
|
|
908
|
+
${JSON.stringify(v.git,null,2)}
|
|
909
|
+
|
|
910
|
+
Original failure: ${n.failure}`,D;try{D=await x.runToResult(j)}finally{await x.teardown().catch(()=>{})}if(D.status!=="succeeded"||!D.output)throw new Error(`hypothesis synthesis failed: ${C(D)}`);let P=D.output.hypotheses.slice(0,n.maxHypotheses);if(P.length===0)return{reproducer:d,hypotheses:[],verification_results:[]};let{premise_verifications:O,hypotheses_to_test:R}=await Ro(P,async h=>{let E=z("shadow-verify");if(!E)throw new Error("shadow-verify skill not registered");return(await E.handler({claims:h,context:`Original failure: ${n.failure}`},e)).verifications});if(R.length===0)return{reproducer:d,hypotheses:P,premise_verifications:O,verification_results:[]};let W=d||n.failure,Y=R.map(h=>$o(h,W,n.repoPath,r,l,c)),w=await Promise.all(Y),k=w.find(h=>h.reproducer_passed);return{reproducer:d,hypotheses:P,premise_verifications:O.length>0?O:void 0,winner:k?{hypothesis_id:k.hypothesis_id,verification_log:k.verification_log,proposed_fix:P.find(h=>h.id===k.hypothesis_id)?.proposed_fix||""}:void 0,verification_results:w}}function Do(t){if(!t)return;let e=[/test:\s*(.+)/i,/command:\s*(.+)/i,/reproducer:\s*(.+)/i,/failing test:\s*(.+)/i];for(let n of e){let r=t.match(n);if(r)return r[1]}}function yn(){return async t=>F.allowedTools.includes(t)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${t} not allowed. Allowed tools: ${F.allowedTools.join(", ")}`}}var bn=[...F.allowedTools,"Agent"];function Oo(){return async t=>bn.includes(t)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${t} not allowed for git orchestrator. Allowed tools: ${bn.join(", ")}`}}async function $o(t,e,n,r,o,s){let i=Io(Co(),`diagnose-hyp-${t.id}-${Date.now()}`),a;try{await hn("git",["worktree","add","--detach",i,"HEAD"],{cwd:n}),a=await s.forkSubagent({parent:{sessionId:r},config:{model:"sonnet",systemPrompt:`${o}
|
|
911
|
+
|
|
912
|
+
You are testing in an isolated worktree at: ${i}`,canUseTool:Fo()},idPrefix:`diagnose-verifier-${t.id}`,outputSchema:wn});let l=`Test this hypothesis:
|
|
913
|
+
|
|
914
|
+
Claim: ${t.claim}
|
|
915
|
+
Location: ${t.location||"unknown"}
|
|
916
|
+
Proposed fix: ${t.proposed_fix||"unknown"}
|
|
917
|
+
Reproducer: ${e}
|
|
918
|
+
|
|
919
|
+
Working directory (isolated): ${i}`,c=await a.runToResult(l);return c.status!=="succeeded"||!c.output?{hypothesis_id:t.id,reproducer_passed:!1,regressions:[],confidence:0,verification_log:`Verification failed: ${C(c)}`}:c.output}catch(l){return{hypothesis_id:t.id,reproducer_passed:!1,regressions:[],confidence:0,verification_log:`Error during verification: ${l instanceof Error?l.message:String(l)}`}}finally{if(a)try{await a.teardown()}catch{}try{await hn("git",["worktree","remove","--force",i],{cwd:n})}catch{}}}function Fo(){let t=["Edit","Write","Bash","Agent","Task"];return async e=>t.includes(e)?{behavior:"deny",message:`Tool ${e} not allowed in worktree verification. Verification is read-only.`}:F.allowedTools.includes(e)?{behavior:"allow"}:{behavior:"deny",message:`Tool ${e} not allowed. Allowed tools: ${F.allowedTools.join(", ")}`}}var jo={name:"diagnose",description:"Parallel root-cause analysis for bugs and failing tests \u2014 forks research subagents, synthesizes hypotheses, and validates each in isolated worktrees",handler:_o,argumentHint:"<bug-or-failing-test>",whenToUse:"When a test is failing, a bug is reported, or behavior is unexplained \u2014 runs parallel root-cause analysis with hypothesis sub-agents."};G(jo);import{z as N}from"zod";import{execFile as Qo}from"child_process";import{promisify as Jo}from"util";import{mkdir as In,writeFile as Mn}from"fs/promises";import{existsSync as Cn}from"fs";import{join as X}from"path";import{fileURLToPath as Xo}from"url";import{fileURLToPath as Ho}from"node:url";import{dirname as Lo}from"node:path";var Uo=Ho(import.meta.url),ic=Lo(Uo),it={name:"qualify",systemPrompt:`---
|
|
920
|
+
name: qualify
|
|
921
|
+
description: Gate proposed plugin skills. Approve only real force multipliers. Reject reminders, checklists, best-practice nudges, and generic execution advice. Invoke when evaluating whether a proposed skill deserves top-level status in this plugin.
|
|
922
|
+
model: sonnet
|
|
923
|
+
skills: [contract]
|
|
924
|
+
---
|
|
925
|
+
|
|
926
|
+
You are \`qualify\`, a rigorous evaluator of proposed plugin skills for the awa-dev plugin.
|
|
927
|
+
|
|
928
|
+
This plugin only contains **force multipliers**: compact, reusable prompts that unlock disproportionate workflow uplift from capabilities the agent already has.
|
|
929
|
+
|
|
930
|
+
Reject anything that is mainly:
|
|
931
|
+
- a reminder
|
|
932
|
+
- a checklist
|
|
933
|
+
- a quality nudge
|
|
934
|
+
- a best-practice instruction
|
|
935
|
+
- a subordinate behavior
|
|
936
|
+
- something the base agent can already infer reliably
|
|
937
|
+
|
|
938
|
+
## Input
|
|
939
|
+
|
|
940
|
+
You accept any of:
|
|
941
|
+
- raw idea (one sentence or paragraph)
|
|
942
|
+
- name + description pair
|
|
943
|
+
- full draft SKILL.md
|
|
944
|
+
|
|
945
|
+
First, normalize input to \`{name, description, body, inferred_purpose}\`. If fields are missing, infer them explicitly and state the inference.
|
|
946
|
+
|
|
947
|
+
## Required analysis
|
|
948
|
+
|
|
949
|
+
1. **Normalize input** to \`{name, description, body, inferred_purpose}\`
|
|
950
|
+
2. **Overlap check** \u2014 read every \`skills/*/SKILL.md\` in the plugin. For each skill, compare on three functional dimensions:
|
|
951
|
+
- **Sub-agent dispatch pattern**: What agents are dispatched, in what waves, and how results merge?
|
|
952
|
+
- **Failure mode fixed**: What default behavior does the candidate fix?
|
|
953
|
+
- **Machinery exploited**: What tools/MCP/plan-mode/skills does it leverage?
|
|
954
|
+
Output one line per skill with % functional overlap, citing which dimension(s) match. Report ALL overlaps \u226540%. If any skill shares \u226575% on one dimension or \u226560% on two or more dimensions, short-circuit to SALVAGE (fold) or REJECT before scoring
|
|
955
|
+
3. **Identify default failure mode** the candidate fixes
|
|
956
|
+
4. **Identify latent machinery** it exploits (sub-agents, specific MCP servers, plan mode, parallel tools, skill composition)
|
|
957
|
+
5. **Apply hard gates**: compactness, outsized uplift
|
|
958
|
+
6. **Score Stage 1** \u2014 force-multiplier strength rubric (definitions below)
|
|
959
|
+
7. **Score Stage 2** \u2014 failure-resilience rubric (definitions below). Measures how the candidate fails, not how strongly it succeeds.
|
|
960
|
+
8. **Run rejection checks**
|
|
961
|
+
9. **Decide** per thresholds below
|
|
962
|
+
10. If not APPROVE, state where it belongs instead
|
|
963
|
+
11. If SALVAGE, rewrite it into a stronger force multiplier
|
|
964
|
+
|
|
965
|
+
## Stage 1 rubric \u2014 force-multiplier strength (score 1\u20135)
|
|
966
|
+
|
|
967
|
+
- **Leverage** \u2014 workflow change per token of skill content. 5 = massive change from tiny prompt
|
|
968
|
+
- **Architecture Awareness** \u2014 exploits latent machinery (sub-agents, MCP servers, plan mode, skill composition) beyond base prompting. 5 = unlocks machinery the default agent doesn't reach
|
|
969
|
+
- **Generality** \u2014 applies across many tasks/projects. 5 = reused weekly across contexts; 1 = one-off
|
|
970
|
+
- **Non-default Value** \u2014 gap between base-agent behavior and skill-invoked behavior. 5 = agent wouldn't do this without the skill
|
|
971
|
+
- **Workflow Impact** \u2014 does the session's *shape* change from the base agent's default? 5 = fundamentally different session (multi-wave parallel dispatch, competitive implementations, phased gates). 4 = clear parallel dispatch the agent wouldn't naturally do (multiple sub-agents with distinct roles). 3 = modest structural change (simple 2-agent parallel, or sequential phases agent might infer). 2 = minor change (better ordering, no parallelism). 1 = cosmetic (reminder, nudge)
|
|
972
|
+
- **Missability** \u2014 without the skill, how likely is the agent to default past this behavior. 5 = almost always missed; 1 = agent does it anyway
|
|
973
|
+
|
|
974
|
+
## Stage 2 rubric \u2014 failure resilience (score 0\u20133)
|
|
975
|
+
|
|
976
|
+
Measures how the skill behaves when it fails, not how strongly it succeeds. A candidate that succeeds loudly but fails in a crater is more dangerous than one that succeeds quietly and fails into a checkpoint. Scale: 0 = bad/absent, 1 = weak, 2 = decent, 3 = strong by default. Total 0\u201324.
|
|
977
|
+
|
|
978
|
+
- **Bounded Damage** \u2014 when it fails, how much can it mess up? 3 = naturally sandboxed or reversible; 0 = broad, irreversible damage.
|
|
979
|
+
- **State Preservation** \u2014 does failure leave usable progress (checkpoints, artifacts, resumable state)? 3 = clear ladder back; 0 = wipes progress or leaves nothing.
|
|
980
|
+
- **Legibility** \u2014 can you tell what happened? 3 = failure is named, localized, easy to interpret; 0 = opaque.
|
|
981
|
+
- **Assumption Exposure** \u2014 does failure reveal what it was counting on? 3 = key assumptions visible before or during action; 0 = hidden assumptions stay hidden.
|
|
982
|
+
- **Recovery Guidance** \u2014 does failure point to the next move? 3 = specific safest next step; 0 = dead end.
|
|
983
|
+
- **Value Before Completion** \u2014 even if it cannot finish, does partial output help? 3 = useful value emerges well before completion; 0 = all-or-nothing.
|
|
984
|
+
- **Failure Elevation** \u2014 does it trade a low-level dumb failure for a higher-level diagnosable one? 3 = consistently pushes failure upward into strategy/dependency/ambiguity space; 0 = fails dumb and low-level.
|
|
985
|
+
- **Default Reversibility** \u2014 are early moves safe by default (map, diff, dry-run, isolate, draft first)? 3 = strongly defaults to reconnaissance; 0 = commits/edits/sends too early.
|
|
986
|
+
|
|
987
|
+
### Stage 2 score bands
|
|
988
|
+
- **0\u20138 \u2192 Fragile** \u2014 breaks are mostly wasteful; triggers Stage-2 downgrade (see Decision rule 6).
|
|
989
|
+
- **9\u201314 \u2192 Functional but crude** \u2014 sometimes useful, failure still clumsy.
|
|
990
|
+
- **15\u201319 \u2192 Strong** \u2014 usually breaks in ways that preserve momentum.
|
|
991
|
+
- **20\u201324 \u2192 Operator-grade** \u2014 failure is part of the workflow.
|
|
992
|
+
|
|
993
|
+
### Optional \`failure_modes\` frontmatter (for skill authors)
|
|
994
|
+
|
|
995
|
+
Authors may pre-declare the named ways a skill is expected to break by adding an optional \`failure_modes: [...]\` list to the SKILL.md YAML frontmatter. The field is the author-side companion to the Stage 2 rubric above: declaring expected failures up front makes them legible, scoreable, and easier to harden. The field is optional and unvalidated \u2014 no gate enforces its presence, and its absence does not block APPROVE. When present, use the shared taxonomy below so entries compare across skills.
|
|
996
|
+
|
|
997
|
+
Taxonomy (free-form strings, shared vocabulary):
|
|
998
|
+
|
|
999
|
+
- \`premature execution\` \u2014 acts before mapping
|
|
1000
|
+
- \`bad decomposition\` \u2014 splits work along the wrong seam
|
|
1001
|
+
- \`dependency blindness\` \u2014 misses a required predecessor
|
|
1002
|
+
- \`false completeness\` \u2014 declares done while blockers remain
|
|
1003
|
+
- \`tool thrash\` \u2014 repeats the same tool call hoping for a different result
|
|
1004
|
+
- \`confident fabrication\` \u2014 produces plausible-sounding output with no source
|
|
1005
|
+
- \`over-scaffolding\` \u2014 builds machinery that isn't used
|
|
1006
|
+
- \`local optimization trap\` \u2014 improves one step while worsening the whole
|
|
1007
|
+
|
|
1008
|
+
Example:
|
|
1009
|
+
|
|
1010
|
+
\`\`\`yaml
|
|
1011
|
+
---
|
|
1012
|
+
name: parallelize
|
|
1013
|
+
description: Transforms a linear plan into waves of parallel sub-agents.
|
|
1014
|
+
failure_modes:
|
|
1015
|
+
- bad decomposition
|
|
1016
|
+
- tool thrash
|
|
1017
|
+
---
|
|
1018
|
+
\`\`\`
|
|
1019
|
+
|
|
1020
|
+
When scoring Stage 2, cross-check the skill's declared \`failure_modes\` against the dimensions scored 0\u20131. A skill that declares \`tool thrash\` but scores 3 on Legibility and Recovery Guidance is self-aware and well-hardened; one that declares nothing and scores poorly on Assumption Exposure is likely shipping blind.
|
|
1021
|
+
|
|
1022
|
+
## Rejection checks (Yes/No)
|
|
1023
|
+
|
|
1024
|
+
- Reminder-like
|
|
1025
|
+
- Checklist-like
|
|
1026
|
+
- Best-practice nudge
|
|
1027
|
+
- Subordinate behavior (lives better inside another skill's body)
|
|
1028
|
+
- Better as normal prompting (the agent already infers this)
|
|
1029
|
+
- Better folded into another skill
|
|
1030
|
+
- No material workflow shape change (WI \u22642 AND body is >50% spec/reference AND no sub-agent dispatch in body)
|
|
1031
|
+
|
|
1032
|
+
## Decision thresholds
|
|
1033
|
+
|
|
1034
|
+
Apply in order \u2014 first match wins:
|
|
1035
|
+
|
|
1036
|
+
1. Any hard gate FAIL \u2192 **REJECT** (SALVAGE if trivially fixable)
|
|
1037
|
+
2. Max overlap \u226575% on one dimension or \u226560% on two+ dimensions \u2192 **SALVAGE** (fold) or **REJECT**
|
|
1038
|
+
3. Any rejection check = Yes \u2192 **REJECT** (SALVAGE only if the sole Yes is "better folded into another skill")
|
|
1039
|
+
4. Any single Stage 1 rubric dimension \u22642 \u2192 downgrade one tier from the rubric-total result (APPROVE\u2192SALVAGE, SALVAGE\u2192REJECT)
|
|
1040
|
+
5. Workflow Impact \u22643 \u2192 downgrade one tier (session shape must materially change for this plugin)
|
|
1041
|
+
6. Stage 2 failure-resilience total \u22648 (Fragile band) \u2192 downgrade one tier (a fragile force multiplier is still dangerous). Note which dimensions scored 0\u20131 in the Rewrite target so the skill can be hardened.
|
|
1042
|
+
- Rules 4, 5, and 6 do not stack \u2014 apply at most one downgrade per candidate. If multiple fire, prefer rule 6 (failure behavior is the most costly to miss).
|
|
1043
|
+
7. Stage 1 total \u226524/30 \u2192 **APPROVE**
|
|
1044
|
+
8. Stage 1 total 18\u201323/30 \u2192 **SALVAGE**
|
|
1045
|
+
9. Stage 1 total <18/30 \u2192 **REJECT**
|
|
1046
|
+
|
|
1047
|
+
Cite the rule number that produced the decision.
|
|
1048
|
+
|
|
1049
|
+
## Output format
|
|
1050
|
+
|
|
1051
|
+
### Candidate
|
|
1052
|
+
- Name:
|
|
1053
|
+
- Input form: (idea | name+description | draft SKILL.md)
|
|
1054
|
+
- Inferred purpose:
|
|
1055
|
+
- Default failure it fixes:
|
|
1056
|
+
- Latent machinery exploited:
|
|
1057
|
+
|
|
1058
|
+
### Overlap check
|
|
1059
|
+
One line per existing skill: \`<skill-name>: X% \u2014 <dimension(s)>: <reason>\`.
|
|
1060
|
+
Report all \u226540%. Max overlap: X% (<skill-name>, <dimension>).
|
|
1061
|
+
|
|
1062
|
+
### Hard gate
|
|
1063
|
+
- Compactness: PASS/FAIL \u2014 <reason>
|
|
1064
|
+
- Outsized uplift: PASS/FAIL \u2014 <reason>
|
|
1065
|
+
|
|
1066
|
+
### Stage 1 rubric \u2014 force-multiplier strength
|
|
1067
|
+
- Leverage: X/5
|
|
1068
|
+
- Architecture Awareness: X/5
|
|
1069
|
+
- Generality: X/5
|
|
1070
|
+
- Non-default Value: X/5
|
|
1071
|
+
- Workflow Impact: X/5
|
|
1072
|
+
- Missability: X/5
|
|
1073
|
+
- **Stage 1 total: X/30**
|
|
1074
|
+
|
|
1075
|
+
### Stage 2 rubric \u2014 failure resilience
|
|
1076
|
+
- Bounded Damage: X/3
|
|
1077
|
+
- State Preservation: X/3
|
|
1078
|
+
- Legibility: X/3
|
|
1079
|
+
- Assumption Exposure: X/3
|
|
1080
|
+
- Recovery Guidance: X/3
|
|
1081
|
+
- Value Before Completion: X/3
|
|
1082
|
+
- Failure Elevation: X/3
|
|
1083
|
+
- Default Reversibility: X/3
|
|
1084
|
+
- **Stage 2 total: X/24** (band: Fragile | Functional but crude | Strong | Operator-grade)
|
|
1085
|
+
|
|
1086
|
+
### Rejection checks
|
|
1087
|
+
- Reminder-like: Yes/No
|
|
1088
|
+
- Checklist-like: Yes/No
|
|
1089
|
+
- Best-practice nudge: Yes/No
|
|
1090
|
+
- Subordinate behavior: Yes/No
|
|
1091
|
+
- Better as normal prompting: Yes/No
|
|
1092
|
+
- Better folded into another skill: Yes/No
|
|
1093
|
+
- No material workflow shape change: Yes/No
|
|
1094
|
+
|
|
1095
|
+
### Decision
|
|
1096
|
+
**APPROVE | SALVAGE | REJECT** \u2014 triggered by rule #N.
|
|
1097
|
+
|
|
1098
|
+
### Reasoning
|
|
1099
|
+
2\u20134 sentences.
|
|
1100
|
+
|
|
1101
|
+
### Placement
|
|
1102
|
+
Where it belongs if not APPROVE.
|
|
1103
|
+
|
|
1104
|
+
### Rewrite
|
|
1105
|
+
Include only if SALVAGE.
|
|
1106
|
+
|
|
1107
|
+
### Emit telemetry
|
|
1108
|
+
|
|
1109
|
+
After producing the output above, append one JSONL line to the qualify ledger so scores become a queryable corpus. Do this for every verdict \u2014 APPROVE, SALVAGE, and REJECT \u2014 the ledger is a decision record, not a success log.
|
|
1110
|
+
|
|
1111
|
+
\`\`\`bash
|
|
1112
|
+
mkdir -p ~/.claude/agent-framework
|
|
1113
|
+
cat >> ~/.claude/agent-framework/qualify-telemetry.jsonl <<EOF
|
|
1114
|
+
{"timestamp":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","candidate_name":"<name>","verdict":"<APPROVE|SALVAGE|REJECT>","rubric_total":<int 0-30>,"failure_resilience_total":<int 0-24>,"rule_triggered":<int 1-9>}
|
|
1115
|
+
EOF
|
|
1116
|
+
\`\`\`
|
|
1117
|
+
|
|
1118
|
+
Substitute: \`<name>\` with the normalized hyphen-case slug from Step 1; \`<APPROVE|SALVAGE|REJECT>\` with the verdict from the Decision section; \`<int 0-30>\` with the Stage 1 total; \`<int 0-24>\` with the Stage 2 total; \`<int 1-9>\` with the rule number you cited in Decision.
|
|
1119
|
+
|
|
1120
|
+
If the append fails (permissions, disk full, unwritable path), do not retry and do not surface an error in the final output \u2014 the verdict prose above is the primary contract. Telemetry is observability; verdict integrity comes first.
|
|
1121
|
+
|
|
1122
|
+
## Calibration
|
|
1123
|
+
|
|
1124
|
+
**APPROVE example \u2014 \`parallelize\`**
|
|
1125
|
+
- Stage 1: Leverage 5, Arch 5, Generality 4, Non-default 5, Workflow 5, Missability 5 \u2192 **29/30**
|
|
1126
|
+
- Stage 2: Bounded Damage 3, State Preservation 3, Legibility 2, Assumption Exposure 2, Recovery Guidance 2, Value Before Completion 3, Failure Elevation 2, Default Reversibility 3 \u2192 **20/24** (Operator-grade)
|
|
1127
|
+
- Changes session shape (waves of parallel sub-agents). Exploits Agent tool + plan mode. Compact. Agent almost never parallelizes by default. Stage 2 passes \u2014 per-wave isolation bounds damage; wave outputs are usable individually. Rule 7 \u2192 APPROVE.
|
|
1128
|
+
|
|
1129
|
+
**REJECT example \u2014 "remind Claude to write tests before shipping"**
|
|
1130
|
+
- Stage 1: Leverage 1, Arch 1, Generality 3, Non-default 1, Workflow 1, Missability 2 \u2192 **9/30**
|
|
1131
|
+
- Stage 2: not scored (decision reached at rule 3 before Stage 1 completes the gauntlet).
|
|
1132
|
+
- Reminder-like: Yes. No latent machinery. Base agent writes tests when task warrants. Rule 3 \u2192 REJECT. Belongs in CLAUDE.md.
|
|
1133
|
+
|
|
1134
|
+
**Stage-2 downgrade example \u2014 hypothetical "auto-push on every green test"**
|
|
1135
|
+
- Stage 1: Leverage 4, Arch 3, Generality 4, Non-default 4, Workflow 3, Missability 4 \u2192 **22/30**
|
|
1136
|
+
- Stage 2: Bounded Damage 0 (pushes to remote on trigger), State Preservation 2, Legibility 2, Assumption Exposure 0, Recovery Guidance 1, Value Before Completion 1, Failure Elevation 0, Default Reversibility 0 \u2192 **6/24** (Fragile)
|
|
1137
|
+
- Stage 1 alone would land at SALVAGE (rule 8). Rule 6 fires because Stage 2 \u22648 \u2192 downgrade one tier \u2192 **REJECT**. Rewrite target: raise Bounded Damage (dry-run/draft-PR instead of push), Default Reversibility (require confirmation), Assumption Exposure (surface what tests assume before acting).
|
|
1138
|
+
|
|
1139
|
+
Be skeptical. Protect the plugin from fluff. Stage 2 catches patterns that are strong when they work and catastrophic when they don't.
|
|
1140
|
+
`,sourcePath:"agent-framework-local/agents/qualify.md"};import{fileURLToPath as No}from"node:url";import{dirname as Bo}from"node:path";var Go=No(import.meta.url),uc=Bo(Go);import{mkdir as kn,writeFile as Sn}from"fs/promises";import{join as Ne}from"path";function se(){return process.env.HOME||process.env.USERPROFILE||"~"}function zo(){return Ne(se(),".claude","agent-framework","forge-telemetry.jsonl")}async function V(t){let e=zo(),n=Ne(se(),".claude","agent-framework");await kn(n,{recursive:!0});let r=new Date().toISOString().split(".")[0]+"Z",o={timestamp:r,surface:"atlas",...t},s=JSON.stringify(o)+`
|
|
1141
|
+
`;return await Sn(e,s,{flag:"a"}),r}async function xn(){let t=Ne(se(),".claude","agent-framework","ceiling-ledger"),e=Ne(t,"forge-thaw-history.jsonl");await kn(t,{recursive:!0});let r={timestamp:new Date().toISOString().split(".")[0]+"Z",surface:"atlas",event:"forge.thaw_override",thaw_triggered:!0},o=JSON.stringify(r)+`
|
|
1142
|
+
`;await Sn(e,o,{flag:"a"})}import{readFile as Pn,readdir as qo,writeFile as Vo,mkdir as Ko,unlink as Wo}from"fs/promises";import{join as ke}from"path";import{existsSync as Yo}from"fs";function at(){return ke(se(),".claude","agent-framework","briefs")}async function En(t){let e=ke(at(),t+".md"),n=await Pn(e,"utf-8");return{id:t,content:n}}async function An(){let t=at();return Yo(t)?(await qo(t,{withFileTypes:!0})).filter(r=>r.isFile()&&r.name.endsWith(".md")).map(r=>r.name.slice(0,-3)):[]}async function lt(t,e){let n=at(),r=ke(n,t+".md"),o=ke(n,e),s=ke(o,t+".md");await Ko(o,{recursive:!0});let i=await Pn(r,"utf-8");await Vo(s,i,"utf-8"),await Wo(r)}function Tn(t){let e=t.split(`
|
|
1143
|
+
`).map(l=>l.trim()).filter(l=>l),n=e.find(l=>/^(APPROVE|SALVAGE|REJECT)/.test(l));if(!n)return{verdict:"REJECT",feedback:t};let r=n.match(/^(APPROVE|SALVAGE|REJECT)/)?.[1];if(!r)return{verdict:"REJECT",feedback:t};let o=t.match(/score:\s*(\d+)/i),s=o&&o[1]?parseInt(o[1],10):void 0,i=e.indexOf(n),a=e.slice(i+1).join(`
|
|
1144
|
+
`).trim();return{verdict:r,score:s,feedback:a||n}}var Zo=Jo(Qo);function es(){let t=process.env.AFK_EVAL_HARNESS_ROOT;if(t){let o=X(t,"scripts","eval-harness","runner.py");if(Cn(o))return o}let e=Xo(import.meta.url),n=X(e,"../../../.."),r=X(n,"..","awa-private","scripts","eval-harness","runner.py");if(Cn(r))return r;throw new Error(`Could not find eval-harness runner.py. Tried: ${r}`)}function ts(t){return X(t,"..","..","..","plugins","awa-private")}function ns(){let t=process.env.HOME||process.env.USERPROFILE||"~";return X(t,".claude","agent-framework","ceiling-ledger")}function rs(t){let e=t.split(`
|
|
1145
|
+
`),n=[],r=/^\s+✗\s+(\S+):/;for(let o of e){let s=o.match(r);s&&s[1]&&n.push(s[1])}return n}async function os(t){let e=ns(),n=X(e,"qualifications.jsonl");await In(e,{recursive:!0});let o=new Date().toISOString().split(".")[0]+"Z",i=JSON.stringify({timestamp:o,surface:"atlas",refers_to_run_id:t,source:"forge-gate-check-ts"})+`
|
|
1146
|
+
`;return await Mn(n,i,{flag:"a"}),o}async function ss(){let t;try{t=es()}catch(l){throw new Error(`Failed to resolve eval-harness runner.py: ${l instanceof Error?l.message:String(l)}`)}let e=ts(t),n="",r="",o=0;try{let l=await Zo("python3",[t,"--plugin-root",e],{timeout:6e4});n=l.stdout||"",r=l.stderr||"",o=0}catch(l){let c=l;if(n=c.stdout||"",r=c.stderr||"",o=typeof c.code=="number"?c.code:1,c.code==="ENOENT"||r&&r.includes("No such file"))throw new Error(`eval-harness runner.py not found at ${t}.`)}let s=o===0?"OPEN":"CLOSED",i=s==="CLOSED"?rs(n):void 0,a;if(s==="OPEN"){let l=new Date().toISOString().split(".")[0]+"Z";a=await os(l)}return{gate_status:s,exit_code:o,stdout:n,stderr:r||void 0,tasks_failed:i,ledger_entry_ref:a}}var is=N.object({iteration:N.number().int().positive(),verdict:N.enum(["APPROVE","SALVAGE","REJECT"]),score:N.number().optional(),feedback:N.string()}),zc=N.object({status:N.enum(["APPROVED","REJECTED","GATE_CLOSED","MAX_ITERATIONS"]),skill_path:N.string().optional(),qualify_verdicts:N.array(is),brief_id:N.string().optional(),telemetry_ref:N.string()});async function as(t,e){let n=typeof t=="string"?{brief:t}:typeof t=="object"&&t!==null?t:{},r=n.brief,o=n.forceThaw??!1,s=n.maxIterations??3,i="",a=[],l="REJECTED",c,d;try{let u=await ss();if(u.gate_status==="CLOSED"&&!o)return i=await V({event:"forge.gate_check",gate_status:"CLOSED"}),{status:"GATE_CLOSED",qualify_verdicts:[],telemetry_ref:i};o&&u.gate_status==="CLOSED"&&(await xn(),i=await V({event:"forge.thaw_override",gate_status:"CLOSED"})),i=await V({event:"forge.gate_check",gate_status:"OPEN"});let p="",m=!1;if(r)p=r,m=!0;else{let P=await An();if(P.length>0){let O=P[0],R=await En(O);p=R.content,d=R.id,m=!0}else{if(!e?.sessionId)throw new Error("forge requires parent session for gap discovery");let R=I("forge")["gap-discovery.md"];if(!R)throw new Error("forge skill missing gap-discovery.md prompt");let w=await(await new S().forkSubagent({parent:{sessionId:e.sessionId},config:{model:"sonnet",systemPrompt:R},idPrefix:"forge-gap-discovery"})).runToResult("Identify the most impactful skill gap.");if(w.status!=="succeeded")throw new Error(`gap discovery failed: ${C(w)}`);if(p=w.message?.content||"",!p)throw new Error("gap discovery returned no concept")}}if(i=await V({event:"forge.brief_loaded",used_brief:m,brief_id:d||null}),!e?.sessionId)throw new Error("forge requires parent session for skill generation");let g=I("forge"),y=g["generate.md"],f=g["system.md"];if(!y)throw new Error("forge skill missing generate.md prompt");if(!f)throw new Error("forge skill missing system.md prompt");let j=await(await new S().forkSubagent({parent:{sessionId:e.sessionId},config:{model:"sonnet",systemPrompt:f},idPrefix:"forge-generate"})).runToResult(`Generate a new amplifier skill based on this concept:
|
|
1147
|
+
|
|
1148
|
+
${p}`);if(j.status!=="succeeded")throw new Error(`skill generation failed: ${C(j)}`);let D=j.message?.content||"";if(!D)throw new Error("skill generation returned no output");for(let P=1;P<=s;P++){let O=it.systemPrompt;if(!O)throw new Error("qualify agent missing system prompt");let Y=await(await new S().forkSubagent({parent:{sessionId:e.sessionId},config:{model:"sonnet",systemPrompt:O},idPrefix:`forge-qualify-${P}`})).runToResult(`Evaluate this amplifier skill against the force-multiplier criteria:
|
|
1149
|
+
|
|
1150
|
+
${D}`);if(Y.status!=="succeeded")throw new Error(`qualify iteration ${P} failed: ${C(Y)}`);let w=Y.message?.content||"",{verdict:k,score:h,feedback:E}=Tn(w),H={iteration:P,verdict:k,score:h,feedback:E};if(a.push(H),i=await V({event:"forge.qualify_iteration",iteration:P,verdict:k,score:h||null}),k==="APPROVE"){l="APPROVED";break}else if(k==="SALVAGE"&&P<s){let _=g["qualify-rework.md"];if(!_)throw new Error("forge skill missing qualify-rework.md prompt");let ee=_.replace("{feedback}",E).replace("{original_skill}",D),te=await(await new S().forkSubagent({parent:{sessionId:e.sessionId},config:{model:"sonnet",systemPrompt:ee},idPrefix:`forge-rework-${P}`})).runToResult("Refine the skill based on the feedback.");if(te.status!=="succeeded")throw new Error(`rework iteration ${P} failed: ${C(te)}`);if(D=te.message?.content||"",!D)throw new Error(`rework iteration ${P} returned no output`)}else k==="REJECT"&&P>=s&&(l="MAX_ITERATIONS")}if(l==="APPROVED"){let P=D.match(/^name:\s*([^\n]+)/m),O=P&&P[1]?P[1].trim().replace(/^["']|["']$/g,""):"unknown",R=X(se(),".claude","agent-framework-local","skills",O);await In(R,{recursive:!0});let W=X(R,"SKILL.md");await Mn(W,D,"utf-8"),c=W,m&&d&&await lt(d,"consumed"),i=await V({event:"forge.complete",status:"APPROVED",skill_name:O,iterations:a.length})}else l==="MAX_ITERATIONS"&&(m&&d&&await lt(d,"failed"),i=await V({event:"forge.complete",status:"MAX_ITERATIONS",iterations:a.length}))}catch(u){throw i=await V({event:"forge.error",error:u instanceof Error?u.message:String(u)}),u}return{status:l,skill_path:c,qualify_verdicts:a,brief_id:d,telemetry_ref:i}}var ls={name:"forge",description:'Creates new amplifier skills gated by forge-gate-check, with autonomous gap discovery, skill generation, and qualify iteration loop \u22643\xD7. Writes approved skills and appends telemetry to shared JSONL with surface: "atlas".',handler:as,argumentHint:"[--brief <path>]",whenToUse:"When the user wants to grow the plugin with a new amplifier skill \u2014 autonomously generates and validates one.",flags:["--brief"]};G(ls);var cs={name:"bash",description:"Execute a shell command and return its stdout and stderr. Use for running programs, installing packages, git operations, and any task that requires a shell. Commands run in the user's default shell. Long-running commands should use timeout_ms. Output is capped at ~100KB; excess is truncated with a notice.",input_schema:{type:"object",properties:{command:{type:"string",description:"The shell command to execute."},timeout_ms:{type:"number",description:"Optional timeout in milliseconds (default 120000, max 600000). The command is killed if it exceeds this duration."}},required:["command"]}},ds={name:"read_file",description:"Read a file from the filesystem. Returns the file content with line numbers. Use offset and limit to read specific sections of large files. When the read returns a partial view, the response ends with a `... (showing lines X-Y of Z [\u2014 pass offset=N to continue])` annotation indicating the full file size and how to continue. Binary files are detected and rejected. Missing files return an error.",input_schema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to the file to read."},offset:{type:"number",description:"Line number to start reading from (1-based). Defaults to 1."},limit:{type:"number",description:"Maximum number of lines to read. Defaults to 2000."}},required:["file_path"]}},us={name:"write_file",description:"Write content to a file, creating it if it does not exist or overwriting if it does. Parent directories are created automatically. Prefer edit_file for modifying existing files \u2014 use write_file only for new files or complete rewrites.",input_schema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to the file to write."},content:{type:"string",description:"The full content to write to the file."}},required:["file_path","content"]}},ps={name:"edit_file",description:"Perform an exact string replacement in a file. Finds old_string and replaces it with new_string. The edit fails if old_string is not found or matches multiple locations (unless replace_all is true). Always use read_file first to verify the exact content before editing.",input_schema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to the file to edit."},old_string:{type:"string",description:"The exact string to find and replace. Must match file content exactly."},new_string:{type:"string",description:"The replacement string."},replace_all:{type:"boolean",description:"If true, replace all occurrences. If false (default), fail when multiple matches exist."}},required:["file_path","old_string","new_string"]}},fs={name:"glob",description:'Find files matching a glob pattern. Returns matching file paths, capped at 500 results. Use for discovering files before reading them. Patterns follow standard glob syntax (e.g., "src/**/*.ts", "*.json").',input_schema:{type:"object",properties:{pattern:{type:"string",description:'Glob pattern to match (e.g., "src/**/*.ts").'},path:{type:"string",description:"Base directory to search from. Defaults to the current working directory."}},required:["pattern"]}},ms={name:"grep",description:"Search file contents for lines matching a pattern. Returns matches in file:line:content format. Uses grep -rn (or ripgrep if available). Output is capped to prevent overflow. Use for finding symbols, strings, or patterns across the codebase.",input_schema:{type:"object",properties:{pattern:{type:"string",description:"Search pattern (basic regex by default)."},path:{type:"string",description:"Directory or file to search. Defaults to current working directory."},include:{type:"string",description:'File glob to restrict search (e.g., "*.ts"). Passed as --include to grep.'}},required:["pattern"]}},gs={name:"list_directory",description:"List the contents of a directory. Returns file and subdirectory names with type annotations (directories end with /). Use for exploring project structure.",input_schema:{type:"object",properties:{path:{type:"string",description:"Absolute path to the directory to list."}},required:["path"]}},Rn={name:"agent",description:"Dispatch a subtask to an independent agent session. The agent runs with its own conversation context and tool access. Use for tasks that benefit from isolated context (research, parallel work, specialized focus). The agent runs to completion and returns its final response.",input_schema:{type:"object",properties:{prompt:{type:"string",description:"The task for the agent to perform."},model:{type:"string",description:"Model for the agent. Defaults to parent session model. Override per-call to right-size cost vs. capability \u2014 `haiku` (cheapest/fastest), `sonnet` (general-use), `opus` (most capable). Append `_1m` (e.g. `sonnet_1m`) for 1M-context variants. Full model IDs are also accepted."},max_turns:{type:"number",description:"Maximum conversation turns (default 10, max 50)."},id_prefix:{type:"string",description:"Label prefix for log correlation."}},required:["prompt"]}},_n={name:"skill",description:"Invoke a registered skill by name. Skills are specialized capabilities that dispatch subagents with domain-specific prompts. Check the system prompt for the list of available skills and their descriptions.",input_schema:{type:"object",properties:{name:{type:"string",description:'Skill name (e.g., "mint", "diagnose", "shadow-verify").'},arguments:{type:"string",description:"Arguments to pass to the skill."}},required:["name"]}},de=[cs,ds,us,ps,fs,ms,gs],Dn=de.map(t=>t.name);import{config as nd}from"dotenv";var On={opus:"claude-opus-4-7",opus_1m:"claude-opus-4-7",sonnet:"claude-sonnet-4-6",sonnet_1m:"claude-sonnet-4-6",haiku:"claude-haiku-4-5-20251001"};function $n(t){return t in On}function Fn(t){let e=On[t];if(!e)throw new Error(`Invalid model: ${t}`);return e}function Se(t){if(t!==void 0)return typeof t=="string"&&$n(t)?Fn(t):t}import{execFileSync as hs}from"child_process";import{existsSync as ys,readFileSync as bs}from"fs";import{homedir as vs,userInfo as ws}from"os";import{join as ks}from"path";function jn(){let t=Ss();if(t===void 0)return;let e=xs(t);if(e!==void 0){if(e.expiresAt!==void 0&&e.expiresAt<=Date.now()){process.stderr.write("agent-afk: Claude Code OAuth token in keychain is expired. Run `claude login` to refresh.\n");return}return e.accessToken}}function Ss(){if(process.platform==="darwin")try{return hs("security",["find-generic-password","-s","Claude Code-credentials","-a",ws().username,"-w"],{stdio:["ignore","pipe","ignore"],encoding:"utf-8"}).trim()}catch{return}if(process.platform==="linux"){let t=ks(vs(),".claude",".credentials.json");if(!ys(t))return;try{return bs(t,"utf-8")}catch{return}}}function xs(t){let e;try{e=JSON.parse(t)}catch{return}if(typeof e!="object"||e===null)return;let n=e.claudeAiOauth;if(typeof n!="object"||n===null)return;let r=n,o=r.accessToken;if(typeof o!="string"||o.length===0)return;let s=r.expiresAt,i={accessToken:o};return typeof s=="number"&&(i.expiresAt=s),i}function Hn(){return process.env.ANTHROPIC_API_KEY||process.env.CLAUDE_CODE_OAUTH_TOKEN||jn()}function $(){return Hn()}async function Ln(t,e){let r=I("mint")["spec.md"];if(!r)throw new Error("mint skill missing spec.md prompt");let i=await(await new S().forkSubagent({parent:{sessionId:e},config:{model:"sonnet",systemPrompt:r,apiKey:$()},idPrefix:"mint-spec"})).runToResult(`Create a detailed specification for: ${t}`);if(i.status!=="succeeded"||!i.message)throw new Error(`spec phase failed: ${C(i)}`);return i.message.content}async function Un(t,e){let r=I("mint")["research.md"];if(!r)throw new Error("mint skill missing research.md prompt");let i=await(await new S().forkSubagent({parent:{sessionId:e},config:{model:"sonnet",systemPrompt:r,apiKey:$()},idPrefix:"mint-research"})).runToResult(`Gather context and research for this specification:
|
|
1151
|
+
|
|
1152
|
+
${t}`);if(i.status!=="succeeded"||!i.message)throw new Error(`research phase failed: ${C(i)}`);return i.message.content}async function Nn(t,e,n){let o=I("mint")["plan.md"];if(!o)throw new Error("mint skill missing plan.md prompt");let i=await new S().forkSubagent({parent:{sessionId:n},config:{model:"sonnet",systemPrompt:o,apiKey:$()},idPrefix:"mint-plan"}),a=`Specification:
|
|
1153
|
+
${t}
|
|
1154
|
+
|
|
1155
|
+
Research findings:
|
|
1156
|
+
${e}
|
|
1157
|
+
|
|
1158
|
+
Create a detailed implementation plan based on the spec and research.`,l=await i.runToResult(a);if(l.status!=="succeeded"||!l.message)throw new Error(`plan phase failed: ${C(l)}`);return l.message.content}function As(t){let e=t.match(/\.\w+\s/g)||[],n=t.match(/Files?:\s*(.+?)(?:\n|$)/gi)||[],r=e.length;for(let o of n){let s=o.replace(/Files?:\s*/i,"").split(/[,;]/g).filter(i=>i.trim().length>0);r+=s.length}return r}async function Bn(t,e){if(As(t)<3)return null;try{return await z("parallelize").handler({plan:t})}catch(r){return console.warn(`Parallelize dispatch failed: ${r instanceof Error?r.message:String(r)}`),null}}import{z as K}from"zod";function ue(t){let e=le();e&&e({type:"panel",spec:t},{subagentId:"__main__"})}var Ts=K.object({status:K.enum(["PASS","FAIL"]),status_reason:K.string().optional(),files_changed:K.array(K.string()),tests_passed:K.boolean(),build_passed:K.boolean().optional(),verification_passed:K.boolean().optional(),notes:K.string()});async function Gn(t,e,n){let o=I("mint")["build.md"];if(!o)throw new Error("mint skill missing build.md prompt");let i=await new S().forkSubagent({parent:{sessionId:n},config:{model:"sonnet",systemPrompt:o,apiKey:$()},idPrefix:"mint-build",outputSchema:Ts}),a=`Implementation plan:
|
|
1159
|
+
${t}
|
|
1160
|
+
|
|
1161
|
+
`+(e?`Wave orchestration plan:
|
|
1162
|
+
${JSON.stringify(e,null,2)}
|
|
1163
|
+
|
|
1164
|
+
`:"")+"Execute the implementation plan following TDD (test-first) principles.",l=await i.runToResult(a);if(l.status!=="succeeded"||!l.output)throw new Error(`build phase failed: ${C(l)}`);let c=l.output,d={filesChanged:c.files_changed,testsPassed:c.tests_passed,notes:c.notes};return ue({kind:"checkpoint",title:"build",body:[`Files changed: ${d.filesChanged.length}`,`Tests: ${d.testsPassed?"passed":"failed"}`,"Next: verify"]}),d}import{z as pe}from"zod";var Cs=pe.object({status:pe.enum(["PASS","FAIL"]),status_reason:pe.string().optional(),issues:pe.array(pe.string()).default([]),summary:pe.string().optional()});async function ct(t,e,n,r,o){let i=await new S().forkSubagent({parent:{sessionId:r},config:{model:"sonnet",systemPrompt:o,apiKey:$()},idPrefix:`mint-verify-${t}`,outputSchema:Cs}),a=`Plan:
|
|
1165
|
+
${e}
|
|
1166
|
+
|
|
1167
|
+
Build results:
|
|
1168
|
+
${JSON.stringify(n,null,2)}
|
|
1169
|
+
|
|
1170
|
+
Mode: ${t}
|
|
1171
|
+
|
|
1172
|
+
Run ${t} verification on the implementation.`,l;try{l=await i.runToResult(a)}finally{await i.teardown().catch(()=>{})}if(l.status!=="succeeded"||!l.output)return{passed:!1,issues:[`${t} verification failed: ${C(l)}`]};let c=l.output,d=c.status==="PASS";return{passed:d,issues:d?void 0:c.issues}}async function Be(t,e,n){let o=I("mint")["verify.md"];if(!o)throw new Error("mint skill missing verify.md prompt");let[s,i,a]=await Promise.all([ct("test",t,e,n,o),ct("lint",t,e,n,o),ct("design-review",t,e,n,o)]),l=[];s.issues&&l.push(...s.issues),i.issues&&l.push(...i.issues),a.issues&&l.push(...a.issues);let c={testsPassed:s.passed,lintPassed:i.passed,designReviewPassed:a.passed,...l.length>0?{issues:l}:{}},d=c.testsPassed&&c.lintPassed&&c.designReviewPassed,u=p=>p?"passed":"failed";return ue({kind:d?"checkpoint":"diagnosis",title:"verify",body:[`Tests: ${u(c.testsPassed)} \xB7 Lint: ${u(c.lintPassed)}`,`Design review: ${u(c.designReviewPassed)}`,...d?["Next: ship"]:[`Issues: ${l.length} (heal loop will retry)`]]}),c}async function zn(t,e,n,r,o){if(n.testsPassed&&n.lintPassed&&n.designReviewPassed)return{healed:!0,newHealIterations:r,newVerifyResults:n};if(r>=2)return{healed:!1,newHealIterations:r,newVerifyResults:n};try{let s=z("diagnose"),i=`Verification failures:
|
|
1173
|
+
Tests: ${n.testsPassed?"PASS":"FAIL"}
|
|
1174
|
+
Lint: ${n.lintPassed?"PASS":"FAIL"}
|
|
1175
|
+
Design: ${n.designReviewPassed?"PASS":"FAIL"}
|
|
1176
|
+
Issues: ${n.issues?.join(`
|
|
1177
|
+
`)||"none"}`,a=await s.handler({failure:i,repoPath:process.cwd(),context:t}),l="";if(typeof a=="object"&&a!==null&&"winner"in a&&typeof a.winner=="object"&&a.winner!==null){let j=a.winner;typeof j.proposed_fix=="string"&&(l=j.proposed_fix)}let d=I("mint")["heal.md"];if(!d)throw new Error("mint skill missing heal.md prompt");let p=await new S().forkSubagent({parent:{sessionId:o.sessionId},config:{model:"sonnet",systemPrompt:d,apiKey:$()},idPrefix:"mint-heal"}),m=n.issues?.join(`
|
|
1178
|
+
`)??"none",g=`Plan:
|
|
1179
|
+
${t}
|
|
1180
|
+
|
|
1181
|
+
Proposed fix from diagnosis:
|
|
1182
|
+
${l}
|
|
1183
|
+
|
|
1184
|
+
Verification issues:
|
|
1185
|
+
${m}
|
|
1186
|
+
|
|
1187
|
+
Apply the fix and update the implementation.`,y=await p.runToResult(g);if(y.status!=="succeeded"||!y.message)throw new Error(`heal phase failed: ${C(y)}`);let f=/^\s*FIX_APPLIED:\s*(true|false)/im.exec(y.message.content)?.[1]?.toLowerCase()==="true",v=r+1;if(!f)return{healed:!1,newHealIterations:v,newVerifyResults:n};if(!o.sessionId)throw new Error("Parent session ID required for verification");let x=await Be(t,e,o.sessionId);return{healed:x.testsPassed&&x.lintPassed&&x.designReviewPassed,newHealIterations:v,newVerifyResults:x}}catch{return{healed:!1,newHealIterations:r+1,newVerifyResults:n}}}async function qn(t,e){let r=I("mint")["ship.md"];if(!r)throw new Error("mint skill missing ship.md prompt");let s=await new S().forkSubagent({parent:{sessionId:e},config:{model:"sonnet",systemPrompt:r,apiKey:$()},idPrefix:"mint-ship"}),i=`Idea: ${t.idea}
|
|
1188
|
+
|
|
1189
|
+
Specification:
|
|
1190
|
+
${t.spec}
|
|
1191
|
+
|
|
1192
|
+
Plan:
|
|
1193
|
+
${t.plan}
|
|
1194
|
+
|
|
1195
|
+
Build results:
|
|
1196
|
+
${JSON.stringify(t.buildResults,null,2)}
|
|
1197
|
+
|
|
1198
|
+
Verification results:
|
|
1199
|
+
${JSON.stringify(t.verifyResults,null,2)}
|
|
1200
|
+
|
|
1201
|
+
Create a ship-ready summary with next steps.`,a=await s.runToResult(i);if(a.status!=="succeeded"||!a.message)throw new Error(`ship phase failed: ${C(a)}`);let l=t.buildResults?.filesChanged.length??0,c=t.healIterations;return ue({kind:"checkpoint",title:"ship \u2014 done",body:[`Files changed: ${l}`,`Heal iterations: ${c}`,`Idea: ${t.idea}`]}),a.message.content}import{existsSync as Vn,mkdirSync as Is,readFileSync as Ms,unlinkSync as Rs,writeFileSync as _s}from"fs";import{dirname as Ds,join as Os}from"path";function dt(t){return Os(en(),t,"mint-state.json")}function Kn(t,e){let n=dt(t);Is(Ds(n),{recursive:!0}),_s(n,JSON.stringify(e,null,2),"utf-8")}function $s(t){if(typeof t!="object"||t===null)return!1;let e=t;return typeof e.currentPhase=="string"&&typeof e.idea=="string"&&typeof e.spec=="string"&&typeof e.healIterations=="number"&&Array.isArray(e.history)}function Wn(t){let e=dt(t);if(!Vn(e))return null;try{let n=JSON.parse(Ms(e,"utf-8"));return $s(n)?n:null}catch{return null}}function ut(t){let e=dt(t);if(Vn(e))try{Rs(e)}catch{}}var Fs=2,Yn=/^\s*--continue(?:\s+(?:approved|yes|y))?\s*$/i,js="To approve and run the rest of the pipeline, invoke /mint --continue approved (or call mint with {userApproved: true} \u2014 no idea field needed). The handler will reload the spec state from disk.";function Z(t,e,n){t.history.push({phase:e,output:n,timestamp:Date.now()})}function Hs(t){if(typeof t=="string")return Yn.test(t)?{userApproved:!0}:{idea:t};if(typeof t=="object"&&t!==null){let e=t,n=typeof e.idea=="string"?e.idea:void 0;if(n!==void 0&&Yn.test(n))return{userApproved:!0};if("idea"in e||"resumeFrom"in e||e.userApproved===!0)return e}throw new Error("mint handler requires input.idea (string), input as string, or {userApproved: true} to resume")}async function Qn(t,e){if(!e.sessionId)throw new Error("runPhasesAfterSpec requires parentSession.sessionId");let n=e.sessionId;try{t.currentPhase="research",t.research=await Un(t.spec,n),Z(t,"research",t.research),t.currentPhase="plan",t.plan=await Nn(t.spec,t.research,n),Z(t,"plan",t.plan),t.currentPhase="parallelize",t.waveOrchestrationPlan=await Bn(t.plan,e),Z(t,"parallelize",JSON.stringify(t.waveOrchestrationPlan||"skipped")),t.currentPhase="build",t.buildResults=await Gn(t.plan,t.waveOrchestrationPlan,n),Z(t,"build",JSON.stringify(t.buildResults)),t.currentPhase="verify",t.verifyResults=await Be(t.plan,t.buildResults,n),Z(t,"verify",JSON.stringify(t.verifyResults)),t.currentPhase="heal";let r=t.verifyResults.testsPassed&&t.verifyResults.lintPassed&&t.verifyResults.designReviewPassed;for(;!r&&t.healIterations<Fs;){let s=await zn(t.plan,t.buildResults,t.verifyResults,t.healIterations,e);t.healIterations=s.newHealIterations,t.verifyResults=s.newVerifyResults,r=s.healed,Z(t,"heal",`Iterations: ${t.healIterations}, Success: ${r}`)}if(!r)return{paused:!0,phase:"heal-failed",reason:`Heal capped at ${t.healIterations} iterations; still have failures`,state:t,nextStep:"Heal loop exhausted. Inspect verifyResults, fix manually, then re-invoke /mint with a fresh idea \u2014 resume is not supported from heal-failed."};t.currentPhase="ship";let o=await qn(t,n);return Z(t,"ship",o),{completed:!0,artifact:o,state:t}}catch(r){throw new Error(`mint failed at ${t.currentPhase}: ${r}`)}}function Jn(t,e){return("completed"in e||e.phase==="heal-failed")&&ut(t),e}async function Ls(t,e){let n=Hs(t);if(!e?.sessionId)throw new Error("mint handler requires a parent session to fork subagents");let r=e.sessionId;if(n.userApproved){let i=n.resumeFrom??Wn(r);if(!i)throw new Error("mint: no paused spec found for this session to continue. Run /mint <idea> first, then /mint --continue approved.");let a=await Qn(i,e);return Jn(r,a)}if(!n.idea)throw new Error("mint: no idea provided. Run /mint <idea> to start, or /mint --continue approved to resume a paused spec.");ut(r);let o={currentPhase:"spec",idea:n.idea,healIterations:0,history:[]};try{o.spec=await Ln(n.idea,r),Z(o,"spec",o.spec)}catch(i){throw new Error(`mint failed at spec: ${i}`)}if(!n.autoApprove)return Kn(r,o),{paused:!0,phase:"spec",spec:o.spec,state:o,nextStep:js};let s=await Qn(o,e);return Jn(r,s)}var Us={name:"mint",description:"Takes a feature idea or refactor scope and delivers a ship-ready, verified implementation end-to-end",handler:Ls,argumentHint:"<idea> | --continue [approved]",whenToUse:"When the user wants a feature or refactor delivered end-to-end (spec \u2192 research \u2192 build \u2192 verify) in one ship-ready pass. After the spec phase pauses for approval, invoke `/mint --continue approved` (or call mint with `{userApproved: true}`) to resume \u2014 the handler reloads the spec state from disk.",flags:["--continue"]};G(Us);import{existsSync as Ns,readdirSync as Bs,readFileSync as Gs,statSync as zs}from"fs";import{join as qs}from"path";function Xn(t){let e=[];function n(r,o=0){if(o>10||!Ns(r))return;let s;try{s=Bs(r)}catch{return}for(let i of s){if(i.startsWith("."))continue;let a=qs(r,i),l;try{l=zs(a)}catch{continue}if(l.isFile()&&i==="SKILL.md"){let c=Vs(a);c.name&&e.push(c)}else l.isDirectory()&&n(a,o+1)}}return n(t),e}function Vs(t){try{let e=Gs(t,"utf-8");if(!e.startsWith(`---
|
|
1202
|
+
`))return{};let n=e.slice(4),r=n.indexOf(`
|
|
1203
|
+
---`);if(r===-1)return{};let o=n.slice(0,r),s=n.slice(r+4).trim(),i={},a=o.split(`
|
|
1204
|
+
`);for(let l of a){if(!l)continue;let c=l.indexOf(":");if(c===-1)continue;let d=l.slice(0,c).trim(),u=l.slice(c+1).trim();d==="name"?i.name=u.replace(/^["']|["']$/g,""):d==="description"?i.description=u.replace(/^["']|["']$/g,""):d==="argumentHint"&&(i.argumentHint=u.replace(/^["']|["']$/g,""))}return s.length>0&&(i.body=s),i}catch{return{}}}function Zn(t){let e=pt(t);if(e.length===0)return"";let n=[];for(let r of e){let o=r.argumentHint?`${r.argumentHint}`:"",s=o?`- \`${r.name} ${o}\`: ${r.description}`:`- ${r.name}: ${r.description}`;n.push(s),r.whenToUse&&n.push(` When to use: ${r.whenToUse}`)}return["Available skills (invoke via the `skill` tool):",...n].join(`
|
|
1205
|
+
`)}function pt(t){let e=[],n=new Set;for(let o of qt()){let s=z(o);e.push({name:o,description:s.description,source:s.origin==="user"?"user":s.origin==="project"?"project":"builtin",argumentHint:s.argumentHint,whenToUse:s.whenToUse}),n.add(o)}let r=t??[...Q(je()),...Q()];for(let o of r){if(o.type!=="local")continue;let s=Xn(o.path);for(let i of s)!i.name||n.has(i.name)||(e.push({name:i.name,description:i.description??`Skill from plugin at ${o.path}`,source:"plugin"}),n.add(i.name))}return e}var Ks=3,Ws="claude-haiku-4-5-20251001",Ys=1024,Qs=[{value:"claude-sonnet-4-5-20250929",displayName:"Claude Sonnet 4.5",description:"Latest balanced Claude \u2014 recommended default"},{value:"claude-opus-4-5-20250929",displayName:"Claude Opus 4.5",description:"Highest-capability Claude"},{value:"claude-haiku-4-5-20250929",displayName:"Claude Haiku 4.5",description:"Fastest, cheapest Claude"}],Ge=class{client;authMode;initSessionId;promptStream;toolDispatcher;maxTokens;tools;systemPrefix;userSystem;currentModel;currentPermissionMode;messages=[];closed=!1;abortController=null;pendingAbortReason=null;closedPromise;closeResolve=null;lastUsage=null;constructor(e){this.client=e.client,this.authMode=e.authMode,this.initSessionId=ft(),this.promptStream=e.promptStream,this.toolDispatcher=e.toolDispatcher,this.maxTokens=e.maxTokens,this.tools=e.tools,this.systemPrefix=e.systemPrefix,this.userSystem=e.userSystem,this.currentModel=e.model,this.currentPermissionMode=e.permissionMode??"default",this.closedPromise=new Promise(n=>{this.closeResolve=()=>n("__closed__")})}async*[Symbol.asyncIterator](){yield{type:"session.init",info:{sessionId:this.initSessionId,model:this.currentModel,permissionMode:this.currentPermissionMode,cwd:process.cwd(),tools:[],slashCommands:[],skills:[],plugins:[],mcpServers:[],apiKeySource:this.authMode,version:"anthropic-direct-v1"}};let n=this.promptStream[Symbol.asyncIterator]();try{for(;!this.closed;){let r=await Promise.race([n.next(),this.closedPromise]);if(r==="__closed__")break;let o=r;if(o.done)break;let s=o.value,i=new AbortController;if(this.abortController=i,this.pendingAbortReason!==null&&!i.signal.aborted&&(i.abort(this.pendingAbortReason),this.pendingAbortReason=null),i.signal.aborted)return;this.messages.push({role:"user",content:s.content});let a=this.composeSystem(),l=et(this.authMode,this.initSessionId,ft()),c={client:this.client,messages:this.messages,system:a,tools:this.tools,toolDispatcher:this.toolDispatcher,model:this.currentModel,maxTokens:this.maxTokens,headers:l,signal:i.signal,ctx:{sessionId:this.initSessionId}};try{for await(let d of $t(c)){if(this.closed)return;d.type==="turn.completed"&&(this.lastUsage=d.usage),yield d}}catch(d){if(i.signal.aborted)return;yield{type:"error",error:d instanceof Error?d:new Error(String(d))};return}finally{this.abortController===i&&(this.abortController=null)}}}catch(r){yield{type:"error",error:r instanceof Error?r:new Error(String(r))}}finally{try{await n.return?.()}catch{}}}composeSystem(){let e=this.systemPrefix,n=this.userSystem,r=[];return e&&e.length>0&&r.push(...e),n&&n.length>0&&r.push({type:"text",text:n}),r.length===0?null:Re()?It(r,_e()):r}async interrupt(){let e=this.abortController;if(e&&!e.signal.aborted){e.abort("interrupted");return}this.pendingAbortReason="interrupted"}async setModel(e){e!==void 0&&e.length>0&&(this.currentModel=e)}async setPermissionMode(e){this.currentPermissionMode=e}async supportedCommands(){try{return pt().map(n=>{let r={name:n.name,description:n.description};return n.argumentHint&&(r.argumentHint=n.argumentHint),r})}catch{return[]}}async supportedModels(){return Qs.map(e=>({...e}))}async supportedAgents(){return[]}async getContextUsage(){return{tools:[],agents:[],isAutoCompactEnabled:!1,apiUsage:this.lastUsage}}async mcpServerStatus(){return[]}async accountInfo(){return{subscriptionType:this.authMode==="oauth"?"claude-subscription":"api-key"}}async rewindFiles(e,n){return{canRewind:!1,error:"anthropic-direct provider does not support file checkpoint rewind"}}async compact(){let e=this.messages.length;if(this.closed)return{compacted:!1,reason:"session-closed",messagesBefore:e,messagesAfter:e};if(this.abortController!==null)return{compacted:!1,reason:"turn-in-flight",messagesBefore:e,messagesAfter:e};let n=Js(),r=Ht(this.messages,n);if(r<=0)return{compacted:!1,reason:"history-too-short",messagesBefore:e,messagesAfter:e};let o=this.messages.slice(0,r),s=Xs(),i=Lt(o,s,Ys),a=new AbortController;this.abortController=a,this.pendingAbortReason!==null&&!a.signal.aborted&&(a.abort(this.pendingAbortReason),this.pendingAbortReason=null);let l;try{if(a.signal.aborted)return{compacted:!1,reason:"aborted",messagesBefore:e,messagesAfter:e};let u=et(this.authMode,this.initSessionId,ft()),p=this.client,m=await Promise.resolve(p.messages.create(i,{headers:u,signal:a.signal}));l=await Zs(m)}catch(u){return a.signal.aborted?{compacted:!1,reason:"aborted",messagesBefore:e,messagesAfter:e}:{compacted:!1,reason:"summarization-failed: "+(u instanceof Error?u.message:String(u)),messagesBefore:e,messagesAfter:e}}finally{this.abortController===a&&(this.abortController=null)}if(l.trim().length===0)return{compacted:!1,reason:"empty-summary",messagesBefore:e,messagesAfter:e};let c=Nt(this.messages,r,l),d=Ut(this.messages,r,l);return this.messages.splice(0,this.messages.length,...d),{compacted:!0,messagesBefore:e,messagesAfter:this.messages.length,tokensSavedEstimate:c}}close(){this.closed=!0;let e=this.abortController;e&&!e.signal.aborted?e.abort("closed"):this.pendingAbortReason="closed",this.closeResolve?.()}};function Js(){let t=process.env.AFK_COMPACT_KEEP_LAST_TURNS;if(t!==void 0&&t.length>0){let e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>0)return e}return Ks}function Xs(){let t=process.env.AFK_COMPACT_MODEL;return t!==void 0&&t.length>0?t:Ws}async function Zs(t){let e="";for await(let n of t)if(n.type==="content_block_delta"){let r=n.delta;r.type==="text_delta"&&typeof r.text=="string"&&(e+=r.text)}return e}var ei=new Set(["read_file","glob","grep","list_directory"]);function Ee(t,e){return e?.allowedTools?e.allowedTools.includes(t)?{allowed:!0}:{allowed:!1,reason:`Tool "${t}" is not in the configured allowlist`}:ei.has(t)?{allowed:!0}:{allowed:!1,reason:`Tool "${t}" requires explicit permission (add to allowedTools config)`}}var ti=new Set(["agent","read_file","glob","grep","list_directory"]);function er(t){return ti.has(t)}function ni(t,e){return t.reduce((n,r,o)=>{let s=e(r.name,r.input),i=n[n.length-1];return i&&s&&i.isConcurrencySafe?i.indices.push(o):n.push({isConcurrencySafe:s,indices:[o]}),n},[])}var ie=class{handlers;schemas;hookRegistry;permissions;subagentExecutor;skillExecutor;classifier;constructor(e){this.handlers=e.handlers,this.schemas=e.schemas,this.hookRegistry=e.hookRegistry,this.permissions=e.permissions,this.subagentExecutor=e.subagentExecutor,this.skillExecutor=e.skillExecutor,this.classifier=e.concurrencyClassifier??er}get toolDefs(){return this.schemas}async execute(e){if(e.signal.aborted)return{content:"Tool call aborted",isError:!0};if(this.hookRegistry){let s={event:"PreToolUse",toolName:e.name,input:e.input};try{await this.hookRegistry.dispatch(s,e.signal)}catch(i){if(i instanceof U)return{content:`Tool "${e.name}" blocked by PreToolUse hook: ${i.message}`,isError:!0};throw i}}let n=Ee(e.name,this.permissions);if(!n.allowed)return{content:n.reason??`Tool "${e.name}" is not permitted`,isError:!0};if(e.name==="agent"){if(!this.subagentExecutor)return{content:"Agent tool is not available in this session configuration",isError:!0};let s;try{s=await this.subagentExecutor.execute(e)}catch(i){s={content:`Agent tool error: ${i instanceof Error?i.message:String(i)}`,isError:!0}}if(this.hookRegistry){let i={event:"PostToolUse",toolName:e.name,output:s.content};try{await this.hookRegistry.dispatch(i,e.signal)}catch{}}return s}if(e.name==="skill"){if(!this.skillExecutor)return{content:"Skill tool is not available in this session configuration",isError:!0};let s;try{s=await this.skillExecutor.execute(e)}catch(i){s={content:`Skill tool error: ${i instanceof Error?i.message:String(i)}`,isError:!0}}if(this.hookRegistry){let i={event:"PostToolUse",toolName:e.name,output:s.content};try{await this.hookRegistry.dispatch(i,e.signal)}catch{}}return s}let r=this.handlers.get(e.name);if(!r)return{content:`Unknown tool "${e.name}". Available tools: ${[...this.handlers.keys()].join(", ")}`,isError:!0};let o;try{o=await r(e.input,e.signal)}catch(s){o={content:`Tool execution error: ${s instanceof Error?s.message:String(s)}`,isError:!0}}if(this.hookRegistry){let s={event:"PostToolUse",toolName:e.name,output:o.content};try{await this.hookRegistry.dispatch(s,e.signal)}catch{}}return o}async executeBatch(e){if(e.length===0)return[];if(e.length===1)return[await this.execute(e[0])];let n=new Array(e.length),r=new Set;for(let i=0;i<e.length;i++){let a=e[i];if(a.signal.aborted){n[i]={content:"Tool call aborted",isError:!0},r.add(i);continue}if(this.hookRegistry){let c={event:"PreToolUse",toolName:a.name,input:a.input};try{await this.hookRegistry.dispatch(c,a.signal)}catch(d){if(d instanceof U){n[i]={content:`Tool "${a.name}" blocked by PreToolUse hook: ${d.message}`,isError:!0},r.add(i);continue}throw d}}let l=Ee(a.name,this.permissions);l.allowed||(n[i]={content:l.reason??`Tool "${a.name}" is not permitted`,isError:!0},r.add(i))}let o=e.map((i,a)=>({call:i,originalIndex:a})).filter((i,a)=>!r.has(a));if(o.length===0)return n;let s=ni(o.map(i=>i.call),this.classifier);for(let i of s){if(e[0].signal.aborted){for(let a of i.indices){let l=o[a].originalIndex;n[l]={content:"Tool call aborted",isError:!0}}continue}if(i.isConcurrencySafe){let a=await Promise.allSettled(i.indices.map(async l=>{let{call:c,originalIndex:d}=o[l];return{result:await this.executeCore(c),originalIndex:d}}));for(let l of a)if(l.status==="fulfilled")n[l.value.originalIndex]=l.value.result;else{let c=l.reason instanceof Error?l.reason.message:String(l.reason),d=i.indices[a.indexOf(l)];n[o[d].originalIndex]={content:`Tool execution error: ${c}`,isError:!0}}}else for(let a of i.indices){let{call:l,originalIndex:c}=o[a];if(l.signal.aborted){n[c]={content:"Tool call aborted",isError:!0};continue}n[c]=await this.executeCore(l)}}return n}async executeCore(e){if(e.name==="agent"){if(!this.subagentExecutor)return{content:"Agent tool is not available in this session configuration",isError:!0};let o;try{o=await this.subagentExecutor.execute(e)}catch(s){o={content:`Agent tool error: ${s instanceof Error?s.message:String(s)}`,isError:!0}}return this.firePostToolUse(e.name,o.content,e.signal),o}if(e.name==="skill"){if(!this.skillExecutor)return{content:"Skill tool is not available in this session configuration",isError:!0};let o;try{o=await this.skillExecutor.execute(e)}catch(s){o={content:`Skill tool error: ${s instanceof Error?s.message:String(s)}`,isError:!0}}return this.firePostToolUse(e.name,o.content,e.signal),o}let n=this.handlers.get(e.name);if(!n)return{content:`Unknown tool "${e.name}". Available tools: ${[...this.handlers.keys()].join(", ")}`,isError:!0};let r;try{r=await n(e.input,e.signal)}catch(o){r={content:`Tool execution error: ${o instanceof Error?o.message:String(o)}`,isError:!0}}return this.firePostToolUse(e.name,r.content,e.signal),r}firePostToolUse(e,n,r){if(!this.hookRegistry)return;let o={event:"PostToolUse",toolName:e,output:n};this.hookRegistry.dispatch(o,r).catch(()=>{})}};import{spawn as ri}from"child_process";function oi(t){if(typeof t!="object"||t===null)throw new Error("Input must be an object");let e=t;if(typeof e.command!="string")throw new Error('Input must have a "command" field of type string');let n=12e4;if(e.timeout_ms!==void 0){if(typeof e.timeout_ms!="number")throw new Error("timeout_ms must be a number");if(e.timeout_ms<0||e.timeout_ms>6e5)throw new Error("timeout_ms must be between 0 and 600000");n=e.timeout_ms}return{command:e.command,timeout_ms:n}}function si(t){return t.replace(/\x1b\[[0-9;]*[a-zA-Z]/g,"")}var tr=async(t,e)=>{let{command:n,timeout_ms:r}=oi(t);return e.aborted?{content:"Command aborted",isError:!0}:new Promise(o=>{let s=!1;function i(p){s||(s=!0,clearTimeout(l),e.removeEventListener("abort",u),o(p))}let a=ri(n,{shell:!0,stdio:["ignore","pipe","pipe"]}),l=setTimeout(()=>{a.kill(),i({content:`Command timed out after ${r}ms`,isError:!0})},r),c="",d="";a.stdout.on("data",p=>{c+=p.toString()}),a.stderr.on("data",p=>{d+=p.toString()});let u=()=>{a.kill(),i({content:"Command aborted",isError:!0})};e.addEventListener("abort",u),a.on("close",()=>{let p=(c+d).trimEnd();p=si(p);let m=1e5;p.length>m&&(p=p.slice(0,m)+`
|
|
1206
|
+
[output truncated \u2014 exceeded 100KB]`),i({content:p})}),a.on("error",p=>{i({content:`Failed to execute: ${p.message}`,isError:!0})})})};import{promises as ii}from"fs";var nr=async(t,e)=>{if(!t||typeof t!="object")return{content:"Invalid input: expected an object",isError:!0};let n=t,r=n.file_path,o=n.offset??1,s=n.limit??2e3;if(typeof r!="string")return{content:"Invalid input: file_path must be a string",isError:!0};if(typeof o!="number"||o<1)return{content:"Invalid input: offset must be a positive number",isError:!0};if(typeof s!="number"||s<1)return{content:"Invalid input: limit must be a positive number",isError:!0};try{let i=await ii.readFile(r),a=Math.min(8192,i.length);for(let f=0;f<a;f++)if(i[f]===0)return{content:`File appears to be binary: ${r}`,isError:!0};let l=i.toString("utf-8");if(l.length===0)return{content:""};let c=l.split(`
|
|
1207
|
+
`),d=Math.max(0,o-1),u=Math.min(c.length,d+s),p=c.slice(d,u),m=c.length;if(p.length===0)return{content:`... (offset ${o} is past end of file \u2014 file has ${m} lines)`};let g=String(m).length,y=p.map((f,v)=>{let x=d+v+1;return`${String(x).padStart(g," ")} ${f}`}).join(`
|
|
1208
|
+
`);if(p.length<m){let f=d+1,v=d+p.length,x=v<m?` \u2014 pass offset=${v+1} to continue`:"";return{content:`${y}
|
|
1209
|
+
... (showing lines ${f}-${v} of ${m}${x})`}}return{content:y}}catch(i){if(i instanceof Error){let a=i;return a.code==="ENOENT"?{content:`File not found: ${r}`,isError:!0}:a.code==="EACCES"?{content:`Permission denied: ${r}`,isError:!0}:{content:`Error reading file: ${i.message}`,isError:!0}}return{content:"Unknown error reading file",isError:!0}}};import{writeFile as ai}from"fs/promises";import{mkdir as li}from"fs/promises";import{dirname as ci}from"path";function di(t){if(typeof t!="object"||t===null)throw new Error("Input must be an object");let e=t;if(typeof e.file_path!="string")throw new Error('Input must have a "file_path" field of type string');if(typeof e.content!="string")throw new Error('Input must have a "content" field of type string');return{file_path:e.file_path,content:e.content}}var rr=async(t,e)=>{if(e.aborted)return{content:"Aborted",isError:!0};let{file_path:n,content:r}=di(t);try{let o=ci(n);return await li(o,{recursive:!0}),await ai(n,r,{signal:e}),{content:`Wrote ${Buffer.byteLength(r,"utf8")} bytes to ${n}`}}catch(o){return o instanceof Error?"code"in o&&o.code==="EACCES"?{content:`Permission denied: ${n}`,isError:!0}:{content:`Error writing file: ${o.message}`,isError:!0}:{content:"Unknown error writing file",isError:!0}}};import{readFile as ui,writeFile as pi}from"fs/promises";function fi(t){if(typeof t!="object"||t===null)throw new Error("Input must be an object");let e=t;if(typeof e.file_path!="string")throw new Error('Input must have a "file_path" field of type string');if(typeof e.old_string!="string")throw new Error('Input must have an "old_string" field of type string');if(typeof e.new_string!="string")throw new Error('Input must have a "new_string" field of type string');let n=!1;if(e.replace_all!==void 0){if(typeof e.replace_all!="boolean")throw new Error("replace_all must be a boolean");n=e.replace_all}return{file_path:e.file_path,old_string:e.old_string,new_string:e.new_string,replace_all:n}}function mi(t,e){if(e.length===0)return 0;let n=0,r=0;for(;(r=t.indexOf(e,r))!==-1;)n++,r+=e.length;return n}function gi(t,e,n){let r=t.split(`
|
|
1210
|
+
`),o=0,s=0;for(let c=0;c<r.length;c++){let d=r[c]?.length??0,u=o+d+1;if(o+d>=n+e.length){s=c;break}o=u}let i=Math.max(0,s-2),a=Math.min(r.length,s+3);return`...${r.slice(i,a).join(`
|
|
1211
|
+
`)}...`}var or=async(t,e)=>{if(e.aborted)return{content:"Aborted",isError:!0};let{file_path:n,old_string:r,new_string:o,replace_all:s}=fi(t);try{let i=await ui(n,"utf-8"),a=mi(i,r);if(a===0)return{content:`old_string not found in ${n}`,isError:!0};if(a>1&&!s)return{content:`old_string matches ${a} locations in ${n}. Use replace_all: true or provide more context.`,isError:!0};let l,c;s?(l=i.split(r).join(o),c=i.indexOf(r)):(c=i.indexOf(r),l=i.slice(0,c)+o+i.slice(c+r.length)),await pi(n,l,"utf-8");let d=gi(i,r,c);return{content:`${a===1?`Replaced 1 occurrence in ${n}`:`Replaced ${a} occurrences in ${n}`}
|
|
1212
|
+
|
|
1213
|
+
${d}`}}catch(i){return{content:`Error: ${i instanceof Error?i.message:String(i)}`,isError:!0}}};import{promises as ir}from"fs";import hi from"path";function yi(t,e){let n=t.replace(/\\/g,"/"),r=e.replace(/\\/g,"/");if(r.includes("**")){let s=r.split("**"),i=0;for(let a=0;a<s.length;a++){let l=s[a]??"",c=sr(l);if(a===0){let d=n.match(new RegExp(`^${c}`));if(!d)return!1;i=d[0].length}else if(a===s.length-1){let d=new RegExp(`${c}$`);if(!n.slice(i).match(d))return!1}else{let d=new RegExp(c),u=n.slice(i).match(d);if(!u)return!1;let p=u.index??0;i+=p+u[0].length}}return!0}return new RegExp(`^${sr(r)}$`).test(n)}function sr(t){return t.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]")}async function bi(t,e){let n=[];async function o(s,i){if(n.length>=500)return!0;try{let a=await ir.readdir(s,{withFileTypes:!0});for(let l of a){if(n.length>=500)return!0;let c=hi.join(s,l.name),d=i?`${i}/${l.name}`:l.name;if(yi(d,e)&&n.push(d),l.isDirectory()&&await o(c,d))return!0}}catch{}return!1}return await o(t,""),n}var ar=async(t,e)=>{if(!t||typeof t!="object")return{content:"Invalid input: expected an object",isError:!0};let n=t,r=n.pattern,o=n.path??process.cwd();if(typeof r!="string")return{content:"Invalid input: pattern must be a string",isError:!0};if(r.trim()==="")return{content:"Invalid input: pattern cannot be empty",isError:!0};if(typeof o!="string")return{content:"Invalid input: path must be a string",isError:!0};try{if(!(await ir.stat(o)).isDirectory())return{content:`Invalid input: path is not a directory: ${o}`,isError:!0};let i=await bi(o,r);if(i.length===0)return{content:`No files matched pattern '${r}' in ${o}`};let a=i.join(`
|
|
1214
|
+
`);return i.length>=500&&(a+=`
|
|
1215
|
+
[results capped at 500 entries]`),{content:a}}catch(s){return s instanceof Error?"code"in s&&s.code==="ENOENT"?{content:`Path not found: ${o}`,isError:!0}:"code"in s&&s.code==="EACCES"?{content:`Permission denied: ${o}`,isError:!0}:{content:`Error scanning directory: ${s.message}`,isError:!0}:{content:"Unknown error scanning directory",isError:!0}}};import{spawn as vi}from"child_process";function wi(t){if(typeof t!="object"||t===null)throw new Error("Input must be an object");let e=t;if(typeof e.pattern!="string")throw new Error('Input must have a "pattern" field of type string');let n=typeof e.path=="string"?e.path:process.cwd(),r;if(e.include!==void 0){if(typeof e.include!="string")throw new Error("include must be a string");r=e.include}return{pattern:e.pattern,path:n,include:r}}function ki(t){return t.replace(/\x1b\[[0-9;]*[a-zA-Z]/g,"")}var lr=async(t,e)=>{let{pattern:n,path:r,include:o}=wi(t);return e.aborted?{content:"Search aborted",isError:!0}:new Promise(s=>{let i=!1;function a(m){i||(i=!0,e.removeEventListener("abort",p),s(m))}let l=["-rn"];o&&l.push(`--include=${o}`),l.push(n,r);let c=vi("grep",l),d="",u="";c.stdout.on("data",m=>{d+=m.toString()}),c.stderr.on("data",m=>{u+=m.toString()});let p=()=>{c.kill(),a({content:"Search aborted",isError:!0})};e.addEventListener("abort",p),c.on("close",m=>{if(m===1){a({content:`No matches found for '${n}' in ${r}`});return}if(m===2){a({content:`grep error: ${u.trim()}`,isError:!0});return}let g=d.trimEnd();g=ki(g);let y=1e5;g.length>y&&(g=g.slice(0,y)+`
|
|
1216
|
+
[output truncated]`),a({content:g})}),c.on("error",m=>{a({content:`Failed to execute grep: ${m.message}`,isError:!0})})})};import{promises as Si}from"fs";var cr=async(t,e)=>{if(!t||typeof t!="object")throw new Error("Invalid input: expected an object");let r=t.path;if(typeof r!="string")throw new Error("Invalid input: path must be a string");try{let o=await Si.readdir(r,{withFileTypes:!0}),s=o.filter(c=>c.isDirectory()).map(c=>`${c.name}/`),i=o.filter(c=>!c.isDirectory()).map(c=>c.name);s.sort(),i.sort();let a=[...s,...i];return a.length===0?{content:"(empty directory)"}:{content:a.join(`
|
|
1217
|
+
`)}}catch(o){if(o instanceof Error){let s=o;return s.code==="ENOENT"?{content:`Directory not found: ${r}`,isError:!0}:s.code==="ENOTDIR"?{content:`Not a directory: ${r}`,isError:!0}:s.code==="EACCES"?{content:`Permission denied: ${r}`,isError:!0}:{content:`Error listing directory: ${o.message}`,isError:!0}}return{content:"Unknown error listing directory",isError:!0}}};function ze(){return new Map([["bash",tr],["read_file",nr],["write_file",rr],["edit_file",or],["glob",ar],["grep",lr],["list_directory",cr]])}var dr=`You have access to tools for working with the filesystem and running commands. Follow these conventions:
|
|
1218
|
+
|
|
1219
|
+
- Use read_file before editing to verify the exact content you want to change.
|
|
1220
|
+
- Prefer edit_file over write_file for modifying existing files \u2014 write_file is for new files or complete rewrites.
|
|
1221
|
+
- Quote file paths that contain spaces with double quotes.
|
|
1222
|
+
- Do not run destructive shell commands (rm -rf, git reset --hard, etc.) unless the user explicitly asks.
|
|
1223
|
+
- Use glob and grep to discover files before reading individual files.
|
|
1224
|
+
- When bash output is very long, it may be truncated. If you need the full output, redirect to a file and read it.
|
|
1225
|
+
- Use absolute paths for file operations.
|
|
1226
|
+
|
|
1227
|
+
When you see a \`<command-name>\` tag in the current conversation turn, the skill has ALREADY been loaded by the user typing a slash command. Do NOT re-invoke the skill tool to dispatch the same skill again. Instead, treat the \`<command-message>\` as the skill name and \`<command-args>\` as its arguments, then follow the instructions in the body block immediately following the tag.`;var ur="anthropic-direct",Pi=8192,Ei="claude-sonnet-4-5-20250929",Ai=null;var Pe=class{name=ur;tools;providerFactory;skillExecutor;constructor(e={}){let n=[...de];e.subagentExecutor&&n.push(Rn),e.skillExecutor&&n.push(_n),this.tools=e.tools??new ie({handlers:ze(),schemas:n,hookRegistry:e.hookRegistry,permissions:e.permissions,subagentExecutor:e.subagentExecutor,skillExecutor:e.skillExecutor}),this.skillExecutor=e.skillExecutor,e.clientFactory&&(this.providerFactory=e.clientFactory)}query(e){let n=e.config,r=n.apiKey&&n.apiKey.length>0?n.apiKey:process.env.ANTHROPIC_API_KEY||process.env.CLAUDE_CODE_OAUTH_TOKEN||"";if(!r||r.length===0)throw new Error(`${ur} provider requires config.apiKey (resolved from ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN)`);let o=At(r),s=Tt(r,o),i=this.providerFactory??Ai,a=i?i(s):new xi(s),l=Ct(o),c=Ti(n.systemPrompt),d=typeof n.model=="string"&&n.model.length>0?Se(n.model)??n.model:Ei,u=Ci(n),p=this.tools instanceof ie?[...this.tools.toolDefs]:[...de],m=[...Q(je()),...Q()],g=n.plugins??[],y=[...m,...g],f=this.skillExecutor?Zn(y):"",v=[dr];f.length>0&&v.push(f),c&&v.push(c);let x=v.join(`
|
|
1228
|
+
|
|
1229
|
+
`);return new Ge({client:a,authMode:o,promptStream:e.prompt,toolDispatcher:this.tools,model:d,...n.permissionMode!==void 0?{permissionMode:n.permissionMode}:{},maxTokens:u,tools:p,userSystem:x,systemPrefix:l})}};function Ti(t){if(t===void 0)return null;if(typeof t=="string")return t.length>0?t:null;if(typeof t=="object"&&t!==null&&"append"in t){let e=t.append;return e&&e.length>0?e:null}return null}function Ci(t){let e=t.maxOutputTokens;return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.floor(e):Pi}var pr=new Pe;import{Codex as mr}from"@openai/codex-sdk";import{mkdtempSync as Ii,rmSync as Mi,writeFileSync as Ri}from"node:fs";import{tmpdir as _i}from"node:os";import{join as fr}from"node:path";var Ae="openai-codex",Di=[{value:"gpt-5.4",displayName:"GPT-5.4",description:"Codex default"},{value:"gpt-5.4-mini",displayName:"GPT-5.4 mini",description:"Faster, cheaper Codex variant"}];function Oi(t){let e=[];if(t.continue&&e.push("continue"),t.resumeSessionAt!==void 0&&e.push("resumeSessionAt"),t.forkSession&&e.push("forkSession"),t.persistSession===!1&&e.push("persistSession=false"),t.enableFileCheckpointing&&e.push("enableFileCheckpointing"),t.thinking!==void 0&&e.push("thinking"),t.maxBudgetUsd!==void 0&&e.push("maxBudgetUsd"),t.taskBudget!==void 0&&e.push("taskBudget"),t.plugins&&t.plugins.length>0&&e.push("plugins"),t.agents&&e.push("agents"),t.agent!==void 0&&e.push("agent"),t.onElicitation&&e.push("onElicitation"),t.hooks&&e.push("hooks"),t.canUseTool&&e.push("canUseTool"),t.mcpServers&&e.push("mcpServers"),t.includeHookEvents&&e.push("includeHookEvents"),t.agentProgressSummaries&&e.push("agentProgressSummaries"),t.includePartialMessages&&e.push("includePartialMessages"),e.length>0)throw new ge(Ae,e.join(", "),`${Ae} provider does not support AgentConfig fields: ${e.join(", ")}`)}function gr(t){return t==="plan"?{sandboxMode:"read-only",approvalPolicy:"untrusted"}:{sandboxMode:"workspace-write",approvalPolicy:"never"}}function $i(t){if(t)switch(t){case"minimal":case"low":case"medium":case"high":case"xhigh":return t;case"max":return"xhigh";default:return}}function Fi(t){let e=t.systemPrompt;if(e!==void 0){if(typeof e=="string")return e.length>0?e:void 0;if(typeof e=="object"&&e!==null&&"append"in e){let n=e.append;return n&&n.length>0?n:void 0}}}function ji(t){let e=Ii(fr(_i(),"afk-codex-instr-")),n=fr(e,"instructions.md");return Ri(n,t,"utf-8"),{path:n,dispose:()=>{try{Mi(e,{recursive:!0,force:!0})}catch{}}}}function Hi(t){if(t.apiKey)return t.apiKey;let e=process.env.OPENAI_API_KEY??process.env.CODEX_API_KEY;return e&&e.length>0?e:void 0}function*Li(t,e,n,r){if(t.type!=="thread.started"&&t.type!=="turn.started"){if(t.type==="turn.completed"){let o=t.usage;yield{type:"turn.completed",usage:{inputTokens:o.input_tokens,outputTokens:o.output_tokens,cachedInputTokens:o.cached_input_tokens,totalTokens:o.input_tokens+o.output_tokens+o.cached_input_tokens,resultSubtype:"success",isError:!1,raw:{input_tokens:o.input_tokens,output_tokens:o.output_tokens,cached_input_tokens:o.cached_input_tokens}},...e!==void 0?{sessionId:e}:{}};return}if(t.type==="turn.failed"){yield{type:"error",error:new Error(t.error.message)};return}if(t.type==="error"){yield{type:"error",error:new Error(t.message)};return}(t.type==="item.started"||t.type==="item.updated"||t.type==="item.completed")&&(yield*Ui(t.item,t.type==="item.completed",e,n,r))}}function*Ui(t,e,n,r,o){if(t.type==="agent_message"){let s=r.get(t.id)??"";if(t.text!==s){let i=t.text.startsWith(s)?t.text.slice(s.length):t.text;r.set(t.id,t.text),i.length>0&&(yield{type:"delta.text",text:i,...n!==void 0?{sessionId:n}:{}})}e&&(yield{type:"assistant.message",text:t.text,...n!==void 0?{sessionId:n}:{}},r.delete(t.id));return}if(t.type==="reasoning"){let s=o.get(t.id)??"";if(t.text!==s){let i=t.text.startsWith(s)?t.text.slice(s.length):t.text;o.set(t.id,t.text),i.length>0&&(yield{type:"delta.reasoning",text:i,...n!==void 0?{sessionId:n}:{}})}e&&o.delete(t.id);return}if(t.type==="command_execution"){if(e){let s=t.status==="failed",i=t.exit_code!==void 0?` (exit ${t.exit_code})`:"",a=`$ ${t.command}${i}
|
|
1230
|
+
`+(t.aggregated_output??"");yield{type:"tool.output",toolUseId:t.id,content:a,isError:s,...n!==void 0?{sessionId:n}:{}}}return}if(t.type==="file_change"){if(e){let s=t.changes.map(i=>`${i.kind} ${i.path}`).join(`
|
|
1231
|
+
`);yield{type:"tool.output",toolUseId:t.id,content:s,isError:t.status==="failed",...n!==void 0?{sessionId:n}:{}}}return}if(t.type==="mcp_tool_call"){if(e){let s=t.status==="failed",i;t.error?i=t.error.message:t.result?i=JSON.stringify(t.result,null,2):i="",yield{type:"tool.output",toolUseId:t.id,content:i,isError:s,...n!==void 0?{sessionId:n}:{}}}return}if(t.type==="web_search"){e&&(yield{type:"tool.output",toolUseId:t.id,content:`web_search: ${t.query}`,...n!==void 0?{sessionId:n}:{}});return}if(t.type!=="todo_list"&&t.type==="error"){e&&(yield{type:"error",error:new Error(t.message)});return}}var mt=class{startOpts;promptStream;codex;thread;currentModel;currentSandbox;currentApproval;abortController=null;pendingAbortReason=null;closed=!1;initSessionId;dispose;closeResolve=null;closedPromise;constructor(e,n,r){this.startOpts=e,this.promptStream=n,this.initSessionId=r,this.codex=new mr(e.codexOptions),this.thread=e.resumeId?this.codex.resumeThread(e.resumeId,e.threadOptions):this.codex.startThread(e.threadOptions),this.currentModel=e.threadOptions.model,this.currentSandbox=e.threadOptions.sandboxMode??"workspace-write",this.currentApproval=e.threadOptions.approvalPolicy??"never",e.instructionsDispose!==void 0&&(this.dispose=e.instructionsDispose),this.closedPromise=new Promise(o=>{this.closeResolve=()=>o("__closed__")})}async*[Symbol.asyncIterator](){yield{type:"session.init",info:{sessionId:this.initSessionId,...this.currentModel!==void 0?{model:this.currentModel}:{},permissionMode:this.sandboxToPermissionMode(),cwd:this.startOpts.threadOptions.workingDirectory??process.cwd(),tools:["Bash","Read","Write","Edit"],slashCommands:[],skills:[],plugins:[],mcpServers:[],apiKeySource:this.startOpts.codexOptions.apiKey!==void 0?"apiKey":"codex-cli",version:"codex-sdk"}};let n=new Map,r=new Map,o=this.initSessionId,s=this.promptStream[Symbol.asyncIterator]();try{for(;!this.closed;){let i=await Promise.race([s.next(),this.closedPromise]);if(i==="__closed__")break;let a=i;if(a.done)break;let l=a.value,c=new AbortController;if(this.abortController=c,this.pendingAbortReason!==null&&!c.signal.aborted&&(c.abort(this.pendingAbortReason),this.pendingAbortReason=null),c.signal.aborted)return;let d;try{let u=typeof l.content=="string"?l.content:l.content.map(p=>{if(typeof p=="object"&&p&&"type"in p){if(p.type==="text")return p.text;if(p.type==="image")return"[image omitted]"}return""}).join(`
|
|
1232
|
+
`);d=await this.thread.runStreamed(u,{signal:c.signal})}catch(u){if(c.signal.aborted)return;yield{type:"error",error:u instanceof Error?u:new Error(String(u))};return}try{for await(let u of d.events){if(this.closed)return;u.type==="thread.started"&&(o=u.thread_id),yield*Li(u,o,n,r)}}catch(u){if(c.signal.aborted)return;yield{type:"error",error:u instanceof Error?u:new Error(String(u))};return}finally{this.abortController===c&&(this.abortController=null)}}}catch(i){yield{type:"error",error:i instanceof Error?i:new Error(String(i))}}finally{try{await s.return?.()}catch{}}}sandboxToPermissionMode(){return this.currentSandbox==="read-only"||this.currentApproval==="untrusted"?"plan":"bypassPermissions"}async interrupt(){let e=this.abortController;if(e&&!e.signal.aborted){e.abort("interrupted");return}this.pendingAbortReason="interrupted"}async setModel(e){this.currentModel=e;let n={...this.startOpts.threadOptions,...e!==void 0?{model:e}:{},sandboxMode:this.currentSandbox,approvalPolicy:this.currentApproval},r=this.thread.id;this.thread=r?this.codex.resumeThread(r,n):this.codex.startThread(n),this.startOpts.threadOptions=n}async setPermissionMode(e){let{sandboxMode:n,approvalPolicy:r}=gr(e);this.currentSandbox=n,this.currentApproval=r;let o={...this.startOpts.threadOptions,sandboxMode:n,approvalPolicy:r},s=this.thread.id;this.thread=s?this.codex.resumeThread(s,o):this.codex.startThread(o),this.startOpts.threadOptions=o}async supportedCommands(){return[]}async supportedModels(){return Di.map(e=>({...e}))}async supportedAgents(){return[]}async getContextUsage(){return{tools:[],agents:[],isAutoCompactEnabled:!1,apiUsage:null}}async mcpServerStatus(){return[]}async accountInfo(){return{}}async rewindFiles(e,n){throw new ge(Ae,"rewindFiles",`${Ae} provider does not support file checkpoint rewind.`)}close(){this.closed=!0;let e=this.abortController;e&&!e.signal.aborted?e.abort("closed"):this.pendingAbortReason="closed",this.closeResolve?.(),this.dispose?.()}getThread(){return this.thread}},Ni=null;var fe=class{name=Ae;query(e){Oi(e.config);let n=Hi(e.config),r=$i(e.config.effort),{sandboxMode:o,approvalPolicy:s}=gr(e.config.permissionMode),i={...e.config.model!==void 0?{model:e.config.model}:{},sandboxMode:o,approvalPolicy:s,...r!==void 0?{modelReasoningEffort:r}:{},skipGitRepoCheck:!0,workingDirectory:process.cwd()},a=Fi(e.config),l={};n!==void 0&&(l.apiKey=n);let c;if(a!==void 0){let{path:g,dispose:y}=ji(a);l.config={...l.config??{},model_instructions_file:g},c=y}A(`\u{1F7E2} OpenAICodexProvider: creating Codex thread (model=${String(e.config.model)}, sandbox=${o}, approval=${s})`);let d=Ni,u=d??(g=>new mr(g)),p=`codex-pending-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,m=new mt({threadOptions:i,codexOptions:l,...e.config.resume!==void 0?{resumeId:e.config.resume}:{},...c!==void 0?{instructionsDispose:c}:{}},e.prompt,p);if(d){let g=u(l);m.codex=g,m.thread=e.config.resume?g.resumeThread(e.config.resume,i):g.startThread(i)}return m}},qe=new fe;var Bi=new Set(["opus","opus_1m","sonnet","sonnet_1m","haiku","auto"]);function xe(t){if(!t)return"anthropic-direct";let e=t.trim().toLowerCase();return!e||Bi.has(e)||e.startsWith("claude-")||e.startsWith("claude_")?"anthropic-direct":e.startsWith("gpt-")||e.startsWith("gpt_")||e.startsWith("o1")||e.startsWith("o3")||e.startsWith("o4")||e.startsWith("codex-")||e.startsWith("codex_")||e==="codex"?"openai-codex":"anthropic-direct"}function Ve(t){return xe(t)==="openai-codex"?qe:pr}async function hr(t,e,n={}){t&&await t.dispatch(e,n.signal)}async function yr(t,e,n={}){if(t)try{await t.dispatch(e,n.signal)}catch(r){if(r instanceof U||r instanceof B){A(`SessionEnd hook swallowed ${r.name}: ${r.message}`),n.onError?.(r);return}A(`SessionEnd hook unexpected error: ${String(r)}`),n.onError?.(r instanceof Error?r:new Error(String(r)))}}var Ke=class{pendingResolve=null;bufferedMessage=null;getSessionId;constructor(e){this.getSessionId=e}pushUserMessage(e){if(this.pendingResolve){let n=this.pendingResolve;this.pendingResolve=null;let r=this.getSessionId();n({content:e,...r!==void 0?{sessionId:r}:{}});return}this.bufferedMessage=e}createIterable(){let e=this;return{[Symbol.asyncIterator](){return{next(){if(e.bufferedMessage!==null){let n=e.bufferedMessage;e.bufferedMessage=null;let r=e.getSessionId();return Promise.resolve({value:{content:n,...r!==void 0?{sessionId:r}:{}},done:!1})}return new Promise(n=>{e.pendingResolve=r=>n({value:r,done:!1})})},return(){return Promise.resolve({value:void 0,done:!0})}}}}}};function Gi(t){let e=/Output too large \((\d+(?:\.\d+)?)\s*(B|KB|MB|GB)\)\.\s*Full output saved to:\s*(\/[^\n]+)/,n=t.match(e);if(!n||!n[1]||!n[2]||!n[3])return null;let r=n[1],o=n[2],s=n[3],i=parseFloat(r),a=i;o==="KB"?a=i*1024:o==="MB"?a=i*1024*1024:o==="GB"&&(a=i*1024*1024*1024);let l=r;return i%1===0&&(l=String(Math.floor(i))),l+=o,{sizeLabel:l,sizeBytes:Math.round(a),absolutePath:s.trim()}}function zi(t){if(t<1024)return`${t}B`;let e=t/1024;if(e<1024)return e%1===0?`${Math.floor(e)}KB`:`${e.toFixed(1)}KB`;let n=e/1024;if(n<1024)return n%1===0?`${Math.floor(n)}MB`:`${n.toFixed(1)}MB`;let r=n/1024;return r%1===0?`${Math.floor(r)}GB`:`${r.toFixed(1)}GB`}function qi(t){let e=Buffer.byteLength(t,"utf8"),n=zi(e),r=t.split(`
|
|
1233
|
+
`);if(r.length<=1&&t.length<=80)return{content:t,truncated:!1,sizeBytes:e,sizeLabel:n};if(r.length<=1)return t.length<=80?{content:t,truncated:!1,sizeBytes:e,sizeLabel:n}:{content:t.substring(0,80)+"\u2026",truncated:!0,sizeBytes:e,sizeLabel:n};if(t.length<=80)return{content:t,truncated:!1,sizeBytes:e,sizeLabel:n};let o=r[0]??"",s=o;return o.length>80&&(s=o.substring(0,80)+"\u2026"),{content:s+`\u2026+${r.length} lines`,truncated:!0,lineCount:r.length,sizeBytes:e,sizeLabel:n}}function Vi(t,e){let n={...t.raw??{}};return t.inputTokens!==void 0&&(n.input_tokens=t.inputTokens),t.outputTokens!==void 0&&(n.output_tokens=t.outputTokens),t.cachedInputTokens!==void 0&&(n.cache_read_input_tokens=t.cachedInputTokens),t.cacheCreationTokens!==void 0&&(n.cache_creation_input_tokens=t.cacheCreationTokens),t.totalTokens!==void 0&&(n.total_tokens=t.totalTokens),{sessionId:e,stopReason:t.stopReason??void 0,resultSubtype:t.resultSubtype,durationMs:t.durationMs,durationApiMs:t.durationApiMs,totalCostUsd:t.totalCostUsd,isError:t.isError,usage:Object.keys(n).length>0?n:void 0,modelUsage:t.modelUsage,permissionDenials:t.permissionDenials,errors:t.errors}}function Ki(t,e){let n=e.info;t.setSessionMetadata(r=>({...r,sessionId:n.sessionId,model:n.model??r.model,...n.permissionMode!==void 0?{permissionMode:n.permissionMode}:{},...n.cwd!==void 0?{cwd:n.cwd}:{},tools:n.tools?[...n.tools]:r.tools,slashCommands:n.slashCommands?[...n.slashCommands]:r.slashCommands,skills:n.skills?[...n.skills]:r.skills,plugins:n.plugins?n.plugins.map(o=>({...o})):r.plugins,mcpServers:n.mcpServers?n.mcpServers.map(o=>({...o})):r.mcpServers,...n.apiKeySource!==void 0?{apiKeySource:n.apiKeySource}:{},...n.version!==void 0?{claudeCodeVersion:n.version}:{},...n.outputStyle!==void 0?{outputStyle:n.outputStyle}:{}})),t.updateSessionIdentity(n.sessionId),t.resolveInitialization()}function Wi(t,e){t.setSessionMetadata(n=>({...n,sessionId:e.sessionId,...e.permissionMode!==void 0?{permissionMode:e.permissionMode}:{permissionMode:n.permissionMode},...e.status!==void 0?{status:e.status}:{}}))}function Yi(t,e){let n=Gi(e.content);if(n){t.push({type:"chunk",chunk:{type:"tool_result",toolUseId:e.toolUseId,content:`Output persisted (${n.sizeLabel}) \u2192 ${n.absolutePath}`,isError:e.isError===!0,persistedPath:n.absolutePath,sizeBytes:n.sizeBytes,sizeLabel:n.sizeLabel}});return}let{content:r,truncated:o,lineCount:s,sizeBytes:i,sizeLabel:a}=qi(e.content);t.push({type:"chunk",chunk:{type:"tool_result",toolUseId:e.toolUseId,content:r,isError:e.isError===!0,sizeBytes:i,sizeLabel:a,...o&&{truncated:o},...s!==void 0&&{lineCount:s}}})}function Qi(t,e){if(!e)return;let n={role:"assistant",content:e,timestamp:new Date};t.conversationHistory.push(n),t.messageQueue.push({type:"message",message:n})}async function br(t){try{for await(let e of t.providerStream)switch(e.type){case"session.init":Ki(t,e);break;case"session.status":Wi(t,e);break;case"delta.text":t.messageQueue.push({type:"chunk",chunk:{type:"content",content:e.text,metadata:{eventType:"delta",deltaType:"text_delta"}}});break;case"delta.reasoning":t.messageQueue.push({type:"chunk",chunk:{type:"thinking",content:e.text,metadata:{eventType:"delta",deltaType:"thinking_delta"}}});break;case"assistant.message":e.sessionId&&t.updateSessionIdentity(e.sessionId),Qi(t,e.text);break;case"tool.use.start":t.messageQueue.push({type:"chunk",chunk:{type:"tool_use_detail",toolUseId:e.toolUseId,toolName:e.toolName,toolInput:e.toolInput}});break;case"tool.use":t.messageQueue.push({type:"chunk",chunk:{type:"tool_use",content:e.summary,metadata:{eventType:"tool_use_summary",precedingToolUseIds:e.toolUseIds}}});break;case"tool.output":Yi(t.messageQueue,e);break;case"progress":t.messageQueue.push({type:"progress",progress:{taskId:e.progress.taskId,description:e.progress.description,...e.progress.summary!==void 0?{summary:e.progress.summary}:{},...e.progress.lastToolName!==void 0?{lastToolName:e.progress.lastToolName}:{},totalTokens:e.progress.totalTokens,toolUses:e.progress.toolUses,durationMs:e.progress.durationMs}});break;case"suggestion":t.messageQueue.push({type:"suggestion",suggestion:e.suggestion});break;case"turn.completed":let n=Vi(e.usage,e.sessionId??t.getSessionMetadata().sessionId);t.setLastResponseMetadata(n);for(let r=t.conversationHistory.length-1;r>=0;r--){let o=t.conversationHistory[r];if(o?.role==="assistant"){o.metadata=n;break}}t.messageQueue.push({type:"done",metadata:n});break;case"error":throw e.error}t.resolveInitializationIfNeeded(),t.messageQueue.complete()}catch(e){let n=e instanceof Error?e:new Error(String(e));throw t.resolveInitializationIfNeeded(),t.messageQueue.fail(n),n}}function vr(t,e,n){t&&(t.aborted?e.abort(t.reason):t.addEventListener("abort",()=>{e.signal.aborted||e.abort(t.reason)},{once:!0})),e.signal.addEventListener("abort",n,{once:!0})}function wr(t,e){let n=t.permissionMode??"bypassPermissions",r=t.persistSession??!0,o={sessionId:t.sessionId,configuredSessionId:t.sessionId,resume:t.resume,resumeSessionAt:t.resumeSessionAt,continue:t.continue,forkSession:t.forkSession,persistSession:r},s={sessionId:t.sessionId,model:e,permissionMode:n};return{sessionIdentity:o,metadata:s}}async function kr(t){try{await hr(t.hookRegistry,{event:"SessionStart",sessionId:t.sessionId()},{signal:t.abortSignal}),await br({providerStream:t.providerQuery,messageQueue:t.messageQueue,conversationHistory:t.conversationHistory,getSessionMetadata:()=>t.stateManager.getSessionMetadata(),setSessionMetadata:e=>t.stateManager.setSessionMetadata(e),updateSessionIdentity:e=>t.stateManager.updateSessionIdentity(e),resolveInitialization:()=>t.stateManager.resolveInitializationOnce(),resolveInitializationIfNeeded:()=>t.stateManager.resolveInitializationIfNeeded(),setLastResponseMetadata:t.setLastResponseMetadata})}catch(e){let n=e instanceof Error?e:new Error(String(e));t.stateManager.isInitializationSettled()||t.stateManager.rejectInitializationOnce(n),await t.dispatchEnd("error").catch(()=>{})}}var We=class{initializationPromise;resolveInitialization;rejectInitialization;initializationSettled=!1;sessionMetadata;sessionIdentity;constructor(e,n){this.sessionIdentity=e,this.sessionMetadata=n,this.initializationPromise=new Promise((r,o)=>{this.resolveInitialization=r,this.rejectInitialization=o})}waitForInitialization(){return this.initializationPromise}getSessionIdentity(){return{...this.sessionIdentity,sessionId:this.getSessionId()}}getSessionMetadata(){return{...this.sessionMetadata,sessionId:this.getSessionId()}}getSessionId(){return this.sessionMetadata.sessionId??this.sessionIdentity.sessionId}updateSessionIdentity(e){e&&(this.sessionIdentity={...this.sessionIdentity,sessionId:e},this.sessionMetadata={...this.sessionMetadata,sessionId:e})}setSessionMetadata(e){this.sessionMetadata=e(this.sessionMetadata)}resolveInitializationIfNeeded(){this.initializationSettled||(this.initializationSettled=!0,this.resolveInitialization(this.getSessionMetadata()))}resolveInitializationOnce(){this.initializationSettled||(this.initializationSettled=!0,this.resolveInitialization(this.getSessionMetadata()))}rejectInitializationOnce(e){this.initializationSettled||(this.initializationSettled=!0,this.rejectInitialization(e))}isInitializationSettled(){return this.initializationSettled}};async function Sr(t,e){return await new Promise((n,r)=>{let o=null,s="",i=!1,a=c=>{i||(i=!0,clearTimeout(l),c())},l=Number.isFinite(e)&&e>0?setTimeout(()=>{a(()=>r(new Error("Response timeout")))},e):void 0;(async()=>{try{for await(let c of t){if(process.env.AFK_CODEX_DEBUG&&console.log("[wait] got event:",c.type),c.type==="error"){a(()=>r(c.error));return}if(c.type==="chunk"&&c.chunk.type==="content"&&(s+=c.chunk.content),c.type==="message"&&c.message.role==="assistant"&&(o=c.message),c.type==="done"){if(process.env.AFK_CODEX_DEBUG&&console.log("[wait] settling with done; assistantMessage=",!!o,"streamedContent=",s.length),o){let d=o;a(()=>n({...d,metadata:c.metadata}));return}if(s){a(()=>n({role:"assistant",content:s,metadata:c.metadata,timestamp:new Date}));return}}}a(o?()=>n(o):s?()=>n({role:"assistant",content:s,timestamp:new Date}):()=>r(new Error("No assistant response received")))}catch(c){a(()=>r(c instanceof Error?c:new Error(String(c))))}})()})}var ne=class{config;currentState="idle";messageQueue;providerQuery;conversationHistory=[];turnCount=0;lastResponseMetadata=null;processingPromise=null;inputStream;abortController;hookRegistry;sessionEndDispatched=!1;stateManager;constructor(e){this.config=e,this.abortController=new AbortController,this.hookRegistry=e.hookRegistry,vr(e.abortSignal,this.abortController,()=>{this.onAbort()}),this.initSdkLifecycle()}initSdkLifecycle(){this.messageQueue=new ae;let e=Se(this.config.model)??this.config.model,{sessionIdentity:n,metadata:r}=wr(this.config,e);this.stateManager=new We(n,r),this.inputStream=new Ke(()=>this.sessionId);let o=this.config.provider??Ve(e);A(`\u{1F7E2} AgentSession: Creating query session via provider=${o.name}`),this.providerQuery=o.query({prompt:this.inputStream.createIterable(),config:this.config}),this.conversationHistory=[],this.turnCount=0,this.lastResponseMetadata=null,this.sessionEndDispatched=!1,this.currentState="idle",this.processingPromise=kr({providerQuery:this.providerQuery,messageQueue:this.messageQueue,conversationHistory:this.conversationHistory,stateManager:this.stateManager,hookRegistry:this.hookRegistry,abortSignal:this.abortController.signal,sessionId:()=>this.sessionId,setLastResponseMetadata:s=>this.lastResponseMetadata=s,dispatchEnd:s=>this.dispatchSessionEndOnce(s)})}get state(){return this.currentState}get sessionId(){return this.stateManager.getSessionId()}get abortSignal(){return this.abortController.signal}async sendMessage(e,n={}){this.assertCanSend(),this.currentState=n.stream?"streaming":"processing";let r={role:"user",content:e,timestamp:new Date};this.conversationHistory.push(r);let o=this.config.timeoutMs??ye;try{this.inputStream.pushUserMessage(e);let s=await be(Sr(this.messageQueue,o),o,{controller:this.abortController,label:this.sessionId??"session"});return this.turnCount++,s}finally{this.state!=="closed"&&(this.currentState="idle")}}async*sendMessageStream(e){this.assertCanSend(),this.currentState="streaming";let r={role:"user",content:typeof e=="string"?e:this.summarizeContentBlocks(e),timestamp:new Date};this.conversationHistory.push(r),this.inputStream.pushUserMessage(e);try{for await(let o of this.messageQueue)if(o.type==="done"&&this.turnCount++,yield o,o.type==="done"||o.type==="error")break}finally{this.state!=="closed"&&(this.currentState="idle")}}summarizeContentBlocks(e){let n=[],r=0;for(let s of e)s.type==="text"?n.push(s.text):s.type==="image"&&r++;let o=n.join(" ");return r>0&&(o=o?`${o} [+ ${r} image(s)]`:`[+ ${r} image(s)]`),o||"[content block(s)]"}async interrupt(){this.currentState!=="streaming"&&this.currentState!=="processing"||(this.currentState="idle",await this.providerQuery.interrupt())}async reset(){if(this.currentState==="closed")throw new Error("Cannot reset: session is closed");if(this.abortController.signal.aborted)throw new B("Cannot reset: session aborted");if(this.currentState==="processing"||this.currentState==="streaming")try{await this.providerQuery.interrupt()}catch{}await this.dispatchSessionEndOnce("reset");try{await this.providerQuery.close()}catch{}this.processingPromise&&await Promise.race([this.processingPromise,new Promise(e=>setTimeout(e,Oe))]).catch(()=>{}),this.messageQueue.complete(),this.stateManager.resolveInitializationIfNeeded();try{this.initSdkLifecycle()}catch(e){throw this.currentState="closed",new Error(`Session reset failed during lifecycle rebuild: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}async onAbort(){try{await this.providerQuery.interrupt()}catch{}}async setModel(e){let n=Se(e),r=this.stateManager.getSessionMetadata();await this.providerQuery.setModel(n??r.model??""),n&&this.stateManager.setSessionMetadata(o=>({...o,model:n}))}async setPermissionMode(e){await this.providerQuery.setPermissionMode(e),this.stateManager.setSessionMetadata(n=>({...n,permissionMode:e}))}waitForInitialization(){return this.stateManager.waitForInitialization()}getSessionIdentity(){return this.stateManager.getSessionIdentity()}getSessionMetadata(){return this.stateManager.getSessionMetadata()}getQuery(){return this.providerQuery}supportedCommands(){return this.providerQuery.supportedCommands()}supportedModels(){return this.providerQuery.supportedModels()}supportedAgents(){return this.providerQuery.supportedAgents()}getContextUsage(){return this.providerQuery.getContextUsage()}mcpServerStatus(){return this.providerQuery.mcpServerStatus()}accountInfo(){return this.providerQuery.accountInfo()}rewindFiles(e,n){return this.providerQuery.rewindFiles(e,n)}async compact(){if(this.currentState==="closed")throw new Error("Cannot compact: session is closed");if(this.currentState!=="idle")return{compacted:!1,reason:"session-busy",messagesBefore:0,messagesAfter:0};let e=this.providerQuery.compact?.bind(this.providerQuery);return e?e():{compacted:!1,reason:"not-supported",messagesBefore:0,messagesAfter:0}}getLastResponseMetadata(){return this.lastResponseMetadata}getOutputStream(){return this.messageQueue}getInputStreamRef(){return{pushUserMessage:e=>this.inputStream.pushUserMessage(e)}}getHistory(){return[...this.conversationHistory]}getTurnCount(){return this.turnCount}async close(){if(this.currentState!=="closed"){this.currentState="closed",this.abortController.signal.aborted||this.abortController.abort("closed"),this.stateManager.resolveInitializationIfNeeded();try{this.providerQuery.close()}catch{}if(this.processingPromise)try{await Promise.race([this.processingPromise,new Promise(e=>setTimeout(e,Oe))])}catch{}this.messageQueue.complete(),await this.dispatchSessionEndOnce("close")}}async dispatchSessionEndOnce(e){this.sessionEndDispatched||(this.sessionEndDispatched=!0,await yr(this.hookRegistry,{event:"SessionEnd",sessionId:this.sessionId,reason:e}))}assertCanSend(){if(this.currentState==="closed")throw new Error("Cannot send message: session is closed");if(this.abortController.signal.aborted)throw new B("Cannot send message: session aborted");if(this.currentState==="processing"||this.currentState==="streaming")throw new Error("Cannot send message: session is busy");if(this.config.maxTurns&&this.turnCount>=this.config.maxTurns)throw new Error(`Maximum turns (${this.config.maxTurns}) exceeded`)}};function Ji(t){let e=new Map,n=t.defaultMode??"ask";if(t.tools)for(let[r,o]of Object.entries(t.tools))typeof o=="string"?e.set(r,{mode:o}):e.set(r,{mode:o.mode,reason:o.reason});if(t.list)for(let r of t.list)e.set(r.tool,{mode:r.mode,reason:r.reason});return{defaultMode:n,byTool:e}}function Xi(t){return t.behavior==="allow"?{behavior:"allow"}:{behavior:"deny",message:t.reason??"Tool denied by permission rules"}}function xr(t){let{defaultMode:e,byTool:n}=Ji(t.rules);return async(r,o)=>{let s={toolName:r,input:o},i=n.get(r),a=i?.mode??e,l=i?.reason,c;return a==="ask"&&t.onAsk?c=await t.onAsk(s):a==="deny"?c={behavior:"deny",reason:l}:c={behavior:"allow"},t.onDecision?.(s,c),Xi(c)}}import{Telegraf as oa}from"telegraf";import{promises as Te}from"fs";import{join as Pr}from"path";var me=class{sessions=new Map;sessionData=new Map;options;constructor(e){this.options={dataDir:e.dataDir||"./data/telegram-sessions",defaultModel:e.defaultModel||"sonnet",apiKey:e.apiKey,settingSources:e.settingSources,thinking:e.thinking,effort:e.effort,createSession:e.createSession}}getSessionIfExists(e){return this.sessions.get(e)}async getSession(e){let n=this.sessions.get(e);if(!n){let o=this.sessionData.get(e)||{chatId:e,model:this.options.defaultModel,createdAt:new Date().toISOString(),lastActivity:new Date().toISOString()},s={model:o.model,apiKey:this.options.apiKey};this.options.settingSources?.length&&(s.settingSources=this.options.settingSources),this.options.thinking!==void 0&&(s.thinking=this.options.thinking),this.options.effort!==void 0&&(s.effort=this.options.effort),n=await this.options.createSession(s),this.sessions.set(e,n),this.sessionData.set(e,o)}let r=this.sessionData.get(e);return r&&(r.lastActivity=new Date().toISOString()),n}async resetSession(e){let n=this.sessions.get(e);n&&(await n.close(),this.sessions.delete(e));let r=this.sessionData.get(e);r&&(r.lastActivity=new Date().toISOString())}async switchModel(e,n){let r=this.sessions.get(e);r&&(await r.close(),this.sessions.delete(e));let o=this.sessionData.get(e);o?(o.model=n,o.lastActivity=new Date().toISOString()):(o={chatId:e,model:n,createdAt:new Date().toISOString(),lastActivity:new Date().toISOString()},this.sessionData.set(e,o))}getModel(e){return this.sessionData.get(e)?.model||this.options.defaultModel}async loadSessions(){try{await Te.mkdir(this.options.dataDir,{recursive:!0});let e=await Te.readdir(this.options.dataDir);for(let n of e)if(n.endsWith(".json")){let r=Pr(this.options.dataDir,n),o=await Te.readFile(r,"utf-8"),s=JSON.parse(o);this.sessionData.set(s.chatId,s)}}catch(e){e.code!=="ENOENT"&&console.error("Failed to load sessions:",e)}}async saveSessions(){try{await Te.mkdir(this.options.dataDir,{recursive:!0});for(let[e,n]of this.sessionData.entries()){let r=Pr(this.options.dataDir,`${e}.json`);await Te.writeFile(r,JSON.stringify(n,null,2))}}catch(e){console.error("Failed to save sessions:",e)}}async closeAll(){await this.saveSessions();let e=Array.from(this.sessions.values()).map(n=>n.close().catch(r=>console.error("Error closing session:",r)));await Promise.all(e),this.sessions.clear()}getSessionCount(){return this.sessions.size}getChatCount(){return this.sessionData.size}};function Ce(t,e=4096){if(t.length<=e)return[t];let n=[],r=t;for(;r.length>0;){if(r.length<=e){n.push(r);break}let o=e,s=r.lastIndexOf(`
|
|
1234
|
+
`,e);if(s>e-500&&s>0)o=s+1;else{let i=r.slice(0,e).match(/[.!?]\s+(?=[A-Z])/g);if(i&&i.length>0){let a=i[i.length-1];if(a){let l=r.lastIndexOf(a,e);l>e-200&&l>0&&(o=l+2)}}else{let a=r.lastIndexOf(" ",e);a>e-100&&a>0&&(o=a+1)}}n.push(r.slice(0,o).trim()),r=r.slice(o).trim()}return n}function gt(t){let e=t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");return e=e.replace(/^```[\w]*\n?([\s\S]*?)```/gm,"<pre>$1</pre>"),e=e.replace(/`([^`]+)`/g,"<code>$1</code>"),e=e.replace(/\*\*([^*]+)\*\*/g,"<b>$1</b>"),e=e.replace(/__([^_]+)__/g,"<b>$1</b>"),e=e.replace(/\*([^*]+)\*/g,"<i>$1</i>"),e=e.replace(/_([^_]+)_/g,"<i>$1</i>"),e=e.replace(/~~([^~]+)~~/g,"<s>$1</s>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(n,r,o)=>'<a href="'+o.replace(/&/g,"&").replace(/"/g,""")+'">'+r+"</a>"),e=e.replace(/^#{1,6}\s+/gm,""),e}function M(t){return`\u274C Error: ${t instanceof Error?t.message:t}`}var Er=[{cmd:"/start",desc:"Show welcome and this command list"},{cmd:"/help",desc:"Show this command list"},{cmd:"/clear",desc:"Clear conversation history (SDK /clear)"},{cmd:"/compact",desc:"Compact conversation history (summarize older messages)"},{cmd:"/model [opus|sonnet|haiku]",desc:"Switch Claude model"}];function Ar(t){let e=["\u{1F4CB} Bot commands (aligned with agent-afk CLI):","",...Er.map(n=>` ${n.cmd}
|
|
1235
|
+
${n.desc}`)];return t&&t.length>0&&e.push("","\u{1F4CB} Session commands (from SDK, when using settingSources):","",...t.map(n=>` /${n.replace(/^\//,"")}`)),e.push("","Just send a message to chat with Claude."),e.join(`
|
|
1236
|
+
`)}function ht(){return`\u{1F44B} Welcome to Agent AFK Bot!
|
|
1237
|
+
|
|
1238
|
+
I'm powered by Claude and can help you with various tasks.
|
|
1239
|
+
|
|
1240
|
+
Available commands:
|
|
1241
|
+
${Er.map(e=>`${e.cmd} - ${e.desc}`).join(`
|
|
1242
|
+
`)}
|
|
1243
|
+
|
|
1244
|
+
Just send me a message to get started!`}function yt(t){return`${{opus:"\u{1F680}",sonnet:"\u26A1",haiku:"\u{1F338}"}[t]||"\u{1F916}"} Switched to Claude ${t.toUpperCase()}`}function Ie(){return"\u{1F504} Conversation history cleared!"}function bt(t){if(!t)return"\u{1F4E6} Conversation compacted (older messages summarized).";let e=t.tokensSavedEstimate!==void 0&&t.tokensSavedEstimate>0?` (~${ea(t.tokensSavedEstimate)} input tokens saved)`:"";return`\u{1F4E6} Compacted ${t.before} \u2192 ${t.after} messages${e}.`}function vt(t){return t==="aborted"?"\u{1F4E6} Compaction cancelled.":t.startsWith("summarization-failed")?`\u26A0\uFE0F Compaction failed: ${t}. History unchanged.`:`\u{1F4E6} Nothing to compact (${t}).`}function ea(t){return t>=1e3?`${Math.round(t/100)/10}k`:String(t)}async function wt(t){await t.reply(ht())}async function kt(t,e){let n=t.chat?.id,r,o=n?e.getSessionIfExists(n):void 0;if(o)try{await Promise.race([o.waitForInitialization(),new Promise((i,a)=>setTimeout(()=>a(new Error("timeout")),2e3))]);let s=o.getSessionMetadata();s.slashCommands?.length&&(r=s.slashCommands)}catch{}await t.reply(Ar(r))}async function St(t,e,n,r){let o=t.chat?.id;if(!o){await t.reply(M("Could not identify chat"));return}try{await e.resetSession(o),n.delete(o),await t.reply(Ie())}catch(s){r("Clear error:",s),await t.reply(M(s))}}async function Tr(t,e,n){let r=t.chat?.id;if(!r){await t.reply(M("Could not identify chat"));return}try{let o=await e.getSession(r);await t.sendChatAction("typing").catch(()=>{});let s=await o.compact();s.compacted?await t.reply(bt({before:s.messagesBefore,after:s.messagesAfter,...s.tokensSavedEstimate!==void 0?{tokensSavedEstimate:s.tokensSavedEstimate}:{}})):await t.reply(vt(s.reason??"unknown"))}catch(o){n("Compact error:",o),await t.reply(M(o))}}async function xt(t,e,n){let r=t.chat?.id;if(!r){await t.reply(M("Could not identify chat"));return}let s=t.message.text.split(/\s+/).slice(1);if(s.length===0){let c=e.getModel(r);await t.reply(`Current model: ${c.toUpperCase()}
|
|
1245
|
+
|
|
1246
|
+
Usage: /model [opus|sonnet|haiku]`);return}let i=s[0];if(!i){await t.reply(M("Please specify a model: opus, sonnet, or haiku"));return}let a=i.toLowerCase();if(!["opus","sonnet","haiku"].includes(a)){await t.reply(M(`Invalid model: ${i}
|
|
1247
|
+
Valid options: opus, sonnet, haiku`));return}try{await e.switchModel(r,a),await t.reply(yt(a))}catch(c){n("Model switch error:",c),await t.reply(M(c))}}function Ye(t){let e=t instanceof Error?t.message:String(t);return e.toLowerCase().includes("rate limit")||e.toLowerCase().includes("too many requests")}function Qe(t){let e=t instanceof Error?t.message:String(t);return e.toLowerCase().includes("network")||e.toLowerCase().includes("connect")||e.toLowerCase().includes("timeout")}var ta=300,na=9e4,ra=6e4;async function Pt(t,e,n,r){let o="",s=null,i=0,a=async(l,c=!1)=>{let d=gt(l||"\u2026"),u=Date.now();if(!s){let m=Ce(d);s=await t.reply(m[0]??"\u2026",{parse_mode:"HTML"});return}if(!c&&u-i<ta&&l.length<100)return;i=u;let p=Ce(d);try{await t.telegram.editMessageText(t.chat?.id,s.message_id,void 0,p[0]??d,{parse_mode:"HTML"})}catch{}};try{let l="sendMessageStream"in e&&typeof e.sendMessageStream=="function"?e.sendMessageStream(n):(async function*(){let m=await e.sendMessage(n,{stream:!1});yield{type:"message",message:m},yield{type:"done",metadata:m.metadata}})();await a("Thinking\u2026");let c=l[Symbol.asyncIterator](),d=!1,u=null,p=()=>{let m=d?ra:na;return new Promise((g,y)=>{u=setTimeout(()=>{u=null,y(new Error(d?"Response timed out. Try sending a shorter message or try again.":"Request timed out. The agent may still be starting (first message can take a minute). Try again in a moment."))},m),c.next().then(f=>{u!=null&&(clearTimeout(u),u=null),g(f)},f=>{u!=null&&(clearTimeout(u),u=null),y(f)})})};for(;;){let m=await p();if(m.done)break;let g=m.value;if(d||(d=!0,console.log("\u{1F4E1} First stream event received:",g.type),r?.("First stream event received:",g.type)),g.type==="chunk"&&g.chunk.type==="content"&&(o+=g.chunk.content,await a(o)),g.type==="message"&&g.message.role==="assistant"&&(o=g.message.content,await a(o)),g.type==="progress"){let{description:y,summary:f,lastToolName:v}=g.progress,x=v?`
|
|
1248
|
+
\u25E6 ${y} (${v})`:`
|
|
1249
|
+
\u25E6 ${y}`;o+=x,f&&(o+=`
|
|
1250
|
+
${f}`),await a(o)}if(g.type==="suggestion"&&(o+=`
|
|
1251
|
+
|
|
1252
|
+
\u{1F4A1} ${g.suggestion}`,await a(o)),g.type==="done"){o.trim()&&await a(o,!0);break}if(g.type==="error")throw g.error}if(o&&s){let m=Ce(gt(o));if(m.length>1)for(let g=1;g<m.length;g++){let y=m[g];y&&await t.reply(y,{parse_mode:"HTML"})}}}catch(l){throw r?.("Streaming error:",l),l}}async function Cr(t,e,n,r,o){if(!r.has(e))try{await Promise.race([n.waitForInitialization(),new Promise((a,l)=>setTimeout(()=>l(new Error("timeout")),5e3))]);let s=n.getSessionMetadata(),i=[{command:"start",description:"Show welcome and command list"},{command:"help",description:"Show this command list"},{command:"clear",description:"Clear conversation history"},{command:"compact",description:"Compact conversation history"},{command:"model",description:"Switch Claude model (opus/sonnet/haiku)"}];if(s.slashCommands?.length)for(let a of s.slashCommands){let l=a.replace(/^\//,"");i.push({command:l,description:`SDK command: ${l}`})}if(s.skills?.length)for(let a of s.skills)i.push({command:a,description:`Run ${a} skill`});await t.telegram.setMyCommands(i,{scope:{type:"chat",chat_id:e}}),r.add(e),o(`Registered ${i.length} commands for chat ${e}`)}catch(s){o(`Could not register dynamic commands for chat ${e}:`,s)}}var Je=class{sessionManager;messageQueues=new Map;registeredCommandChats;log;bot;constructor(e,n,r,o){this.bot=e,this.sessionManager=n,this.registeredCommandChats=r,this.log=o}async handle(e){let n=e.chat?.id,r=e.message.text;if(!(!n||!r)&&(console.log(`\u{1F4EC} Message from chat ID: ${n}`),!r.startsWith("/")))try{let o=await this.sessionManager.getSession(n);if(Cr(this.bot,n,o,this.registeredCommandChats,this.log).catch(s=>this.log("Failed to register chat commands:",s)),o.state!=="idle"){this.enqueueMessage(n,e,r),await e.reply("Message queued.");return}await this.processOne(n,e,r)}catch(o){console.error("\u274C Message handling error:",o),this.log("Message handling error:",o);let s=o;if((s?.message??"").includes("session is busy")){this.enqueueMessage(n,e,r),await e.reply("Message queued.");return}Ye(o)?await e.reply("\u23F3 Rate limit reached. Please wait a moment and try again."):Qe(o)?await e.reply("\u{1F310} Network error. Please check your connection and try again."):await e.reply(M(s))}}async processClearDirect(e,n){try{await this.sessionManager.resetSession(e),this.registeredCommandChats.delete(e),await n.reply(Ie())}catch(r){this.log("Clear error:",r),await n.reply(M(r))}}enqueueMessage(e,n,r){let o=this.messageQueues.get(e);o||(o=[],this.messageQueues.set(e,o)),o.push({type:"message",ctx:n,text:r})}enqueueClear(e,n){let r=this.messageQueues.get(e);r||(r=[],this.messageQueues.set(e,r)),r.push({type:"clear",ctx:n})}async processOne(e,n,r){try{let o=await this.sessionManager.getSession(e);await n.sendChatAction("typing").catch(()=>{}),await Pt(n,o,r,this.log)}catch(o){console.error("\u274C Message handling error:",o),this.log("Message handling error:",o);let s=o;Ye(o)?await n.reply("\u23F3 Rate limit reached. Please wait a moment and try again."):Qe(o)?await n.reply("\u{1F310} Network error. Please check your connection and try again."):await n.reply(M(s))}finally{this.drainQueue(e)}}async drainQueue(e){let n=this.messageQueues.get(e);if(!n?.length)return;let r=n.shift();r.type==="message"?await this.processOne(e,r.ctx,r.text):await this.processClearDirect(e,r.ctx)}};function Et(t,e=()=>{}){return async(n,r)=>{let o=n.chat?.id;if(o===void 0||!t.has(o)){e("[allowlist] Rejecting update from chat:",o??"<unknown>");return}await r()}}var Xe=class{bot;sessionManager;options;running=!1;registeredCommandChats=new Set;messageHandler;constructor(e){this.options=e,this.bot=new oa(e.botToken),this.sessionManager=new me(e),this.messageHandler=new Je(this.bot,this.sessionManager,this.registeredCommandChats,this.log.bind(this)),this.setupHandlers()}setupHandlers(){this.bot.use(Et(this.options.allowedChatIds,this.log.bind(this))),this.bot.command("start",e=>wt(e)),this.bot.command("help",e=>kt(e,this.sessionManager)),this.bot.command("clear",async e=>{let n=e.chat?.id;if(!n){await e.reply(M("Could not identify chat"));return}(await this.sessionManager.getSession(n)).state!=="idle"?(this.messageHandler.enqueueClear(n,e),await e.reply("Clear queued.")):await St(e,this.sessionManager,this.registeredCommandChats,this.log.bind(this))}),this.bot.command("compact",e=>Tr(e,this.sessionManager,this.log.bind(this))),this.bot.command("model",e=>xt(e,this.sessionManager,this.log.bind(this))),this.bot.on("text",e=>this.messageHandler.handle(e)),this.bot.catch((e,n)=>{this.log("Bot error:",e),n.reply(M("An unexpected error occurred. Please try again.")).catch(r=>this.log("Failed to send error message:",r))})}async start(){if(this.running)throw new Error("Bot is already running");this.log("Loading sessions..."),await this.sessionManager.loadSessions(),this.log("Starting bot..."),await this.bot.launch(),this.log("Registering bot commands..."),await this.bot.telegram.setMyCommands([{command:"start",description:"Show welcome and command list"},{command:"help",description:"Show this command list"},{command:"clear",description:"Clear conversation history"},{command:"compact",description:"Compact conversation history"},{command:"model",description:"Switch Claude model (opus/sonnet/haiku)"}]),this.running=!0,this.log("Bot started successfully");let e=async n=>{this.log(`Received ${n}, shutting down...`),await this.stop(),process.exit(0)};process.once("SIGINT",()=>e("SIGINT")),process.once("SIGTERM",()=>e("SIGTERM"))}async stop(){if(this.running){this.log("Stopping bot..."),this.running=!1,this.log("Closing sessions..."),await this.sessionManager.closeAll(),this.log("Stopping bot polling...");try{this.bot.stop()}catch(e){this.log("Error stopping bot (may not have been started):",e)}this.log("Bot stopped")}}getStats(){return{running:this.running,activeSessions:this.sessionManager.getSessionCount(),totalChats:this.sessionManager.getChatCount()}}async handleStart(e){return wt(e)}async handleHelp(e){return kt(e,this.sessionManager)}async handleClear(e){let n=e.chat?.id;if(!n){await e.reply(M("Could not identify chat"));return}if((await this.sessionManager.getSession(n)).state!=="idle")this.messageHandler.enqueueClear(n,e),await e.reply("Clear queued.");else return St(e,this.sessionManager,this.registeredCommandChats,this.log.bind(this))}async handleMessage(e){return this.messageHandler.handle(e)}async handleModelSwitch(e){return xt(e,this.sessionManager,this.log.bind(this))}log(...e){this.options.verbose&&console.log("[TelegramBot]",...e)}};export{ne as AgentSession,ae as MessageQueue,fe as OpenAICodexProvider,me as SessionManager,S as SubagentManager,Xe as TelegramBot,xr as createCanUseToolHook,qe as openaiCodexProvider,xe as providerForModel,Ve as resolveProvider};
|