From 120949db19b91816c1fedc816b8d757f267dafc0 Mon Sep 17 00:00:00 2001 From: mastertyko <11311479+mastertyko@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:12:53 +0200 Subject: [PATCH] fix(claude-code): use native Windows claude lookup --- .../claude-code-cli/stream-adapter.ts | 15 ++++++++++++--- .../tests/stream-adapter.test.ts | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/resources/extensions/claude-code-cli/stream-adapter.ts b/src/resources/extensions/claude-code-cli/stream-adapter.ts index 70af68108..b1eb8acca 100644 --- a/src/resources/extensions/claude-code-cli/stream-adapter.ts +++ b/src/resources/extensions/claude-code-cli/stream-adapter.ts @@ -50,6 +50,17 @@ function createAssistantStream(): AssistantMessageEventStream { let cachedClaudePath: string | null = null; +export function getClaudeLookupCommand(platform: NodeJS.Platform = process.platform): string { + return platform === "win32" ? "where claude" : "which claude"; +} + +export function parseClaudeLookupOutput(output: Buffer | string): string { + return output + .toString() + .trim() + .split(/\r?\n/)[0] ?? ""; +} + /** * Resolve the path to the system-installed `claude` binary. * The SDK defaults to a bundled cli.js which doesn't exist when @@ -58,9 +69,7 @@ let cachedClaudePath: string | null = null; function getClaudePath(): string { if (cachedClaudePath) return cachedClaudePath; try { - cachedClaudePath = execSync("which claude", { timeout: 5_000, stdio: "pipe" }) - .toString() - .trim(); + cachedClaudePath = parseClaudeLookupOutput(execSync(getClaudeLookupCommand(), { timeout: 5_000, stdio: "pipe" })); } catch { cachedClaudePath = "claude"; // fall back to PATH resolution } diff --git a/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts b/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts index 983c7a369..40fd399dc 100644 --- a/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +++ b/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts @@ -4,6 +4,8 @@ import { makeStreamExhaustedErrorMessage, buildPromptFromContext, buildSdkOptions, + getClaudeLookupCommand, + parseClaudeLookupOutput, } from "../stream-adapter.ts"; import type { Context, Message } from "@gsd/pi-ai"; @@ -126,3 +128,19 @@ describe("stream-adapter — session persistence (#2859)", () => { ); }); }); + +describe("stream-adapter — Windows Claude path lookup (#3770)", () => { + test("getClaudeLookupCommand uses where on Windows", () => { + assert.equal(getClaudeLookupCommand("win32"), "where claude"); + }); + + test("getClaudeLookupCommand uses which on non-Windows platforms", () => { + assert.equal(getClaudeLookupCommand("darwin"), "which claude"); + assert.equal(getClaudeLookupCommand("linux"), "which claude"); + }); + + test("parseClaudeLookupOutput keeps the first native path from multi-line lookup output", () => { + const output = "C:\\Users\\Binoy\\.local\\bin\\claude.exe\r\nC:\\Program Files\\Claude\\claude.exe\r\n"; + assert.equal(parseClaudeLookupOutput(output), "C:\\Users\\Binoy\\.local\\bin\\claude.exe"); + }); +});