@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/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # πŸš€ Geoff Coder
2
+
3
+ A terminal-based AI coding agent built with **Bun.js** featuring an agentic loop with tool call support. This agent can read files, write code, execute commands, and iterate on tasks autonomously.
4
+
5
+ ## Features
6
+
7
+ - **Agentic Loop**: Autonomous task execution with tool calling
8
+ - **System Reminders**: Context-aware reminders injected at key points (inspired by Claude Code)
9
+ - **OpenAI-Compatible API**: Works with any OpenAI-compatible inference API
10
+ - **Containerized**: Docker support for isolated execution
11
+ - **Tool Suite**: File operations, shell commands, code search, diff application
12
+ - **Streaming Support**: Real-time response streaming for verbose mode
13
+
14
+ ## Architecture
15
+
16
+ The system is built around patterns observed in production coding agents like Claude Code, Cline, and OpenCode:
17
+
18
+ ```
19
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
20
+ β”‚ CLI Interface β”‚
21
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
22
+ β”‚ Agent Loop β”‚
23
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
24
+ β”‚ β”‚ Message │───▢│ LLM Call │───▢│ Tool β”‚ β”‚
25
+ β”‚ β”‚ History β”‚ β”‚ (Stream) β”‚ β”‚ Execution β”‚ β”‚
26
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
27
+ β”‚ β–² β”‚ β”‚
28
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
29
+ β”‚ └─────────│ System β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
30
+ β”‚ β”‚ Reminders β”‚ β”‚
31
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
32
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
33
+ β”‚ Tool Registry β”‚
34
+ β”‚ read_file β”‚ write_file β”‚ execute_command β”‚ search_files β”‚
35
+ β”‚ apply_diff β”‚ list_files β”‚ task_complete β”‚
36
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
37
+ ```
38
+
39
+ ### Key Components
40
+
41
+ 1. **Agent Loop** (`src/agent/loop.ts`): The core execution engine that:
42
+ - Manages conversation history
43
+ - Calls the LLM with tool definitions
44
+ - Handles tool call responses
45
+ - Injects system reminders at appropriate times
46
+ - Iterates until task completion
47
+
48
+ 2. **System Reminders** (`src/prompts/system.ts`): Context-aware prompts injected at:
49
+ - Pre-user message (task focus)
50
+ - Post-tool result (verification reminders)
51
+ - Context refresh (iteration warnings)
52
+ - Task boundaries (completion prompts)
53
+
54
+ 3. **Tool Registry** (`src/tools/registry.ts`): Extensible tool system with:
55
+ - File read/write operations
56
+ - Shell command execution
57
+ - Code search (grep)
58
+ - Diff application for surgical edits
59
+ - Task completion signaling
60
+
61
+ 4. **API Client** (`src/agent/api-client.ts`): OpenAI-compatible client with:
62
+ - Streaming support
63
+ - Tool call accumulation
64
+ - Error handling and retries
65
+
66
+ ## Installation
67
+
68
+ ### Prerequisites
69
+
70
+ - [Bun](https://bun.sh) v1.0+
71
+ - An OpenAI-compatible API key
72
+
73
+ ### Local Installation
74
+
75
+ ```bash
76
+ # Clone or download the project
77
+ cd geoff-coder
78
+
79
+ # Install dependencies
80
+ bun install
81
+
82
+ # Set your API key
83
+ export OPENAI_API_KEY="your-api-key"
84
+
85
+ # Run the agent
86
+ bun run src/index.ts "Create a hello world Express server"
87
+ ```
88
+
89
+ ### Docker Installation
90
+
91
+ ```bash
92
+ # Build the image
93
+ docker build -t geoff-coder .
94
+
95
+ # Run interactively
96
+ docker run -it --rm \
97
+ -e OPENAI_API_KEY="your-api-key" \
98
+ -v $(pwd)/workspace:/workspace \
99
+ geoff-coder
100
+
101
+ # Or use docker-compose
102
+ export OPENAI_API_KEY="your-api-key"
103
+ docker compose up
104
+ ```
105
+
106
+ ## Usage
107
+
108
+ ### Single Task Mode
109
+
110
+ ```bash
111
+ # Execute a single task
112
+ bun run src/index.ts "Create a REST API with Express"
113
+
114
+ # With verbose output
115
+ bun run src/index.ts -v "Fix the bug in auth.ts"
116
+
117
+ # With custom model
118
+ bun run src/index.ts -m "gpt-4-turbo" "Write tests for utils"
119
+ ```
120
+
121
+ ### Interactive Mode
122
+
123
+ ```bash
124
+ # Start interactive REPL
125
+ bun run src/index.ts -i
126
+
127
+ # In the REPL
128
+ >>> Create a new React component for user profiles
129
+ >>> Add validation to the form
130
+ >>> exit
131
+ ```
132
+
133
+ ### With Local LLM (Ollama)
134
+
135
+ ```bash
136
+ # Using Ollama
137
+ bun run src/index.ts \
138
+ -u "http://localhost:11434/v1" \
139
+ -m "llama3" \
140
+ -k "not-needed" \
141
+ "Explain this codebase"
142
+
143
+ # Or with docker-compose
144
+ docker compose --profile local up
145
+ ```
146
+
147
+ ### CLI Options
148
+
149
+ ```
150
+ Usage: bun run src/index.ts [options] [prompt]
151
+
152
+ Options:
153
+ -h, --help Show help message
154
+ -v, --verbose Enable verbose output
155
+ -i, --interactive Run in interactive mode (REPL)
156
+ -m, --model <model> Model to use (default: gpt-4o)
157
+ -u, --base-url <url> API base URL
158
+ -k, --api-key <key> API key
159
+ -d, --dir <path> Working directory
160
+ --max-iterations <n> Maximum loop iterations (default: 20)
161
+ --max-tokens <n> Maximum tokens per response (default: 4096)
162
+ -t, --temperature <n> Temperature (default: 0.7)
163
+ ```
164
+
165
+ ### Environment Variables
166
+
167
+ ```bash
168
+ OPENAI_API_KEY # Required: Your API key
169
+ OPENAI_BASE_URL # Optional: API endpoint (default: https://api.openai.com/v1)
170
+ OPENAI_MODEL # Optional: Model name (default: gpt-4o)
171
+ ```
172
+
173
+ ## Customization
174
+
175
+ ### Adding Custom Tools
176
+
177
+ Extend the tool registry in `src/tools/registry.ts`:
178
+
179
+ ```typescript
180
+ // Add to toolDefinitions array
181
+ {
182
+ type: "function",
183
+ function: {
184
+ name: "my_custom_tool",
185
+ description: "Description of what this tool does",
186
+ parameters: {
187
+ type: "object",
188
+ properties: {
189
+ param1: { type: "string", description: "Parameter description" }
190
+ },
191
+ required: ["param1"]
192
+ }
193
+ }
194
+ }
195
+
196
+ // Add handler in createToolRegistry function
197
+ handlers.set("my_custom_tool", async (args): Promise<ToolResult> => {
198
+ // Implementation
199
+ return { success: true, output: "Result" }
200
+ })
201
+ ```
202
+
203
+ ### Adding System Reminders
204
+
205
+ Extend the reminders in `src/prompts/system.ts`:
206
+
207
+ ```typescript
208
+ systemReminders.push({
209
+ id: "my_reminder",
210
+ content: "<system-reminder>Your reminder text</system-reminder>",
211
+ injectionPoint: "post_tool_result",
212
+ priority: 5,
213
+ condition: (state) => state.iteration > 3
214
+ })
215
+ ```
216
+
217
+ ### Project-Specific Instructions
218
+
219
+ Create an `AGENTS.md` file in your project root with custom instructions that will be loaded by the agent.
220
+
221
+ ## How It Works
222
+
223
+ ### The Agentic Loop
224
+
225
+ 1. **User Input**: User provides a task description
226
+ 2. **System Prompt**: Base instructions + system reminders are assembled
227
+ 3. **LLM Call**: Request sent to the model with tool definitions
228
+ 4. **Response Processing**:
229
+ - If tool calls: Execute tools, add results to history, continue loop
230
+ - If text only: Check for completion indicators
231
+ 5. **Iteration**: Loop continues until:
232
+ - `task_complete` tool is called
233
+ - Max iterations reached
234
+ - No more tool calls and response looks complete
235
+
236
+ ### System Reminders (Key Innovation)
237
+
238
+ Inspired by Claude Code's `<system-reminder>` pattern, reminders are injected:
239
+
240
+ - **Pre-user message**: Focus on task, avoid unnecessary changes
241
+ - **Post-tool result**: Verify changes after file operations
242
+ - **Context refresh**: Warning after many iterations
243
+ - **Task boundary**: Prompt to use `task_complete`
244
+
245
+ This helps maintain agent focus and prevents drift in long-running tasks.
246
+
247
+ ## Comparison with Similar Tools
248
+
249
+ | Feature | This Agent | Claude Code | Cline | OpenCode |
250
+ |---------|-----------|-------------|-------|----------|
251
+ | Open Source | βœ… | ❌ | βœ… | βœ… |
252
+ | Provider Agnostic | βœ… | ❌ | βœ… | βœ… |
253
+ | System Reminders | βœ… | βœ… | ❌ | ❌ |
254
+ | Containerized | βœ… | ❌ | ❌ | ❌ |
255
+ | Runtime | Bun | Node | Node | Go/Bun |
256
+ | IDE Integration | ❌ | βœ… | βœ… | ❌ |
257
+
258
+ ## Development
259
+
260
+ ```bash
261
+ # Run in development mode (with auto-reload)
262
+ bun --watch run src/index.ts -i
263
+
264
+ # Type check
265
+ bun run tsc --noEmit
266
+
267
+ # Build for distribution
268
+ bun build src/index.ts --outdir dist --target bun
269
+ ```
270
+
271
+ ## License
272
+
273
+ MIT
274
+
275
+ ## Acknowledgments
276
+
277
+ Architecture patterns inspired by:
278
+ - [Claude Code](https://claude.ai) - System reminder injection pattern
279
+ - [Cline](https://github.com/cline/cline) - Tool definitions and agentic loop
280
+ - [OpenCode](https://github.com/sst/opencode) - CLI design and provider abstraction
281
+ - [OpenAI Codex CLI](https://github.com/openai/codex) - Agent loop implementation
package/dist/cli.cjs ADDED
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';var C=require('fs/promises'),_=require('path'),child_process=require('child_process'),fs=require('fs'),os=require('os');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var C__namespace=/*#__PURE__*/_interopNamespace(C);var ___namespace=/*#__PURE__*/_interopNamespace(_);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=___namespace.join(e,Me,Ae),t=`${"default"}.json`;return ___namespace.join(s,t)}async function Ie(r,e){let s=Ee(),t=___namespace.dirname(s);await C__namespace.mkdir(t,{recursive:true});let o={todos:r,sessionId:"default",updatedAt:new Date().toISOString()};await C__namespace.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=child_process.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 C.stat(n.resolvedPath)).isDirectory()){let l=await C.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 C.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 C.mkdir(_.dirname(n.resolvedPath),{recursive:!0}),await C.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 C.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=child_process.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=child_process.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 C.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 C.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(fs.existsSync(s))try{return fs.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(os.homedir(),".geoff.json");if(fs.existsSync(t))try{let n=JSON.parse(fs.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(fs.existsSync(n))try{let i=JSON.parse(fs.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)),fs.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);});