From c795e498c1445753d0173078919cb92703d0d424 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 22 Mar 2026 19:04:01 -0700 Subject: [PATCH] fix: resolve claude binary path for daemon-spawned agent The browse server runs as a daemon and may not inherit the user's shell PATH. Add findClaudeBin() that checks ~/.local/bin/claude (standard install location), which claude, and common system paths. Shows a clear error in the sidebar chat if claude CLI is not found. Co-Authored-By: Claude Opus 4.6 (1M context) --- browse/src/server.ts | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/browse/src/server.ts b/browse/src/server.ts index 5767eda9..7fdc3cab 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -141,6 +141,27 @@ function findBrowseBin(): string { const BROWSE_BIN = findBrowseBin(); +function findClaudeBin(): string | null { + const candidates = [ + path.join(process.env.HOME || '', '.local', 'bin', 'claude'), + path.join(process.env.HOME || '', '.local', 'share', 'claude', 'versions', 'latest'), + '/usr/local/bin/claude', + '/opt/homebrew/bin/claude', + ]; + // Also check if 'claude' is in current PATH (works when spawned from shell) + try { + const proc = Bun.spawnSync(['which', 'claude'], { stdout: 'pipe', stderr: 'pipe', timeout: 2000 }); + if (proc.exitCode === 0) { + const p = proc.stdout.toString().trim(); + if (p) candidates.unshift(p); + } + } catch {} + for (const c of candidates) { + try { if (fs.existsSync(c)) return c; } catch {} + } + return null; +} + function shortenPath(str: string): string { return str .replace(new RegExp(BROWSE_BIN.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '$B') @@ -362,9 +383,19 @@ function spawnClaude(userMessage: string): void { addChatEntry({ ts: new Date().toISOString(), role: 'agent', type: 'agent_start' }); - const proc = spawn('claude', args, { + // Resolve claude binary — daemon process may not have user's PATH + const claudeBin = findClaudeBin(); + if (!claudeBin) { + addChatEntry({ ts: new Date().toISOString(), role: 'agent', type: 'agent_error', error: 'Claude CLI not found. Install: npm install -g @anthropic-ai/claude-code' }); + agentStatus = 'idle'; + agentStartTime = null; + currentMessage = null; + return; + } + + const proc = spawn(claudeBin, args, { stdio: ['pipe', 'pipe', 'pipe'], - cwd: sidebarSession?.worktreePath || process.cwd(), + cwd: (sidebarSession as any)?.worktreePath || process.cwd(), env: { ...process.env, BROWSE_STATE_FILE: config.stateFile }, } as any); proc.stdin?.end();