From 01e7e0c1e3055ec663e53dc2947a3b0a59536d57 Mon Sep 17 00:00:00 2001 From: 7418 <7418@openclaw.ai> Date: Fri, 13 Feb 2026 17:07:21 +0800 Subject: [PATCH] fix: parse Windows .cmd wrapper to extract real CLI script path v0.10.7 skipped .cmd wrappers entirely, but the SDK's internal fallback can't find the CLI either. Now we read the .cmd file, extract the actual .js path (e.g. node_modules/@anthropic-ai/ claude-code/cli.js), resolve %~dp0 to the wrapper's directory, and pass the resolved script path to the SDK. Regex patterns are scoped to paths containing "claude" to avoid matching unrelated .js files in the wrapper. Bump version to 0.10.8. Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 4 ++-- package.json | 2 +- src/lib/claude-client.ts | 46 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index e35d049..e9f3f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepilot", - "version": "0.10.7", + "version": "0.10.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepilot", - "version": "0.10.7", + "version": "0.10.8", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.33", "@hugeicons/core-free-icons": "^3.1.1", diff --git a/package.json b/package.json index 4644cd1..8ecec65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codepilot", - "version": "0.10.7", + "version": "0.10.8", "private": true, "author": { "name": "op7418", diff --git a/src/lib/claude-client.ts b/src/lib/claude-client.ts index 81c9fb2..52ba7d5 100644 --- a/src/lib/claude-client.ts +++ b/src/lib/claude-client.ts @@ -49,6 +49,41 @@ 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 { @@ -296,13 +331,18 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream