From 9024fa088a72781dba5f32ccc8d52ed6877ea381 Mon Sep 17 00:00:00 2001 From: unknown <2110307107@stu.pku.edu.cn> Date: Thu, 2 Apr 2026 20:25:03 +0800 Subject: [PATCH] fix: Remove pathToClaudeCodeExecutable to support native Claude CLI on Windows Let SDK use its built-in cli.js with expanded PATH from getExpandedPath(). This fixes spawn EINVAL errors with native Claude CLI installations on Windows. - Delete resolveScriptFromCmd() and findClaudePath() functions - Remove pathToClaudeCodeExecutable from all SDK call sites - ~30 lines removed, simpler and more reliable Fixes #410 --- src/lib/claude-client.ts | 77 +++----------------------------------- src/lib/provider-doctor.ts | 35 +---------------- 2 files changed, 7 insertions(+), 105 deletions(-) diff --git a/src/lib/claude-client.ts b/src/lib/claude-client.ts index c8df8215..58bf1178 100644 --- a/src/lib/claude-client.ts +++ b/src/lib/claude-client.ts @@ -20,7 +20,7 @@ import { registerConversation, unregisterConversation } from './conversation-reg import { captureCapabilities, isCacheFresh, setCachedPlugins } from './agent-sdk-capabilities'; import { getSetting, updateSdkSessionId, createPermissionRequest } from './db'; import { resolveForClaudeCode, toClaudeCodeEnv } from './provider-resolver'; -import { findClaudeBinary, findGitBash, getExpandedPath, invalidateClaudePathCache } from './platform'; +import { findGitBash, getExpandedPath } from './platform'; import { notifyPermissionRequest, notifyGeneric } from './telegram-bot'; import { classifyError, formatClassifiedError } from './error-classifier'; import { resolveWorkingDirectory } from './working-directory'; @@ -53,49 +53,8 @@ function sanitizeEnv(env: Record): Record { return clean; } -/** - * On Windows, npm installs CLI tools as .cmd wrappers that can't be - * spawned without shell:true. Parse the wrapper to extract the real - * .js script path so we can pass it to the SDK directly. - */ -function resolveScriptFromCmd(cmdPath: string): string | undefined { - try { - const content = fs.readFileSync(cmdPath, 'utf-8'); - const cmdDir = path.dirname(cmdPath); - - // npm .cmd wrappers typically contain a line like: - // "%~dp0\node_modules\@anthropic-ai\claude-code\cli.js" %* - // Match paths containing claude-code or claude-agent and ending in .js - const patterns = [ - // Quoted: "%~dp0\...\cli.js" - /"%~dp0\\([^"]*claude[^"]*\.js)"/i, - // Unquoted: %~dp0\...\cli.js - /%~dp0\\(\S*claude\S*\.js)/i, - // Quoted with %dp0%: "%dp0%\...\cli.js" - /"%dp0%\\([^"]*claude[^"]*\.js)"/i, - ]; - - for (const re of patterns) { - const m = content.match(re); - if (m) { - const resolved = path.normalize(path.join(cmdDir, m[1])); - if (fs.existsSync(resolved)) return resolved; - } - } - } catch { - // ignore read errors - } - return undefined; -} - -let cachedClaudePath: string | null | undefined; - -function findClaudePath(): string | undefined { - if (cachedClaudePath !== undefined) return cachedClaudePath || undefined; - const found = findClaudeBinary(); - cachedClaudePath = found ?? null; - return found; -} +// Removed resolveScriptFromCmd() and findClaudePath() — let SDK use its built-in cli.js +// with expanded PATH from getExpandedPath() which includes all Claude installation paths. /** * Invalidate the cached Claude binary path in this module AND in platform.ts. @@ -329,16 +288,7 @@ export async function generateTextViaSdk(params: { queryOptions.model = params.model; } - const claudePath = findClaudePath(); - if (claudePath) { - const ext = path.extname(claudePath).toLowerCase(); - if (ext === '.cmd' || ext === '.bat') { - const scriptPath = resolveScriptFromCmd(claudePath); - if (scriptPath) queryOptions.pathToClaudeCodeExecutable = scriptPath; - } else { - queryOptions.pathToClaudeCodeExecutable = claudePath; - } - } + // Let SDK use its built-in cli.js with expanded PATH from getExpandedPath() const conversation = query({ prompt: params.prompt, @@ -481,24 +431,7 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream): Record { stderr: stderrCallback, }; - // Resolve executable path (handle Windows .cmd wrappers) - const ext = path.extname(claudePath).toLowerCase(); - if (ext === '.cmd' || ext === '.bat') { - const scriptPath = resolveScriptFromCmd(claudePath); - if (scriptPath) queryOptions.pathToClaudeCodeExecutable = scriptPath; - } else { - queryOptions.pathToClaudeCodeExecutable = claudePath; - } + // Let SDK use its built-in cli.js with expanded PATH from getExpandedPath() // 6. Run the probe try {