From 895b85636a0d40ad2652022b4aed25e8d4e5b502 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 4 Feb 2026 01:59:34 +0100 Subject: [PATCH] fix(coding-agent): handle npm on Windows with shell fallback fixes #1220 --- .../coding-agent/src/core/package-manager.ts | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/src/core/package-manager.ts b/packages/coding-agent/src/core/package-manager.ts index 3135c16b..553df57e 100644 --- a/packages/coding-agent/src/core/package-manager.ts +++ b/packages/coding-agent/src/core/package-manager.ts @@ -1595,12 +1595,31 @@ export class DefaultPackageManager implements PackageManager { }; } + private resolveCommand(command: string): { command: string; shell: boolean } { + if (process.platform === "win32" && command === "npm") { + const whereResult = spawnSync("where", [command], { encoding: "utf-8" }); + const resolved = whereResult.status === 0 ? (whereResult.stdout || "").split(/\r?\n/)[0] : ""; + const lower = resolved.toLowerCase(); + if (resolved) { + if (lower.endsWith(".exe")) { + return { command: resolved, shell: false }; + } + if (lower.endsWith(".cmd") || lower.endsWith(".bat") || lower.endsWith(".ps1")) { + return { command: resolved, shell: true }; + } + } + return { command, shell: true }; + } + return { command, shell: process.platform === "win32" }; + } + private runCommand(command: string, args: string[], options?: { cwd?: string }): Promise { + const resolved = this.resolveCommand(command); return new Promise((resolvePromise, reject) => { - const child = spawn(command, args, { + const child = spawn(resolved.command, args, { cwd: options?.cwd, stdio: "inherit", - shell: process.platform === "win32", + shell: resolved.shell, }); child.on("error", reject); child.on("exit", (code) => { @@ -1614,10 +1633,11 @@ export class DefaultPackageManager implements PackageManager { } private runCommandSync(command: string, args: string[]): string { - const result = spawnSync(command, args, { + const resolved = this.resolveCommand(command); + const result = spawnSync(resolved.command, args, { stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8", - shell: process.platform === "win32", + shell: resolved.shell, }); if (result.status !== 0) { throw new Error(`Failed to run ${command} ${args.join(" ")}: ${result.stderr || result.stdout}`);