From 472d302133b4831ebab5ada7e01c322b158298fe Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 2 Apr 2026 12:50:56 +0800 Subject: [PATCH 1/3] fix: add shell:true on Windows to spawn bun from npm Windows npm installs bun as a shell script (C:\Users\...\AppData\Roaming\npm\bun), not a native executable. Without shell:true, spawn() fails with ENOENT when trying to execute it. Fixes Stop hook failure: "Failed to start Bun: spawn bun ENOENT" --- plugin/scripts/bun-runner.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin/scripts/bun-runner.js b/plugin/scripts/bun-runner.js index 90ee0997..8c340cd7 100644 --- a/plugin/scripts/bun-runner.js +++ b/plugin/scripts/bun-runner.js @@ -152,11 +152,13 @@ const stdinData = await collectStdin(); // Spawn Bun with the provided script and args // Use spawn (not spawnSync) to properly handle stdio -// Note: Don't use shell mode on Windows - it breaks paths with spaces in usernames +// Use shell mode on Windows because npm-installed bun is a .cmd/.sh script, +// not a native executable. Without shell:true, spawn() fails with ENOENT. // Use windowsHide to prevent a visible console window from spawning on Windows const child = spawn(bunPath, args, { stdio: [stdinData ? 'pipe' : 'ignore', 'inherit', 'inherit'], windowsHide: true, + shell: IS_WINDOWS, env: process.env }); From 4d4b0a2f24dc9d718278f561aaacc1494879166e Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 2 Apr 2026 13:00:15 +0800 Subject: [PATCH 2/3] fix: prefer bun.cmd over bun shell script on Windows Windows npm installs both bun (shell script) and bun.cmd (batch file). When spawning bun, cmd.exe cannot execute the shell script directly. This change makes findBun() return the full path to bun.cmd on Windows. Fixes: Stop hook "spawn bun ENOENT" on Windows --- plugin/scripts/bun-runner.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin/scripts/bun-runner.js b/plugin/scripts/bun-runner.js index 8c340cd7..e99d07d4 100644 --- a/plugin/scripts/bun-runner.js +++ b/plugin/scripts/bun-runner.js @@ -55,6 +55,13 @@ function findBun() { }); if (pathCheck.status === 0 && pathCheck.stdout.trim()) { + // On Windows, prefer bun.cmd over bun (bun is a shell script, bun.cmd is the Windows batch file) + if (IS_WINDOWS) { + const bunCmdPath = pathCheck.stdout.split('\n').find(line => line.trim().endsWith('bun.cmd')); + if (bunCmdPath) { + return bunCmdPath.trim(); + } + } return 'bun'; // Found in PATH } From bd47a919a8c0a945b0701da7c83fc29e02b83309 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 2 Apr 2026 13:06:12 +0800 Subject: [PATCH 3/3] fix: use cmd /c to execute bun.cmd on Windows Instead of using shell:true with spawn(), use cmd.exe as the command with /c flag to properly execute bun.cmd on Windows. Without this, spawn() with shell:true fails because cmd.exe doesn't know how to handle the bun shell script directly. Fixes: Stop hook "Failed to start Bun: spawn bun ENOENT" --- plugin/scripts/bun-runner.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugin/scripts/bun-runner.js b/plugin/scripts/bun-runner.js index e99d07d4..09fc0899 100644 --- a/plugin/scripts/bun-runner.js +++ b/plugin/scripts/bun-runner.js @@ -159,15 +159,24 @@ const stdinData = await collectStdin(); // Spawn Bun with the provided script and args // Use spawn (not spawnSync) to properly handle stdio -// Use shell mode on Windows because npm-installed bun is a .cmd/.sh script, -// not a native executable. Without shell:true, spawn() fails with ENOENT. +// On Windows, use cmd.exe to execute bun.cmd since npm-installed bun is a batch file // Use windowsHide to prevent a visible console window from spawning on Windows -const child = spawn(bunPath, args, { +const spawnOptions = { stdio: [stdinData ? 'pipe' : 'ignore', 'inherit', 'inherit'], windowsHide: true, - shell: IS_WINDOWS, env: process.env -}); +}; + +let spawnCmd = bunPath; +let spawnArgs = args; + +if (IS_WINDOWS) { + // On Windows, bun.cmd must be executed via cmd /c + spawnCmd = 'cmd'; + spawnArgs = ['/c', bunPath, ...args]; +} + +const child = spawn(spawnCmd, spawnArgs, spawnOptions); // Write buffered stdin to child's pipe, then close it so the child sees EOF if (stdinData && child.stdin) {