@geoffai/coder 1.0.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/dist/cli.js ADDED
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ import*as C from'fs/promises';import {stat,readdir,readFile,mkdir,writeFile}from'fs/promises';import*as _ from'path';import {resolve,join,dirname}from'path';import {spawn}from'child_process';import {existsSync,readFileSync}from'fs';import {homedir}from'os';var _e={maxRetries:3,baseDelayMs:1e3,maxDelayMs:3e4,retryableStatuses:[429,500,502,503,504]},S=class{apiKey;baseUrl;model;retryConfig;constructor(e){this.apiKey=e.apiKey,this.baseUrl=e.baseUrl.replace(/\/$/,""),this.model=e.model,this.retryConfig={..._e,...e.retryConfig};}async sleep(e){return new Promise(s=>setTimeout(s,e))}calculateBackoff(e,s){if(s)return Math.min(s*1e3,this.retryConfig.maxDelayMs);let t=this.retryConfig.baseDelayMs*Math.pow(2,e),o=Math.random()*.3*t;return Math.min(t+o,this.retryConfig.maxDelayMs)}shouldRetry(e,s){return s<this.retryConfig.maxRetries&&this.retryConfig.retryableStatuses.includes(e)}async createCompletion(e,s,t={}){let o={model:this.model,messages:e,tools:s?.length?s:void 0,tool_choice:s?.length?"auto":void 0,stream:false,max_tokens:t.maxTokens??4096,temperature:t.temperature??.7},n=null;for(let i=0;i<=this.retryConfig.maxRetries;i++)try{let a=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(o)});if(!a.ok){let l=await a.text();if(this.shouldRetry(a.status,i)){let u=a.headers.get("Retry-After"),p=this.calculateBackoff(i,u?parseInt(u):void 0);console.warn(`\x1B[33m\u26A0 API request failed (${a.status}), retrying in ${Math.round(p/1e3)}s (attempt ${i+1}/${this.retryConfig.maxRetries})\x1B[0m`),await this.sleep(p);continue}throw new Error(`API request failed: ${a.status} - ${l}`)}return a.json()}catch(a){if(n=a,i<this.retryConfig.maxRetries&&a.message.includes("fetch")){let l=this.calculateBackoff(i);console.warn(`\x1B[33m\u26A0 Network error, retrying in ${Math.round(l/1e3)}s (attempt ${i+1}/${this.retryConfig.maxRetries})\x1B[0m`),await this.sleep(l);continue}throw a}throw n??new Error("Max retries exceeded")}async*createStreamingCompletion(e,s,t={}){let o={model:this.model,messages:e,tools:s?.length?s:void 0,tool_choice:s?.length?"auto":void 0,stream:true,max_tokens:t.maxTokens??4096,temperature:t.temperature??.7},n=null;for(let i=0;i<=this.retryConfig.maxRetries;i++)try{let a=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(o)});if(!a.ok){let m=await a.text();if(this.shouldRetry(a.status,i)){let d=a.headers.get("Retry-After"),g=this.calculateBackoff(i,d?parseInt(d):void 0);console.warn(`\x1B[33m\u26A0 API request failed (${a.status}), retrying in ${Math.round(g/1e3)}s (attempt ${i+1}/${this.retryConfig.maxRetries})\x1B[0m`),await this.sleep(g);continue}throw new Error(`API request failed: ${a.status} - ${m}`)}let l=a.body?.getReader();if(!l)throw new Error("No response body");let u=new TextDecoder,p="";try{for(;;){let{done:m,value:d}=await l.read();if(m)break;p+=u.decode(d,{stream:!0});let g=p.split(`
3
+ `);p=g.pop()??"";for(let f of g){let y=f.trim();if(!(!y||y==="data: [DONE]")&&y.startsWith("data: "))try{yield JSON.parse(y.slice(6));}catch{}}}}finally{l.releaseLock();}return}catch(a){if(n=a,i<this.retryConfig.maxRetries&&(a.message.includes("fetch")||a.message.includes("network"))){let l=this.calculateBackoff(i);console.warn(`\x1B[33m\u26A0 Network error, retrying in ${Math.round(l/1e3)}s (attempt ${i+1}/${this.retryConfig.maxRetries})\x1B[0m`),await this.sleep(l);continue}throw a}throw n??new Error("Max retries exceeded")}async accumulateStream(e,s,t={}){let o="",n=[],i=new Map;for await(let l of this.createStreamingCompletion(e,s,t)){let u=l.choices[0]?.delta;if(u){if(u.content){let p=typeof u.content=="string"?u.content:"";o+=p,t.onToken?.(p);}if(u.tool_calls)for(let p of u.tool_calls){let m=p.index??0;i.has(m)||i.set(m,{id:p.id??"",name:p.function?.name??"",args:""});let d=i.get(m);p.id&&(d.id=p.id),p.function?.name&&(d.name=p.function.name),p.function?.arguments&&(d.args+=p.function.arguments);}}}for(let[l,u]of i)u.id&&u.name&&n.push({id:u.id,type:"function",function:{name:u.name,arguments:u.args}});let a={role:"assistant",content:o||""};return n.length>0&&(a.tool_calls=n),a}};function $(r,e){let s=resolve(r),t=resolve(r,e);return !t.startsWith(s+"/")&&t!==s?{valid:false,resolvedPath:t,error:`Path "${e}" resolves outside the working directory`}:{valid:true,resolvedPath:t}}var Pe=[/rm\s+(-rf?|--recursive)?\s*[\/~]/i,/rm\s+-rf?\s*\*/i,/>\s*\/dev\/sd/i,/mkfs/i,/dd\s+.*of=\/dev/i,/:(){ :|:& };:/i,/chmod\s+(-R\s+)?777\s+\//i,/chown\s+(-R\s+)?.*\s+\//i,/curl.*\|\s*(ba)?sh/i,/wget.*\|\s*(ba)?sh/i,/eval\s*\(/i,/>\s*\/etc\//i,/rm\s+.*\/\s*$/i],Se=[/git\s+(reset|clean)\s+--hard/i,/git\s+push\s+.*--force/i,/drop\s+database/i,/truncate\s+table/i,/delete\s+from\s+\w+\s*;?\s*$/i,/npm\s+publish/i,/pip\s+install\s+--user/i];function G(r){for(let e of Pe)if(e.test(r))return {allowed:false,blocked:`Command contains dangerous pattern: ${e.source}`};for(let e of Se)if(e.test(r))return {allowed:true,warning:`Command may be destructive: ${e.source}`};return {allowed:true}}var Re=[/(?:api[_-]?key|apikey|secret|password|passwd|token|auth)[=:]\s*['"]?[\w\-\.]+['"]?/gi,/Bearer\s+[\w\-\.]+/gi,/ghp_[a-zA-Z0-9]{36}/g,/sk-[a-zA-Z0-9]{48}/g,/-----BEGIN.*PRIVATE KEY-----/gi];function R(r){let e=r;for(let s of Re)e=e.replace(s,"[REDACTED]");return e}var Me=".geoff",Ae="todos";function Ee(r){let e=process.env.HOME||process.env.USERPROFILE||process.cwd(),s=_.join(e,Me,Ae),t=`${"default"}.json`;return _.join(s,t)}async function Ie(r,e){let s=Ee(),t=_.dirname(s);await C.mkdir(t,{recursive:true});let o={todos:r,sessionId:"default",updatedAt:new Date().toISOString()};await C.writeFile(s,JSON.stringify(o,null,2),"utf-8");}function H(r){return async e=>{try{let s=e,{todos:t}=s;if(!Array.isArray(t))return {success:!1,output:"",error:'Parameter "todos" must be an array.'};for(let p of t){if(!p.id||typeof p.id!="string"||p.id.trim()==="")return {success:!1,output:"",error:'Each todo must have a non-empty "id" string.'};if(!p.content||typeof p.content!="string"||p.content.trim()==="")return {success:!1,output:"",error:'Each todo must have a non-empty "content" string.'};if(!["pending","in_progress","completed"].includes(p.status))return {success:!1,output:"",error:'Each todo must have a valid "status" (pending, in_progress, completed).'}}let o=t.map(p=>p.id);if(new Set(o).size!==o.length)return {success:!1,output:"",error:"Todo IDs must be unique."};await Ie(t,r);let n=t.filter(p=>p.status==="pending").length,i=t.filter(p=>p.status==="in_progress").length,a=t.filter(p=>p.status==="completed").length,l;t.length===0?l="Todo list cleared.":l=`Todo list updated: ${a} completed, ${i} in progress, ${n} pending.`;let u=JSON.stringify(t);return l+=`
4
+
5
+ <system-reminder>
6
+ Your todo list has changed. DO NOT mention this explicitly to the user. Here are the latest contents of your todo list:
7
+
8
+ ${u}. Continue on with the tasks at hand if applicable.
9
+ </system-reminder>`,{success:!0,output:l}}catch(s){return {success:false,output:"",error:`Failed to write todos: ${s instanceof Error?s.message:String(s)}`}}}}var W={type:"function",function:{name:"todo_write",description:"Creates and manages a structured task list for your current coding session. This helps track progress, organize complex tasks, and demonstrate thoroughness. Use for complex multi-step tasks (3+ steps), when user provides multiple tasks, or when starting/completing work. Do NOT use for single trivial tasks.",parameters:{type:"object",properties:{todos:{type:"array",description:"The updated todo list"}},required:["todos"]}}};var j=new Map;function J(r="default"){return j.has(r)||j.set(r,{enabled:false,awaitingApproval:false,approved:false}),j.get(r)}function De(r="default"){let e=J(r);e.enabled=true,e.approved=false,e.awaitingApproval=false;}function K(r,e){return async s=>{try{let t=s,{plan:o}=t;if(!o||typeof o!="string"||o.trim()==="")return {success:!1,output:"",error:'Parameter "plan" must be a non-empty string.'};let n=J(r||"default");if(n.plan=o,n.awaitingApproval=!0,e);return n.awaitingApproval=!1,n.approved=!0,n.enabled=!1,{success:!0,output:`Plan presented for approval:
10
+
11
+ ${o}
12
+
13
+ User has approved your plan. You can now start coding. Start with updating your todo list if applicable.`}}catch(t){return {success:false,output:"",error:`Failed to exit plan mode: ${t instanceof Error?t.message:String(t)}`}}}}var X={type:"function",function:{name:"exit_plan_mode",description:"Use this tool when you are in plan mode and have finished presenting your plan and are ready to code. This will prompt the user to approve the plan. IMPORTANT: Only use this tool when planning implementation steps for coding tasks. For research tasks (searching files, reading code, understanding codebase) - do NOT use this tool.",parameters:{type:"object",properties:{plan:{type:"string",description:"The plan you came up with, that you want the user to approve. Supports markdown. Should be concise but complete."}},required:["plan"]}}};function V(r,e){return async s=>{try{if(De(r||"default"),e);return {success:!0,output:"Entered plan mode. Please create a plan before starting implementation."}}catch(t){return {success:false,output:"",error:`Failed to enter plan mode: ${t instanceof Error?t.message:String(t)}`}}}}var Y={type:"function",function:{name:"enter_plan_mode",description:"Enter plan mode to create a plan before implementing code changes. Use this when starting a complex task that requires planning. In plan mode, create a clear plan and then use exit_plan_mode to get approval.",parameters:{type:"object",properties:{},required:[]}}};var U=class{processes=new Map;counter=0;start(e,s,t){let o=`bg_${++this.counter}_${Date.now()}`,n=spawn("bash",["-c",e],{cwd:s,env:{...process.env,FORCE_COLOR:"0"},detached:false}),i={id:o,pid:n.pid||0,command:e,startTime:Date.now(),output:[],isComplete:false,exitCode:null,process:n};return n.stdout?.on("data",a=>{let l=a.toString();if(i.output.push(l),i.output.length>1e3&&(i.output=i.output.slice(-500)),t){let u={type:"thinking",timestamp:Date.now(),data:{processId:o,type:"stdout",output:l,isBackground:true}};t(u);}}),n.stderr?.on("data",a=>{let l=a.toString();if(i.output.push(`[stderr] ${l}`),t){let u={type:"thinking",timestamp:Date.now(),data:{processId:o,type:"stderr",output:l,isBackground:true}};t(u);}}),n.on("close",a=>{if(i.isComplete=true,i.exitCode=a,t){let l={type:"thinking",timestamp:Date.now(),data:{processId:o,type:"exit",exitCode:a,isBackground:true,message:`Background process ${o} exited with code ${a}`}};t(l);}}),n.on("error",a=>{i.isComplete=true,i.output.push(`[error] ${a.message}`);}),this.processes.set(o,i),i}get(e){return this.processes.get(e)}getAll(){return Array.from(this.processes.values())}kill(e){let s=this.processes.get(e);if(!s)return false;try{return s.process.kill("SIGTERM"),setTimeout(()=>{try{s.process.kill("SIGKILL");}catch{}},1e3),!0}catch{return false}}getOutput(e,s){let t=this.processes.get(e);if(!t)return "";let o=t.output;return s&&s<o.length?o.slice(-s).join(""):o.join("")}cleanup(){for(let[e,s]of this.processes)s.isComplete&&this.processes.delete(e);}},k=new U;function Z(r,e){return async s=>{try{let t=s.command;if(!t||typeof t!="string")return {success:!1,output:"",error:'Parameter "command" must be a non-empty string.'};let o=k.start(t,r,e);await new Promise(i=>setTimeout(i,500));let n=k.getOutput(o.id,20);return {success:!0,output:`Started background process:
14
+ ID: ${o.id}
15
+ PID: ${o.pid}
16
+ Command: ${t}
17
+
18
+ Initial output:
19
+ ${n||"(no output yet)"}
20
+
21
+ Use get_process_output to check status or kill_process to stop.`}}catch(t){return {success:false,output:"",error:`Failed to start background process: ${t instanceof Error?t.message:String(t)}`}}}}function Q(){return async r=>{try{let e=r.process_id,s=r.tail;if(!e||typeof e!="string")return {success:!1,output:"",error:'Parameter "process_id" must be a non-empty string.'};let t=k.get(e);if(!t){let a=k.getAll();if(a.length===0)return {success:!1,output:"",error:`No background processes found. Process ID ${e} is invalid.`};let l=a.map(u=>` ${u.id}: ${u.command.slice(0,50)} (${u.isComplete?"completed":"running"})`).join(`
22
+ `);return {success:!1,output:"",error:`Process ${e} not found. Available processes:
23
+ ${l}`}}let o=k.getOutput(e,s||50),n=Math.round((Date.now()-t.startTime)/1e3),i;return t.isComplete?i=`Completed (exit code: ${t.exitCode})`:i="Running",{success:!0,output:`Process: ${e}
24
+ Status: ${i}
25
+ Duration: ${n}s
26
+ Command: ${t.command}
27
+
28
+ Output:
29
+ ${o||"(no output)"}`}}catch(e){return {success:false,output:"",error:`Failed to get process output: ${e instanceof Error?e.message:String(e)}`}}}}function ee(){return async r=>{try{let e=r.process_id;return !e||typeof e!="string"?{success:!1,output:"",error:'Parameter "process_id" must be a non-empty string.'}:k.kill(e)?{success:!0,output:`Process ${e} has been killed.`}:{success:!1,output:"",error:`Failed to kill process ${e}. It may not exist or already be terminated.`}}catch(e){return {success:false,output:"",error:`Failed to kill process: ${e instanceof Error?e.message:String(e)}`}}}}function te(){return async r=>{try{let e=k.getAll();return e.length===0?{success:!0,output:"No background processes running."}:{success:!0,output:`Background processes:
30
+
31
+ ${e.map(t=>{let o=Math.round((Date.now()-t.startTime)/1e3),n=t.isComplete?`completed (exit: ${t.exitCode})`:"running";return ` ${t.id}:
32
+ Command: ${t.command.slice(0,60)}${t.command.length>60?"...":""}
33
+ Status: ${n}
34
+ Duration: ${o}s`}).join(`
35
+
36
+ `)}`}}catch(e){return {success:false,output:"",error:`Failed to list processes: ${e instanceof Error?e.message:String(e)}`}}}}var se={type:"function",function:{name:"run_background",description:"Start a command running in the background. Useful for dev servers, watch processes, or long-running tasks. Returns immediately with a process ID. Use get_process_output to check status.",parameters:{type:"object",properties:{command:{type:"string",description:"The shell command to run in the background"}},required:["command"]}}},oe={type:"function",function:{name:"get_process_output",description:"Get the output from a background process. Returns recent output and status.",parameters:{type:"object",properties:{process_id:{type:"string",description:"The ID of the background process"},tail:{type:"string",description:"Number of recent lines to return (default: 50)"}},required:["process_id"]}}},re={type:"function",function:{name:"kill_process",description:"Kill a background process by ID.",parameters:{type:"object",properties:{process_id:{type:"string",description:"The ID of the background process to kill"}},required:["process_id"]}}},ne={type:"function",function:{name:"list_processes",description:"List all background processes and their status.",parameters:{type:"object",properties:{},required:[]}}};var Fe=[{type:"function",function:{name:"read_file",description:"Read the contents of a file at the specified path. Use this to examine existing code, configuration files, or any text file.",parameters:{type:"object",properties:{path:{type:"string",description:"The path to the file to read (relative to working directory or absolute)"}},required:["path"]}}},{type:"function",function:{name:"write_file",description:"Write content to a file, creating it if it doesn't exist or overwriting if it does. Creates parent directories as needed.",parameters:{type:"object",properties:{path:{type:"string",description:"The path where the file should be written"},content:{type:"string",description:"The content to write to the file"}},required:["path","content"]}}},{type:"function",function:{name:"list_files",description:"List files and directories at the specified path. Returns a tree-like structure showing the contents.",parameters:{type:"object",properties:{path:{type:"string",description:"The directory path to list (defaults to current working directory)"},recursive:{type:"string",description:"Whether to list recursively (true/false, defaults to false)",enum:["true","false"]},max_depth:{type:"string",description:"Maximum depth for recursive listing (default: 3)"}},required:["path"]}}},{type:"function",function:{name:"execute_command",description:"Execute a shell command in the working directory. Use for running scripts, installing packages, git operations, tests, etc. Commands run in bash.",parameters:{type:"object",properties:{command:{type:"string",description:"The shell command to execute"},timeout:{type:"string",description:"Timeout in milliseconds (default: 30000)"}},required:["command"]}}},{type:"function",function:{name:"search_files",description:"Search for files matching a pattern or containing specific text using grep. Returns matching files and lines.",parameters:{type:"object",properties:{pattern:{type:"string",description:"The text pattern or regex to search for"},path:{type:"string",description:"Directory to search in (defaults to working directory)"},file_pattern:{type:"string",description:"Glob pattern to filter files (e.g., '*.ts', '*.py')"}},required:["pattern"]}}},{type:"function",function:{name:"apply_diff",description:"Apply a diff/patch to modify specific parts of a file. Use for precise edits instead of rewriting entire files.",parameters:{type:"object",properties:{path:{type:"string",description:"The path to the file to modify"},original:{type:"string",description:"The exact original text to find and replace"},replacement:{type:"string",description:"The new text to replace the original with"},replace_all:{type:"string",description:"Whether to replace all occurrences (true/false, defaults to false)",enum:["true","false"]}},required:["path","original","replacement"]}}},{type:"function",function:{name:"task_complete",description:"Signal that the task has been completed. Use this when you have finished the user's request and want to present the final result.",parameters:{type:"object",properties:{summary:{type:"string",description:"A brief summary of what was accomplished"},files_modified:{type:"string",description:"Comma-separated list of files that were created or modified"}},required:["summary"]}}},W,Y,X,se,oe,re,ne],ue=["node_modules",".git","__pycache__",".venv","venv","dist","build",".next",".nuxt","coverage",".cache",".parcel-cache"];function pe(r){let e=typeof r=="string"?r:r.workingDir,s=typeof r=="string"?ue:r.ignorePatterns??ue,t=new Map;return t.set("read_file",async o=>{try{let n=$(e,o.path);if(!n.valid)return {success:!1,output:"",error:n.error};if((await stat(n.resolvedPath)).isDirectory()){let l=await readdir(n.resolvedPath);return {success:!1,output:"",error:`Path is a directory, not a file. Contents: ${l.slice(0,20).join(", ")}${l.length>20?` ... and ${l.length-20} more`:""}`}}let a=await readFile(n.resolvedPath,"utf-8");return {success:!0,output:R(a)}}catch(n){return {success:false,output:"",error:`Failed to read file: ${n.message}`}}}),t.set("write_file",async o=>{try{let n=$(e,o.path);return n.valid?(await mkdir(dirname(n.resolvedPath),{recursive:!0}),await writeFile(n.resolvedPath,o.content,"utf-8"),{success:!0,output:`Successfully wrote to ${o.path}`}):{success:!1,output:"",error:n.error}}catch(n){return {success:false,output:"",error:`Failed to write file: ${n.message}`}}}),t.set("list_files",async o=>{try{let n=$(e,o.path||".");if(!n.valid)return {success:!1,output:"",error:n.error};let i=n.resolvedPath,a=o.recursive==="true",l=parseInt(o.max_depth)||3;async function u(m,d,g){if(d>l)return [];let f=await readdir(m,{withFileTypes:!0}),y=[];for(let b of f){if(s.some(w=>w.includes("*")?new RegExp("^"+w.replace(/\*/g,".*")+"$").test(b.name):b.name===w))continue;let O=b.isDirectory();if(y.push(`${g}${O?"\u{1F4C1} ":"\u{1F4C4} "}${b.name}`),O&&a&&d<l){let w=await u(join(m,b.name),d+1,g+" ");y.push(...w);}}return y}return {success:!0,output:(await u(i,0,"")).join(`
37
+ `)||"(empty directory)"}}catch(n){return {success:false,output:"",error:`Failed to list files: ${n.message}`}}}),t.set("execute_command",async o=>{let n=o.command,i=parseInt(o.timeout)||3e4,a=G(n);return a.allowed?(a.warning&&console.warn(`\x1B[33m\u26A0 Warning: ${a.warning}\x1B[0m`),new Promise(l=>{let u=spawn("bash",["-c",n],{cwd:e,timeout:i,env:{...process.env,FORCE_COLOR:"0"}}),p="",m="";u.stdout.on("data",d=>{p+=d.toString();}),u.stderr.on("data",d=>{m+=d.toString();}),u.on("close",d=>{let g=R([p,m].filter(Boolean).join(`
38
+ `));l(d===0?{success:true,output:g||"(no output)"}:{success:false,output:g,error:`Command exited with code ${d}`});}),u.on("error",d=>{l({success:false,output:"",error:`Failed to execute command: ${d.message}`});});})):{success:false,output:"",error:`Command blocked: ${a.blocked}`}}),t.set("search_files",async o=>{let n=o.pattern,i=$(e,o.path||".");if(!i.valid)return {success:false,output:"",error:i.error};let a=i.resolvedPath,l=o.file_pattern,u=n.replace(/['"\\]/g,"\\$&"),p=(l||"*").replace(/['"\\]/g,"\\$&"),m=`rg -n --glob '${p}' '${u}' '${a}' 2>/dev/null | head -50`,d=`grep -rn --include='${p}' '${u}' '${a}' 2>/dev/null | head -50`;return new Promise(g=>{let f=spawn("bash",["-c",`command -v rg >/dev/null && ${m} || ${d}`],{cwd:e}),y="";f.stdout.on("data",b=>{y+=b.toString();}),f.on("close",()=>{if(y){let b=R(y).split(`
39
+ `).map(v=>v.startsWith(e)?v.replace(e+"/",""):v).join(`
40
+ `);g({success:true,output:b});}else g({success:true,output:"No matches found"});});})}),t.set("apply_diff",async o=>{try{let n=$(e,o.path);if(!n.valid)return {success:!1,output:"",error:n.error};let i=n.resolvedPath,a=o.original,l=o.replacement,u=o.replace_all==="true"||o.replace_all===!0,p=await readFile(i,"utf-8");if(!p.includes(a))return {success:!1,output:"",error:"Original text not found in file. Please verify the exact text to replace."};let m=p.split(a).length-1,d=u||m===1?p.replaceAll(a,l):p.replace(a,l);return await writeFile(i,d,"utf-8"),{success:!0,output:m>1&&!u?`Successfully applied diff to ${o.path} (1 of ${m} occurrences). Use replace_all: true to replace all.`:`Successfully applied diff to ${o.path}${m>1?` (${m} occurrences)`:""}`}}catch(n){return {success:false,output:"",error:`Failed to apply diff: ${n.message}`}}}),t.set("task_complete",async o=>({success:true,output:`\u2705 Task Complete
41
+
42
+ ${o.summary}${o.files_modified?`
43
+
44
+ Modified files: ${o.files_modified}`:""}`})),t.set("todo_write",H()),t.set("enter_plan_mode",V()),t.set("exit_plan_mode",K()),t.set("run_background",Z(e)),t.set("get_process_output",Q()),t.set("kill_process",ee()),t.set("list_processes",te()),{definitions:Fe,handlers:t}}function Be(r){let e=[join(r,"AGENTS.md"),join(r,".agents.md"),join(r,"CLAUDE.md"),join(r,".claude.md")];for(let s of e)if(existsSync(s))try{return readFileSync(s,"utf-8").trim()}catch{}return null}function de(r){let e=Be(r.workingDirectory),s=`You are an AI coding agent operating in a terminal environment. You help users with software engineering tasks by reading, writing, and executing code.
45
+
46
+ ## Your Capabilities
47
+ - Read and analyze files in the codebase
48
+ - Write and modify files with precise edits
49
+ - Execute shell commands (bash) for builds, tests, git, etc.
50
+ - Search through code to find relevant files and patterns
51
+ - Apply targeted diffs for surgical code changes
52
+
53
+ ## Working Directory
54
+ ${r.workingDirectory}
55
+
56
+ ## Operating System
57
+ ${r.osInfo}
58
+
59
+ ## Guidelines
60
+
61
+ ### Task Execution
62
+ - Break complex tasks into steps and execute them methodically
63
+ - Read files before modifying them to understand the context
64
+ - Use apply_diff for small changes; write_file for new files or large rewrites
65
+ - Verify your changes by reading the file after modification when appropriate
66
+ - Run relevant tests or builds after making changes
67
+
68
+ ### Code Quality
69
+ - Follow existing code style and patterns in the project
70
+ - Add appropriate error handling
71
+ - Write clean, maintainable code
72
+ - Include comments for complex logic
73
+
74
+ ### Tool Usage
75
+ - Use the most appropriate tool for each task
76
+ - Prefer apply_diff over write_file for existing files when making small changes
77
+ - Use search_files to find relevant code before making changes
78
+ - Execute commands to verify your work (run tests, check syntax, etc.)
79
+
80
+ ### Communication
81
+ - Explain what you're doing and why
82
+ - Report errors clearly and suggest fixes
83
+ - Summarize your changes when completing a task
84
+ - Ask clarifying questions if the request is ambiguous
85
+
86
+ ### Safety
87
+ - Never execute destructive commands without explicit confirmation
88
+ - Be careful with rm, git reset --hard, and similar operations
89
+ - Don't expose secrets or credentials
90
+ - Respect file permissions and ownership
91
+
92
+ ## Important Rules
93
+ 1. Always verify file paths exist before writing
94
+ 2. Never guess at file contents - read them first
95
+ 3. Keep iterating until the task is fully complete
96
+ 4. Use task_complete when you've finished the user's request
97
+ 5. If stuck, explain the issue and ask for guidance`;return e?`${s}
98
+
99
+ ## Project-Specific Instructions
100
+ The following instructions are specific to this project and should be followed:
101
+
102
+ ${e}`:s}var Ge=[{id:"task_focus",content:`<system-reminder>
103
+ Stay focused on the current task. Do what was asked - nothing more, nothing less.
104
+ Do not create unnecessary files or make changes beyond the scope of the request.
105
+ </system-reminder>`,injectionPoint:"pre_user_message",priority:10},{id:"verify_changes",content:`<system-reminder>
106
+ After making file changes, verify they were applied correctly if the task is complex.
107
+ Consider running relevant tests or build commands to catch errors early.
108
+ </system-reminder>`,injectionPoint:"post_tool_result",priority:5,condition:r=>{let e=r.lastToolCalls[0];return e?.function?.name==="write_file"||e?.function?.name==="apply_diff"}},{id:"iteration_warning",content:`<system-reminder>
109
+ You have been running for several iterations. Make sure you are making progress.
110
+ If stuck, consider:
111
+ - Re-reading the original request
112
+ - Checking for errors in your approach
113
+ - Asking the user for clarification
114
+ </system-reminder>`,injectionPoint:"context_refresh",priority:8,condition:r=>r.iteration>5},{id:"complete_task",content:`<system-reminder>
115
+ If you have finished the user's request, use the task_complete tool to signal completion.
116
+ Provide a clear summary of what was accomplished.
117
+ </system-reminder>`,injectionPoint:"task_boundary",priority:7,condition:r=>r.iteration>2},{id:"file_operation_reminder",content:`<system-reminder>
118
+ When modifying existing files:
119
+ - ALWAYS read the file first to understand its current state
120
+ - ALWAYS prefer apply_diff for small, targeted changes
121
+ - Use write_file only for new files or complete rewrites
122
+ </system-reminder>`,injectionPoint:"pre_user_message",priority:6}];function A(r,e){return Ge.filter(s=>s.injectionPoint===e).filter(s=>!s.condition||s.condition(r)).sort((s,t)=>t.priority-s.priority).map(s=>s.content)}function me(r,e,s){let t=A(e,"pre_user_message"),o=[];return t.length>0&&o.push(t.join(`
123
+ `)),o.push(r),o.join(`
124
+
125
+ `)}function ge(r,e,s){let t=A(e,"post_tool_result"),o=[r];return t.length>0&&o.push(t.join(`
126
+ `)),o.join(`
127
+
128
+ `)}function fe(r){return [...A(r,"context_refresh"),...A(r,"task_boundary")].join(`
129
+
130
+ `)}var c={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",gray:"\x1B[90m",clearLine:"\x1B[2K"},x={h:"\u2500",v:"\u2502",tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",hDouble:"\u2550",hHeavy:"\u2501"},h={success:"\u2713",error:"\u2717",warn:"\u26A0",info:"\u2139",tool:"\u{1F527}",user:"\u{1F464}",bot:"\u{1F916}",stats:"\u{1F4CA}",tokens:"\u{1F524}",files:"\u{1F4C1}",code:"\u{1F4DD}",exec:"\u2699\uFE0F",diamond:"\u25C8"},T=class{verbose;currentMetricsLine=null;onLogEvent;constructor(e=false,s){this.verbose=e,this.onLogEvent=s;}setLogEventCallback(e){this.onLogEvent=e;}emit(e,s){this.onLogEvent&&this.onLogEvent({type:e,timestamp:Date.now(),data:s});}timestamp(){return new Date().toISOString().split("T")[1].slice(0,8)}num(e){return e.toLocaleString()}truncate(e,s){return e.length>s?e.slice(0,s)+"...":e}colorize(e,s){return `${s}${e}${c.reset}`}statusIcon(e){return e?this.colorize(h.success,c.green):this.colorize(h.error,c.red)}line(e=x.h,s=60){return this.colorize(e.repeat(s),c.dim)}label(e,s,t=c.cyan){return ` ${e.padEnd(13)}${this.colorize(s,t)}`}section(e,s){this.print(`${c.bright}${s} ${e}${c.reset}`);}clearStatusLine(){this.currentMetricsLine!==null&&process.stdout.write(`\r${c.clearLine}`);}restoreStatusLine(){this.currentMetricsLine!==null&&process.stdout.write(this.currentMetricsLine);}print(e){this.clearStatusLine(),console.log(e),this.restoreStatusLine();}info(e){this.emit("info",{message:e}),this.print(`${this.colorize(h.info,c.blue)} ${e}`);}debug(e){this.verbose&&this.print(this.colorize(`${this.timestamp()} ${e}`,c.gray));}warn(e){this.emit("warn",{message:e}),this.print(`${this.colorize(h.warn,c.yellow)} ${e}`);}error(e){this.emit("error",{message:e}),this.print(`${this.colorize(h.error,c.red)} ${e}`);}success(e){this.emit("success",{message:e}),this.print(`${this.colorize(h.success,c.green)} ${e}`);}tool(e,s){if(this.emit("tool_call",{toolName:e,toolArgs:s}),e==="task_complete")try{let t=JSON.parse(s);this.emit("task_complete",{message:t.summary,toolName:e}),this.clearStatusLine(),console.log(`
131
+ ${this.colorize(`${h.success} Task Complete`,c.green)}`),t.summary&&console.log(this.colorize(t.summary,c.dim)),console.log(),this.restoreStatusLine();return}catch{}this.print(`${this.colorize(`${h.tool} ${e}`,c.cyan)} ${this.colorize(this.truncate(s,200),c.dim)}`);}toolResult(e,s){if(this.emit("tool_result",{toolName:e,success:s.success,output:s.output,error:s.error}),e==="task_complete")return;let t=this.statusIcon(s.success);if(this.verbose){let o=this.truncate(s.output,500);this.print(`${t} ${this.colorize(`${e}:`,c.dim)}`),o&&this.print(this.colorize(o,c.gray)),s.error&&this.print(this.colorize(s.error,c.red));}else {let o=s.output.split(`
132
+ `)[0].slice(0,80)||"(no output)";this.print(`${t} ${this.colorize(o,c.dim)}`);}}assistant(e){this.emit("assistant_message",{message:e}),this.clearStatusLine(),console.log(`
133
+ ${this.colorize(`${h.bot} Geoff:`,c.magenta)}`),console.log(e),console.log(),this.restoreStatusLine();}user(e){this.emit("user_message",{message:e}),this.clearStatusLine(),console.log(`
134
+ ${this.colorize(`${h.user} You:`,c.green)} ${e}
135
+ `),this.restoreStatusLine();}separator(){this.print(this.line());}banner(){let s=(u,p,m="")=>`${c.cyan}${u}${m.padEnd(61)}${p}${c.reset}`,t=u=>s(x.v,x.v,u),o=t(""),n=["\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2591\u2588\u2588\u2588\u2588\u2588\u2557\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2591\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2551\u2591\u2591\u2588\u2588\u2557\u2591\u2588\u2588\u2588\u2588\u2588\u2557\u2591\u2591\u2588\u2588\u2551\u2591\u2591\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557\u2591\u2591\u2588\u2588\u2588\u2588\u2588\u2557\u2591\u2591","\u2588\u2588\u2551\u2591\u2591\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D\u2591\u2591\u2588\u2588\u2551\u2591\u2591\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D\u2591\u2591\u2588\u2588\u2554\u2550\u2550\u255D\u2591\u2591","\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2551\u2591\u2591\u2591\u2591\u2591","\u2591\u255A\u2550\u2550\u2550\u2550\u2550\u255D\u2591\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u2591\u255A\u2550\u2550\u2550\u2550\u255D\u2591\u255A\u2550\u255D\u2591\u2591\u2591\u2591\u2591\u255A\u2550\u255D\u2591\u2591\u2591\u2591\u2591"],i=`${c.dim}${x.hHeavy.repeat(41)}${c.reset}${c.cyan}`,a=`${c.bright}C O D E R V E R S I O N 1 : 1${c.reset}${c.cyan}`,l=[h.diamond,"Reasoning",h.diamond,"Planning",h.diamond,"Execution",h.diamond,"Learning"].map((u,p)=>p%2===0?`${c.yellow}${u}${c.reset}${c.cyan}`:u).join(" ");console.log(),console.log(s(x.tl,x.tr,x.h.repeat(61))),console.log(o),n.forEach(u=>console.log(t(` ${c.bright}${u}${c.reset}${c.cyan} `))),console.log(o),console.log(t(` ${i} `)),console.log(t(` ${a} `)),console.log(t(` ${i} `)),console.log(o),console.log(t(` ${l} `)),console.log(o),console.log(s(x.bl,x.br,x.h.repeat(61))),console.log();}config(e){this.print(this.colorize("Configuration:",c.dim));for(let[s,t]of Object.entries(e)){let o=s==="apiKey"?"***":String(t);this.print(` ${s}: ${this.colorize(o,s==="apiKey"?c.dim:c.cyan)}`);}this.print("");}metricsUpdate(e,s){this.emit("metrics_update",{iteration:s,metrics:e});let t=((Date.now()-e.startTime)/1e3).toFixed(1),o=[`[Iter ${s}]`,`${c.cyan}ctx:${this.num(e.contextSize)}${c.dim}`,`${c.green}+${e.linesAdded}${c.dim}/${c.red}-${e.linesRemoved}${c.dim} lines`,`${c.yellow}${e.filesCreated} new${c.dim}/${c.blue}${e.filesModified} mod${c.dim} files`,`${c.magenta}${this.num(e.tokensUsed)} tok${c.dim}`,`${t}s`];this.currentMetricsLine=`\r${c.dim}${o.join(" | ")}${c.reset}`,process.stdout.write(`\r${c.clearLine}${this.currentMetricsLine}`);}clearMetricsLine(){this.currentMetricsLine=null,process.stdout.write(`\r${c.clearLine}`);}metricsSummary(e,s){this.clearMetricsLine();let t=((Date.now()-e.startTime)/1e3).toFixed(1),o=e.linesAdded-e.linesRemoved,n=o>=0?c.green:c.red,i=o>=0?"+":"";console.log(`
136
+ ${c.bright}${c.cyan}${x.hDouble.repeat(55)}${c.reset}`),console.log(`${c.bright} ${h.stats} Session Summary${c.reset}`),console.log(`${c.cyan}${x.hDouble.repeat(55)}${c.reset}
137
+ `),this.section("Tokens",h.tokens),console.log(this.label("Total:",this.num(e.tokensUsed),c.magenta)),console.log(this.label("Prompt:",this.num(e.promptTokens),c.dim)),console.log(this.label("Completion:",this.num(e.completionTokens),c.dim)),console.log(this.label("Context:",`${this.num(e.contextSize)} (final)`,c.cyan)),console.log(),this.section("Files",h.files),console.log(this.label("Created:",e.filesCreated,c.green));for(let a of e.createdFiles)console.log(this.colorize(` + ${a}`,c.dim));console.log(this.label("Modified:",e.filesModified,c.yellow));for(let a of e.modifiedFiles)console.log(this.colorize(` ~ ${a}`,c.dim));console.log(),this.section("Code Changes",h.code),console.log(this.label("Lines added:",`+${this.num(e.linesAdded)}`,c.green)),console.log(this.label("Lines removed:",`-${this.num(e.linesRemoved)}`,c.red)),console.log(this.label("Net change:",`${i}${this.num(o)}`,n)),console.log(),this.section("Execution",h.exec),console.log(this.label("Iterations:",s,c.blue)),console.log(this.label("API calls:",e.apiCalls,c.dim)),console.log(this.label("Tool calls:",e.toolCallsTotal,c.dim)),console.log(this.label("Commands run:",e.commandsExecuted,c.dim)),console.log(this.label("Duration:",`${t}s`,c.dim)),console.log(`
138
+ ${c.cyan}${x.hDouble.repeat(55)}${c.reset}
139
+ `);}iterationHeader(e,s){this.clearMetricsLine(),console.log(`
140
+ ${this.colorize(`${x.h.repeat(3)} Iteration ${e}/${s} ${x.h.repeat(40)}`,c.dim)}`);}};var E={"gpt-4o":128e3,"gpt-4o-mini":128e3,"gpt-4-turbo":128e3,"gpt-4":8192,"gpt-4-32k":32768,"gpt-3.5-turbo":16385,"gpt-3.5-turbo-16k":16385,"claude-3-opus":2e5,"claude-3-sonnet":2e5,"claude-3-haiku":2e5,"claude-3-5-sonnet":2e5,llama3:8192,"llama3:70b":8192,codellama:16384,mistral:32768,mixtral:32768,"qwen3-coder":131072,"qwen3-coder:30b":131072,"qwen3:30b":131072,"qwen3:8b":131072,qwen3:131072,"qwen2.5-coder":131072,default:32768};function P(r){if(E[r])return E[r];for(let[e,s]of Object.entries(E))if(r.startsWith(e))return s;return E.default}function he(r,e){let s=4;return e.includes("claude")?s=3.5:(e.includes("llama")||e.includes("mistral"))&&(s=3.8),Math.ceil(r.length/s)}function I(r,e){let s="";if(typeof r.content=="string")s=r.content;else if(Array.isArray(r.content))for(let n of r.content)n.type==="text"&&n.text&&(s+=n.text);let o=he(s,e)+4;if(r.tool_calls)for(let n of r.tool_calls)o+=he(n.function.name+n.function.arguments,e);return o}function D(r,e){return r.reduce((s,t)=>s+I(t,e),0)}function ye(r,e,s,t=4096){let n=(P(e))-t;if(D(r,e)<=n)return {messages:r,truncated:false,removedCount:0,summarized:false};let a=r[0]?.role==="system"?r[0]:null,l=a?r.slice(1):r,u=-1;for(let f=l.length-1;f>=0;f--)if(l[f].role==="user"){u=f;break}let p=[],m=a?I(a,e):0;if(u>=0){let f=l[u];p.unshift(f),m+=I(f,e);}for(let f=l.length-1;f>=0;f--){if(f===u)continue;let y=l[f],b=I(y,e);if(m+b<=n){let v=p.findIndex((O,w)=>l.indexOf(p[w])>f);v===-1?p.push(y):p.splice(v,0,y),m+=b;}}p.sort((f,y)=>{let b=l.indexOf(f),v=l.indexOf(y);return b-v});let d=l.length-p.length,g=[];return a&&g.push(a),d>0&&g.push({role:"user",content:`<system-notice>
141
+ Previous conversation context (${d} messages) was truncated to fit within context limits.
142
+ The conversation continues from where relevant context remains.
143
+ </system-notice>`}),g.push(...p),{messages:g,truncated:d>0,removedCount:d,summarized:d>0}}function be(r,e,s=.8){let t=P(e);return D(r,e)/t>=s}function He(){return `call_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}function We(r){let e=[],s=/<function=([^>]+)>([\s\S]*?)<\/function>/g,t=/<parameter=([^>]+)>\n?([\s\S]*?)\n?<\/parameter>/g,o;for(;(o=s.exec(r))!==null;){let n=o[1].trim(),i=o[2],a={},l;for(;(l=t.exec(i))!==null;){let u=l[1].trim(),p=l[2].trim();try{a[u]=JSON.parse(p);}catch{a[u]=p;}}t.lastIndex=0,e.push({name:n,arguments:a});}return e}function Je(r){let e=[],s=/<tool_call>([\s\S]*?)<\/tool_call>/g,t;for(;(t=s.exec(r))!==null;){let o=t[1],n=/<name>([\s\S]*?)<\/name>/i.exec(o);if(!n)continue;let i=n[1].trim(),a=/<arguments>([\s\S]*?)<\/arguments>/i.exec(o);if(!a)continue;let l={},u=a[1].trim();try{l=JSON.parse(u);}catch{let p=/<([^>]+)>([\s\S]*?)<\/\1>/g,m;for(;(m=p.exec(u))!==null;){let d=m[2].trim();try{l[m[1]]=JSON.parse(d);}catch{l[m[1]]=d;}}}e.push({name:i,arguments:l});}return e}function Ke(r){let e=[],s=/<invoke\s+name=["']([^"']+)["']>([\s\S]*?)<\/invoke>/g,t=/<parameter\s+name=["']([^"']+)["']>([\s\S]*?)<\/parameter>/g,o;for(;(o=s.exec(r))!==null;){let n=o[1].trim(),i=o[2],a={},l;for(;(l=t.exec(i))!==null;){let u=l[1].trim(),p=l[2].trim();try{a[u]=JSON.parse(p);}catch{a[u]=p;}}t.lastIndex=0,e.push({name:n,arguments:a});}return e}function Xe(r){let e=[],s=/\{[\s\n]*"name"[\s\n]*:[\s\n]*"([^"]+)"[\s\n]*,[\s\n]*"arguments"[\s\n]*:[\s\n]*(\{[^}]*\}|\{\})\}/g,t;for(;(t=s.exec(r))!==null;)try{let o=t[1],n=t[2];if(o){let i=JSON.parse(n);e.push({name:o,arguments:i});}}catch{}if(e.length===0){let o=/\{[\s\S]{0,500}?"name"[\s\S]{0,50}?:[\s\S]{0,50}?"([^"]+)"[\s\S]{0,50}?,[\s\S]{0,50}?"arguments"[\s\S]{0,50}?:[\s\S]{0,500}?\}/g;for(;(t=o.exec(r))!==null;)try{let n=t[0],i=JSON.parse(n);i.name&&e.push({name:i.name,arguments:i.arguments||{}});}catch{}}return e}function L(r){return [/<function=[^>]+>/i,/<tool_call>/i,/<invoke\s+name=/i].some(s=>s.test(r))}function Ve(r){return /\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/i.test(r)}function Ye(r){let e=[];e.push(...We(r)),e.push(...Je(r)),e.push(...Ke(r)),e.push(...Xe(r));let s=new Set;return e.filter(t=>{let o=`${t.name}:${JSON.stringify(t.arguments)}`;return s.has(o)?false:(s.add(o),true)})}function Ze(r){return r.map(e=>({id:He(),type:"function",function:{name:e.name,arguments:JSON.stringify(e.arguments)}}))}function F(r){let e=r;return e=e.replace(/<function=[^>]+>[\s\S]*?<\/function>/g,""),e=e.replace(/<tool_call>[\s\S]*?<\/tool_call>/g,""),e=e.replace(/<invoke\s+name=["'][^"']+["']>[\s\S]*?<\/invoke>/g,""),e=e.replace(/\n{3,}/g,`
144
+
145
+ `).trim(),e}function xe(r){let e=L(r),s=Ve(r);if(!e&&!s)return null;let t=Ye(r);return t.length===0?null:Ze(t)}var N=class{client;config;state;tools;logger;aborted=false;constructor(e){this.config=e,this.client=new S({apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model}),this.tools=pe(e.workingDirectory),this.logger=new T(e.verbose),this.state=this.createInitialState();}emit(e,s={}){if(this.config.onEvent){let t={type:e,timestamp:Date.now(),data:s};this.config.onEvent(t);}}createInitialMetrics(){return {tokensUsed:0,promptTokens:0,completionTokens:0,contextSize:0,filesCreated:0,filesModified:0,linesAdded:0,linesRemoved:0,commandsExecuted:0,toolCallsTotal:0,apiCalls:0,startTime:Date.now(),modifiedFiles:new Set,createdFiles:new Set}}createInitialState(){return {messages:[],iteration:0,isRunning:false,lastToolCalls:[],tokensUsed:0,metrics:this.createInitialMetrics()}}getSystemMessage(){return {role:"system",content:de({workingDirectory:this.config.workingDirectory,osInfo:`${process.platform} ${process.arch}`})}}estimateContextSize(){return D(this.state.messages,this.config.model)}manageContextWindow(){let e=ye(this.state.messages,this.config.model,void 0,this.config.maxTokens);e.truncated&&(this.logger.warn(`Context truncated: removed ${e.removedCount} messages to fit within ${P(this.config.model)} token limit`),this.state.messages=e.messages);}checkContextLimits(){if(be(this.state.messages,this.config.model,.75)){let e=this.estimateContextSize(),s=P(this.config.model);this.logger.warn(`Context usage: ${e}/${s} tokens (${Math.round(e/s*100)}%)`);}}async run(e){this.state=this.createInitialState(),this.state.isRunning=true,this.aborted=false,this.state.messages=[this.getSystemMessage()];let s=me(e,this.state);this.state.messages.push({role:"user",content:s}),this.logger.info(`Starting agent loop with task: ${e.slice(0,100)}...`),this.emit("thinking",{message:`Starting task: ${e.slice(0,100)}...`,level:"info"});let t="";try{for(;this.state.iteration<this.config.maxIterations&&!this.aborted;){if(this.state.iteration++,this.state.metrics.contextSize=this.estimateContextSize(),this.emit("iteration_start",{iteration:this.state.iteration,maxIterations:this.config.maxIterations,contextSize:this.state.metrics.contextSize}),this.emit("thinking",{message:`Iteration ${this.state.iteration}/${this.config.maxIterations}`,level:"info",icon:"\u2500"}),this.logger.metricsUpdate(this.state.metrics,this.state.iteration),this.manageContextWindow(),this.checkContextLimits(),this.state.iteration>3&&this.state.iteration%3===0){let a=fe(this.state);a&&this.state.messages.push({role:"user",content:a});}this.logger.clearMetricsLine(),this.emit("llm_call",{model:this.config.model,messageCount:this.state.messages.length});let o=await this.callLLM();if(!o){this.logger.error("No response from LLM"),this.emit("error",{message:"No response from LLM"});break}let n=typeof o.content=="string"?o.content:o.content?.map(a=>a.type==="text"?a.text:"").join("")||"";this.emit("llm_response",{content:n,hasToolCalls:!!o.tool_calls?.length,toolCallCount:o.tool_calls?.length||0,contentLength:n.length});let i=await this.processXmlToolCalls(o);if(this.state.messages.push(i),i.tool_calls&&i.tool_calls.length>0){this.state.lastToolCalls=i.tool_calls,this.state.metrics.toolCallsTotal+=i.tool_calls.length;let a=await this.handleToolCalls(i.tool_calls);for(let l of i.tool_calls)if(l.function.name==="task_complete")return t=JSON.parse(l.function.arguments).summary,this.state.isRunning=!1,this.emit("task_complete",{summary:t,metrics:{tokensUsed:this.state.metrics.tokensUsed,filesCreated:this.state.metrics.filesCreated,filesModified:this.state.metrics.filesModified,linesAdded:this.state.metrics.linesAdded,linesRemoved:this.state.metrics.linesRemoved,iterations:this.state.iteration,createdFiles:Array.from(this.state.metrics.createdFiles),modifiedFiles:Array.from(this.state.metrics.modifiedFiles)}}),this.emit("thinking",{message:"Task Complete",level:"success",icon:"\u2713",summary:(t||"").slice(0,200)}),t;if(!a)break}else if(i.content){let a=typeof i.content=="string"?i.content:i.content.map(u=>u.type==="text"?u.text:"").join("");if(this.logger.assistant(a),t=a,this.state.lastToolCalls=[],this.shouldStopLoop()||this.looksLikeCompletion(a)){this.emit("task_complete",{summary:a,metrics:{tokensUsed:this.state.metrics.tokensUsed,filesCreated:this.state.metrics.filesCreated,filesModified:this.state.metrics.filesModified,iterations:this.state.iteration,createdFiles:Array.from(this.state.metrics.createdFiles),modifiedFiles:Array.from(this.state.metrics.modifiedFiles)}}),this.emit("thinking",{message:"Task Complete",level:"success",icon:"\u2713",summary:a.slice(0,200)});break}}this.logger.metricsUpdate(this.state.metrics,this.state.iteration);}this.state.iteration>=this.config.maxIterations&&(this.logger.warn(`Reached maximum iterations (${this.config.maxIterations})`),this.emit("thinking",{message:`Reached maximum iterations (${this.config.maxIterations})`,level:"warning",icon:"\u26A0"}));}catch(o){throw this.logger.error(`Agent loop error: ${o.message}`),this.emit("error",{message:o.message,stack:o.stack}),o}finally{this.state.isRunning=false;}return t}async callLLM(){try{if(this.logger.debug("Thinking..."),this.state.metrics.apiCalls++,this.config.verbose){let e=await this.client.accumulateStream(this.state.messages,this.tools.definitions,{maxTokens:this.config.maxTokens,temperature:this.config.temperature,onToken:o=>process.stdout.write(o)});console.log();let s=typeof e.content=="string"?e.content.length:0,t=Math.ceil(s/4);return this.state.metrics.completionTokens+=t,this.state.metrics.tokensUsed+=t,e}else {let e=await this.client.createCompletion(this.state.messages,this.tools.definitions,{maxTokens:this.config.maxTokens,temperature:this.config.temperature});return e.usage&&(this.state.metrics.promptTokens+=e.usage.prompt_tokens,this.state.metrics.completionTokens+=e.usage.completion_tokens,this.state.metrics.tokensUsed+=e.usage.total_tokens,this.state.tokensUsed=this.state.metrics.tokensUsed),e.choices[0]?.message??null}}catch(e){return this.logger.error(`LLM call failed: ${e.message}`),null}}async processXmlToolCalls(e){if(e.tool_calls&&e.tool_calls.length>0)return e;let s=typeof e.content=="string"?e.content:Array.isArray(e.content)?e.content.map(o=>o.type==="text"?o.text:"").join(""):"";if(!s||!L(s))return e;this.logger.debug("Detected XML-style tool calls in response, parsing...");let t=xe(s);if(t&&t.length>0){this.logger.info(`Parsed ${t.length} XML tool call(s) to JSON format`);let o=F(s);return {...e,content:o,tool_calls:t}}if(L(s)){this.logger.debug("Regex parsing failed, attempting AI-assisted conversion...");try{let o=await this.aiAssistedToolParsing(s);if(o&&o.length>0){this.logger.info(`AI parsed ${o.length} tool call(s) from XML`);let n=F(s);return {...e,content:n,tool_calls:o}}}catch(o){this.logger.warn(`AI-assisted parsing failed: ${o.message}`);}}return e}async aiAssistedToolParsing(e){let s=`Convert the following XML-style tool calls to JSON format.
146
+
147
+ INPUT:
148
+ ${e}
149
+
150
+ OUTPUT FORMAT:
151
+ Return ONLY a JSON array of tool calls in this exact format:
152
+ [
153
+ {
154
+ "name": "tool_name",
155
+ "arguments": { "param1": "value1", "param2": "value2" }
156
+ }
157
+ ]
158
+
159
+ If there are no valid tool calls, return: []
160
+
161
+ JSON OUTPUT:`;try{let o=(await this.client.createCompletion([{role:"system",content:"You are a tool call parser. Extract tool calls from XML and output them as JSON."},{role:"user",content:s}],void 0,{maxTokens:2048,temperature:0})).choices[0]?.message?.content;if(!o||typeof o!="string")return null;let n=o.match(/\[[\s\S]*\]/);if(!n)return null;let i=JSON.parse(n[0]);return !Array.isArray(i)||i.length===0?null:i.map(a=>({id:`call_${Math.random().toString(36).substring(2,11)}`,type:"function",function:{name:a.name,arguments:JSON.stringify(a.arguments)}}))}catch(t){return this.logger.debug(`AI parsing error: ${t.message}`),null}}async handleToolCalls(e){for(let s of e){let{name:t,arguments:o}=s.function;this.logger.tool(t,o);let n;try{n=JSON.parse(o);}catch{let l={success:false,output:"",error:`Invalid JSON in tool arguments: ${o}`};this.addToolResultMessage(s.id,t,l),this.emit("tool_result",{toolCallId:s.id,toolName:t,success:false,error:l.error});continue}this.emit("tool_call",{toolCallId:s.id,toolName:t,arguments:n});let i=o.length>100?o.slice(0,100)+"...":o;this.emit("thinking",{message:`${t} ${i}`,level:"tool",icon:"\u{1F527}",toolName:t});let a=this.tools.handlers.get(t);if(!a){let l={success:false,output:"",error:`Unknown tool: ${t}`};this.addToolResultMessage(s.id,t,l),this.emit("tool_result",{toolCallId:s.id,toolName:t,success:false,error:l.error});continue}try{let l=await a(n);this.logger.toolResult(t,l),this.trackToolMetrics(t,n,l),this.emit("tool_result",{toolCallId:s.id,toolName:t,success:l.success,output:l.output.slice(0,500),error:l.error});let u=l.success?"\u2713":"\u2717",p=l.success?"success":"error",m=l.success?l.output.split(`
162
+ `)[0].slice(0,80)||"(no output)":l.error||"Unknown error";this.emit("thinking",{message:m,level:p,icon:u,toolName:t,success:l.success});let d=n.path;l.success&&d&&(t==="write_file"?this.emit("file_written",{path:d,sandboxId:this.config.sandboxId,operation:"create"}):t==="apply_diff"&&this.emit("file_modified",{path:d,sandboxId:this.config.sandboxId,operation:"modify"}));let g=ge(l.success?l.output:`Error: ${l.error}
163
+ ${l.output}`,this.state,t);this.state.messages.push({role:"tool",tool_call_id:s.id,content:g});}catch(l){let u={success:false,output:"",error:`Tool execution failed: ${l.message}`};this.addToolResultMessage(s.id,t,u),this.emit("tool_result",{toolCallId:s.id,toolName:t,success:false,error:u.error}),this.emit("thinking",{message:u.error,level:"error",icon:"\u2717",toolName:t});}}return true}trackToolMetrics(e,s,t){let o=this.state.metrics,n=s.path;if(t.success)switch(e){case "write_file":if(n){let a=s.content.split(`
164
+ `).length;!o.createdFiles.has(n)&&!o.modifiedFiles.has(n)&&(o.filesCreated++,o.createdFiles.add(n)),o.linesAdded+=a;}break;case "apply_diff":if(n){let i=s.original,a=s.replacement;!o.modifiedFiles.has(n)&&!o.createdFiles.has(n)&&(o.filesModified++,o.modifiedFiles.add(n));let l=i.split(`
165
+ `).length,u=a.split(`
166
+ `).length;o.linesRemoved+=l,o.linesAdded+=u;}break;case "execute_command":o.commandsExecuted++;break}}addToolResultMessage(e,s,t){let o=t.success?t.output:`Error: ${t.error}
167
+ ${t.output}`;this.state.messages.push({role:"tool",tool_call_id:e,content:o});}looksLikeCompletion(e){let s=["let me know if","is there anything else","feel free to ask","hope this helps","task complete","successfully completed","all done","i've created","i've built","i have created","i have built","here's what i","here is what i","i've successfully","i have successfully","the website is ready","the app is ready","the code is ready","implementation is complete","development is complete","ready to use","you can now","you should now","everything is set up","## complete","## summary","## features","## what i've"],t=e.toLowerCase();return s.some(o=>t.includes(o))}shouldStopLoop(){return (this.state.metrics.filesCreated>0||this.state.metrics.filesModified>0)&&this.state.lastToolCalls.length===0?(this.logger.info("Files created/modified and no pending tool calls - stopping loop"),true):false}abort(){this.aborted=true,this.state.isRunning=false,this.logger.warn("Agent loop aborted");}getState(){return {...this.state}}getMetrics(){return {...this.state.metrics}}getTokensUsed(){return this.state.metrics.tokensUsed}};var et=["node_modules",".git","__pycache__",".venv","venv","dist","build",".next",".nuxt","coverage",".cache",".parcel-cache","*.log",".DS_Store"];function tt(){return {model:"qwen3-coder:30b",baseUrl:"https://geoffnet.magma-rpc.com/v1",maxIterations:20,maxTokens:4096,temperature:.7,verbose:false,ignorePatterns:et,blockedCommands:[],retry:{maxRetries:3,baseDelayMs:1e3,maxDelayMs:3e4}}}function Te(r){let s={...tt()},t=join(homedir(),".geoff.json");if(existsSync(t))try{let n=JSON.parse(readFileSync(t,"utf-8"));s=ke(s,n);}catch(n){console.warn(`Warning: Failed to parse ${t}: ${n.message}`);}let o=[join(r,".geoff.json"),join(r,"geoff.config.json")];for(let n of o)if(existsSync(n))try{let i=JSON.parse(readFileSync(n,"utf-8"));s=ke(s,i);break}catch(i){console.warn(`Warning: Failed to parse ${n}: ${i.message}`);}return s}function ke(r,e){return {...r,...e,retry:{...r.retry,...e.retry},ignorePatterns:e.ignorePatterns??r.ignorePatterns,blockedCommands:e.blockedCommands??r.blockedCommands}}function $e(r){let e=[];return r.maxIterations!==void 0&&(r.maxIterations<1||r.maxIterations>100)&&e.push("maxIterations must be between 1 and 100"),r.maxTokens!==void 0&&(r.maxTokens<100||r.maxTokens>32e3)&&e.push("maxTokens must be between 100 and 32000"),r.temperature!==void 0&&(r.temperature<0||r.temperature>2)&&e.push("temperature must be between 0 and 2"),r.retry?.maxRetries!==void 0&&(r.retry.maxRetries<0||r.retry.maxRetries>10)&&e.push("retry.maxRetries must be between 0 and 10"),{valid:e.length===0,errors:e}}var q=null,Ce=false;function rt(r){let e=async s=>{Ce&&(r.warn("Force shutdown requested"),process.exit(1)),Ce=true,r.info(`
168
+ Received ${s}, shutting down gracefully...`),q&&(q.abort(),await new Promise(t=>setTimeout(t,1e3))),r.info("Shutdown complete"),process.exit(0);};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM")),process.on("uncaughtException",s=>{r.error(`Uncaught exception: ${s.message}`),console.error(s.stack),process.exit(1);}),process.on("unhandledRejection",s=>{r.error(`Unhandled rejection: ${s}`),process.exit(1);});}function nt(){let r=process.argv.slice(2),e={workingDir:process.cwd(),maxIterations:0,maxTokens:0,temperature:-1,verbose:false,interactive:false,help:false};for(let s=0;s<r.length;s++){let t=r[s];switch(t){case "-h":case "--help":e.help=true;break;case "-v":case "--verbose":e.verbose=true;break;case "-i":case "--interactive":e.interactive=true;break;case "-d":case "--dir":e.workingDir=resolve(r[++s]);break;case "--max-iterations":{let o=parseInt(r[++s]);(isNaN(o)||o<1||o>100)&&(console.error("Error: --max-iterations must be a number between 1 and 100"),process.exit(1)),e.maxIterations=o;break}case "--max-tokens":{let o=parseInt(r[++s]);(isNaN(o)||o<100||o>32e3)&&(console.error("Error: --max-tokens must be a number between 100 and 32000"),process.exit(1)),e.maxTokens=o;break}case "-t":case "--temperature":{let o=parseFloat(r[++s]);(isNaN(o)||o<0||o>2)&&(console.error("Error: --temperature must be a number between 0 and 2"),process.exit(1)),e.temperature=o;break}default:!t.startsWith("-")&&!e.prompt&&(e.prompt=t);}}return e}function it(){console.log(`
169
+ Usage: geoff [options] [prompt]
170
+
171
+ Options:
172
+ -h, --help Show this help message
173
+ -v, --verbose Enable verbose output
174
+ -i, --interactive Run in interactive mode (REPL)
175
+ -d, --dir <path> Working directory (default: current)
176
+ --max-iterations <n> Maximum loop iterations (default: 20)
177
+ --max-tokens <n> Maximum tokens per response (default: 4096)
178
+ -t, --temperature <n> Temperature (default: 0.7)
179
+
180
+ Configuration Files:
181
+ ~/.geoff.json User-level configuration
182
+ .geoff.json Project-level configuration (in working directory)
183
+ AGENTS.md Project-specific instructions for the agent
184
+
185
+ Model configuration (model, baseUrl, apiKey) is set exclusively in .geoff.json:
186
+ {
187
+ "model": "qwen3-coder:30b",
188
+ "baseUrl": "https://geoffnet.magma-rpc.com/v1",
189
+ "apiKey": "your-api-key"
190
+ }
191
+
192
+ Configuration priority (highest to lowest):
193
+ CLI arguments > Project config (.geoff.json) > User config (~/.geoff.json) > Defaults
194
+
195
+ Examples:
196
+ # Single task
197
+ geoff "Create a hello world Express server"
198
+
199
+ # Interactive mode
200
+ geoff -i
201
+
202
+ # Verbose mode with custom directory
203
+ geoff -v -d ./my-project "Add tests"
204
+ `);}async function at(r,e){r.banner(),console.log(`Type your tasks, or 'exit' to quit.
205
+ `);let t=(await import('readline')).createInterface({input:process.stdin,output:process.stdout}),o=()=>{t.question(">>> ",async n=>{let i=n.trim();if(!i){o();return}(i.toLowerCase()==="exit"||i.toLowerCase()==="quit")&&(console.log("Goodbye!"),t.close(),process.exit(0)),r.user(i);try{let l=await e().run(i);}catch(a){r.error(a.message);}o();});};o(),await new Promise(()=>{});}async function lt(r,e,s){e.banner(),e.user(r);try{let t=await s.run(r);e.separator(),e.success("Task completed");}catch(t){e.error(t.message),process.exit(1);}}async function ct(){let r=nt();r.help&&(it(),process.exit(0)),existsSync(r.workingDir)||(console.error(`Error: Working directory does not exist: ${r.workingDir}`),process.exit(1));let e=Te(r.workingDir),s=$e(e);if(!s.valid){console.error("Configuration errors:");for(let i of s.errors)console.error(` - ${i}`);process.exit(1);}let t={model:e.model,baseUrl:e.baseUrl,apiKey:e.apiKey||"",workingDir:r.workingDir,maxIterations:r.maxIterations||e.maxIterations||20,maxTokens:r.maxTokens||e.maxTokens||4096,temperature:r.temperature>=0?r.temperature:e.temperature??.7,verbose:r.verbose||e.verbose||false,interactive:r.interactive,prompt:r.prompt};t.apiKey||(console.error("Error: API key required. Add 'apiKey' to ~/.geoff.json or .geoff.json"),process.exit(1));let o=new T(t.verbose);rt(o),t.verbose&&o.config({model:t.model,baseUrl:t.baseUrl,workingDir:t.workingDir,maxIterations:t.maxIterations,temperature:t.temperature});let n=()=>{let i=new N({apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,maxIterations:t.maxIterations,maxTokens:t.maxTokens,temperature:t.temperature,workingDirectory:t.workingDir,autoApprove:true,verbose:t.verbose});return q=i,i};t.interactive||!t.prompt?await at(o,n):await lt(t.prompt,o,n());}ct().catch(r=>{console.error("Fatal error:",r),process.exit(1);});