From 57d98eb1d7f38cc6cf1c899a17673a5ea961407a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Sormage=C3=A7?= Date: Fri, 30 Jan 2026 14:56:46 +0000 Subject: [PATCH 1/3] fix(windows): resolve command execution and binary detection issues --- src/agents/skills/config.ts | 15 +++++++++------ src/hooks/config.ts | 15 +++++++++------ src/process/exec.ts | 1 + 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/agents/skills/config.ts b/src/agents/skills/config.ts index c053f2b39..a6287f7d8 100644 --- a/src/agents/skills/config.ts +++ b/src/agents/skills/config.ts @@ -77,13 +77,16 @@ export function isBundledSkillAllowed(entry: SkillEntry, allowlist?: string[]): export function hasBinary(bin: string): boolean { const pathEnv = process.env.PATH ?? ""; const parts = pathEnv.split(path.delimiter).filter(Boolean); + const extensions = process.platform === "win32" ? [".exe", ".cmd", ".bat", ""] : [""]; for (const part of parts) { - const candidate = path.join(part, bin); - try { - fs.accessSync(candidate, fs.constants.X_OK); - return true; - } catch { - // keep scanning + for (const ext of extensions) { + const candidate = path.join(part, bin + ext); + try { + fs.accessSync(candidate, fs.constants.X_OK); + return true; + } catch { + // keep scanning + } } } return false; diff --git a/src/hooks/config.ts b/src/hooks/config.ts index 8a1da761d..0cdb17481 100644 --- a/src/hooks/config.ts +++ b/src/hooks/config.ts @@ -54,13 +54,16 @@ export function resolveRuntimePlatform(): string { export function hasBinary(bin: string): boolean { const pathEnv = process.env.PATH ?? ""; const parts = pathEnv.split(path.delimiter).filter(Boolean); + const extensions = process.platform === "win32" ? [".exe", ".cmd", ".bat", ""] : [""]; for (const part of parts) { - const candidate = path.join(part, bin); - try { - fs.accessSync(candidate, fs.constants.X_OK); - return true; - } catch { - // keep scanning + for (const ext of extensions) { + const candidate = path.join(part, bin + ext); + try { + fs.accessSync(candidate, fs.constants.X_OK); + return true; + } catch { + // keep scanning + } } } return false; diff --git a/src/process/exec.ts b/src/process/exec.ts index 44f8b2ce0..0e8397c64 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -85,6 +85,7 @@ export async function runCommandWithTimeout( cwd, env: resolvedEnv, windowsVerbatimArguments, + shell: process.platform === "win32", }); // Spawn with inherited stdin (TTY) so tools like `pi` stay interactive when needed. return await new Promise((resolve, reject) => { From 950377f041e667a97f4f4b68f7cf1566cf33d405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Sormage=C3=A7?= Date: Fri, 30 Jan 2026 16:17:36 +0000 Subject: [PATCH 2/3] fix(windows): handle undefined environment variables in runCommandWithTimeout --- src/process/exec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/process/exec.ts b/src/process/exec.ts index 0e8397c64..a56319913 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -73,7 +73,11 @@ export async function runCommandWithTimeout( return false; })(); - const resolvedEnv = env ? { ...process.env, ...env } : { ...process.env }; + const resolvedEnv = Object.fromEntries( + Object.entries({ ...process.env, ...(env ?? {}) }) + .filter(([, value]) => value !== undefined) + .map(([key, value]) => [key, String(value)]), + ); if (shouldSuppressNpmFund) { if (resolvedEnv.NPM_CONFIG_FUND == null) resolvedEnv.NPM_CONFIG_FUND = "false"; if (resolvedEnv.npm_config_fund == null) resolvedEnv.npm_config_fund = "false"; From 55d6f768fc4fcde6d329bf80935ab661d7deeb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Sormage=C3=A7?= Date: Fri, 30 Jan 2026 16:31:35 +0000 Subject: [PATCH 3/3] fix(windows): normalize env entries for spawn --- src/process/exec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/process/exec.ts b/src/process/exec.ts index a56319913..371302e31 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -73,8 +73,9 @@ export async function runCommandWithTimeout( return false; })(); + const mergedEnv = env ? { ...process.env, ...env } : { ...process.env }; const resolvedEnv = Object.fromEntries( - Object.entries({ ...process.env, ...(env ?? {}) }) + Object.entries(mergedEnv) .filter(([, value]) => value !== undefined) .map(([key, value]) => [key, String(value)]), );