/** * Bug Fixer Agent * * A persistent agent that finds and fixes bugs in code. * Remembers the codebase and past fixes across sessions. */ import { readFile, writeFile } from 'node:fs/promises'; import { existsSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { createSession, resumeSession, type Session } from '../../src/index.js'; import { BugFixerState, DEFAULT_CONFIG } from './types.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const STATE_FILE = join(__dirname, 'state.json'); // ANSI colors const COLORS = { agent: '\x1b[36m', // Cyan system: '\x1b[90m', // Gray success: '\x1b[32m', // Green error: '\x1b[31m', // Red reset: '\x1b[0m', }; const SYSTEM_PROMPT = `You are a bug-fixing agent. Your job is to find and fix bugs in code. ## Your Capabilities You have access to tools for exploring and modifying code: - Read files to understand the codebase - Search for patterns with Grep - Find files with Glob - Edit files to fix bugs - Run commands with Bash (tests, linters, etc.) ## Your Approach 1. **Understand the bug** - Read the error message or failing test carefully 2. **Explore the codebase** - Find relevant files and understand the context 3. **Identify the root cause** - Don't just fix symptoms, find the actual issue 4. **Make minimal changes** - Fix the bug without unnecessary refactoring 5. **Verify the fix** - Run tests or the command that was failing ## Memory You remember past sessions. Use this to: - Recall where things are in the codebase - Remember patterns that caused bugs before - Avoid repeating failed approaches ## Style - Be concise but thorough - Explain what you're doing and why - If you're unsure, say so and explain your reasoning`; /** * Load state from disk */ export async function loadState(): Promise { if (existsSync(STATE_FILE)) { const data = await readFile(STATE_FILE, 'utf-8'); return JSON.parse(data); } return { agentId: null, fixCount: 0, }; } /** * Save state to disk */ export async function saveState(state: BugFixerState): Promise { await writeFile(STATE_FILE, JSON.stringify(state, null, 2)); } /** * Get or create the bug fixer agent */ export async function getOrCreateAgent(state: BugFixerState): Promise { if (state.agentId) { console.log(`${COLORS.system}Resuming bug fixer agent...${COLORS.reset}`); return resumeSession(state.agentId, { model: DEFAULT_CONFIG.model, allowedTools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'], permissionMode: 'bypassPermissions', }); } console.log(`${COLORS.system}Creating new bug fixer agent...${COLORS.reset}`); const session = await createSession({ model: DEFAULT_CONFIG.model, systemPrompt: SYSTEM_PROMPT, memory: [ { label: 'codebase-knowledge', value: `# Codebase Knowledge ## Project Structure (Will be populated as I explore) ## Key Files (Important files I've discovered) ## Patterns (Common patterns in this codebase)`, description: 'What I know about this codebase', }, { label: 'fix-history', value: `# Fix History ## Past Bugs (Bugs I've fixed before) ## Lessons Learned (What I've learned from past fixes)`, description: 'History of bugs fixed and lessons learned', }, ], allowedTools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'], permissionMode: 'bypassPermissions', }); return session; } /** * Stream output with color */ function createStreamPrinter(): (text: string) => void { return (text: string) => { process.stdout.write(`${COLORS.agent}${text}${COLORS.reset}`); }; } /** * Send a message and get response */ export async function chat( session: Session, message: string, onOutput?: (text: string) => void ): Promise { await session.send(message); let response = ''; const printer = onOutput || createStreamPrinter(); for await (const msg of session.stream()) { if (msg.type === 'assistant') { response += msg.content; printer(msg.content); } else if (msg.type === 'tool_call') { // @ts-ignore - tool name might be in different places const toolName = msg.name || msg.tool || 'tool'; console.log(`\n${COLORS.system}[${toolName}]${COLORS.reset}`); } else if (msg.type === 'tool_result') { console.log(`${COLORS.system}[done]${COLORS.reset}\n`); } } return response; } /** * Fix a bug */ export async function fixBug( session: Session, state: BugFixerState, description: string ): Promise { console.log(`\n${COLORS.system}Starting bug fix...${COLORS.reset}\n`); const prompt = `Please fix this bug: ${description} Steps: 1. First, explore the codebase to understand the context 2. Find the root cause of the bug 3. Make the minimal fix needed 4. Run any relevant tests to verify Go ahead and fix it.`; await chat(session, prompt, createStreamPrinter()); // Update fix count state.fixCount++; await saveState(state); console.log(`\n\n${COLORS.success}Bug fix attempt complete.${COLORS.reset}`); console.log(`${COLORS.system}Total fixes attempted: ${state.fixCount}${COLORS.reset}\n`); } /** * Interactive mode - keep fixing bugs */ export async function interactiveMode(session: Session, state: BugFixerState): Promise { const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); const askQuestion = (prompt: string): Promise => { return new Promise((resolve) => { rl.question(prompt, (answer) => { resolve(answer); }); }); }; console.log(`${COLORS.system}Interactive mode. Describe bugs to fix, or type 'quit' to exit.${COLORS.reset}\n`); while (true) { const input = await askQuestion(`${COLORS.agent}Describe the bug > ${COLORS.reset}`); if (input.toLowerCase() === 'quit' || input.toLowerCase() === 'exit') { break; } if (!input.trim()) continue; await fixBug(session, state, input); } rl.close(); } /** * Show status */ export async function showStatus(state: BugFixerState): Promise { console.log('\nšŸ› Bug Fixer Status\n'); console.log(`Agent: ${state.agentId || '(not created yet)'}`); if (state.agentId) { console.log(` → https://app.letta.com/agents/${state.agentId}`); } console.log(`Fixes attempted: ${state.fixCount}`); console.log(''); } /** * Reset state */ export async function reset(): Promise { if (existsSync(STATE_FILE)) { const fs = await import('node:fs/promises'); await fs.unlink(STATE_FILE); } console.log('\nšŸ—‘ļø Bug fixer reset. Agent forgotten.\n'); }