feat(v2): auto-detect Claude CLI path and pass to SDK via pathToClaudeCodeExecutable

Both sidecar runners (agent-runner.ts and agent-runner-deno.ts) now include
findClaudeCli() which checks common paths (~/.local/bin/claude,
~/.claude/local/claude, /usr/local/bin/claude, /usr/bin/claude) and falls
back to `which claude`. The resolved path is passed to the SDK query()
options as pathToClaudeCodeExecutable. If the CLI is not found, an
agent_error is emitted immediately instead of a cryptic SDK failure.
This commit is contained in:
Hibryda 2026-03-07 01:28:04 +01:00
parent 14b62da729
commit d35b3dc7fc
2 changed files with 68 additions and 0 deletions

View file

@ -71,10 +71,16 @@ async function handleQuery(msg: QueryMessage) {
}
}
if (!claudePath) {
send({ type: "agent_error", sessionId, message: "Claude CLI not found. Install Claude Code first." });
return;
}
try {
const q = query({
prompt,
options: {
pathToClaudeCodeExecutable: claudePath,
abortController: controller,
cwd: cwd || Deno.cwd(),
env: cleanEnv,
@ -144,6 +150,32 @@ function handleStop(msg: StopMessage) {
controller.abort();
}
function findClaudeCli(): string | undefined {
const home = Deno.env.get("HOME") ?? Deno.env.get("USERPROFILE") ?? "";
const candidates = [
`${home}/.local/bin/claude`,
`${home}/.claude/local/claude`,
"/usr/local/bin/claude",
"/usr/bin/claude",
];
for (const p of candidates) {
try { Deno.statSync(p); return p; } catch { /* not found */ }
}
try {
const proc = new Deno.Command("which", { args: ["claude"], stdout: "piped", stderr: "null" });
const out = new TextDecoder().decode(proc.outputSync().stdout).trim();
if (out) return out.split("\n")[0];
} catch { /* not found */ }
return undefined;
}
const claudePath = findClaudeCli();
if (claudePath) {
log(`Found Claude CLI at ${claudePath}`);
} else {
log("WARNING: Claude CLI not found — agent sessions will fail");
}
// Main: read NDJSON from stdin
log("Sidecar started (Deno)");
send({ type: "ready" });