Summary
Proposal: invoke vp-managed package managers (and npm/npx) by spawning the real JS entry with the managed Node directly (node <pm>.cjs <args>), instead of resolving to the Windows .cmd shim and then routing through PowerShell. This removes the root cause behind both the Ctrl+C terminal corruption and the CI stdin hang in every stdio configuration, and drops the PowerShell dependency.
Background
Why the current approach has structural limits
- It couples two unrelated concerns through a heuristic: terminal Ctrl+C corruption is a console property, but it is gated on stdin TTY-ness.
- It keeps a hard dependency on PowerShell being present plus
-ExecutionPolicy Bypass for the interactive path.
- It still leaves the
cmd.exe corruption in place for non-TTY-but-console cases (e.g. vp install < input.txt from a real terminal).
Proposal: bypass the shims for vp-managed binaries
vp already has everything needed to skip the shim layer for the binaries it manages:
- The installer writes the real JS entry next to the shims (
crates/vite_install/src/package_manager.rs, crates/vite_install/src/shim.rs): <pm>.cjs, <pm>.cmd, and <pm>.ps1 all just run node <pm>.cjs, e.g. $VP_HOME/package_manager/pnpm/<ver>/pnpm/bin/pnpm.cjs.
- vp ships its own Node:
$VP_HOME/js_runtime/node/<ver>/node(.exe).
So for the managed scope, spawn:
$VP_HOME/js_runtime/node/<ver>/node(.exe) .../pnpm/bin/pnpm.cjs <args>
This is byte-for-byte what the shims do internally, minus the wrapper.
Benefits:
- No
cmd.exe, so no Ctrl+C "Terminate batch job (Y/N)?" corruption, in every stdio config.
- No
.ps1, so no $MyInvocation.ExpectingInput stdin hang, in every stdio config.
- No PowerShell dependency or execution-policy concerns.
- Removes the TTY heuristic for this path; identical behavior in CI and interactive. vp keeps full control of stdio inheritance and signal handling (
execute_with_terminal_guard, fix_stdio_streams).
Scope and fallback
Apply this only to what vp owns (the $VP_HOME managed shims: package managers plus npm/npx under js_runtime), where the .cjs target is known. For arbitrary third-party node_modules/.bin/*.cmd, parsing the shim to find the node target is more involved, so the existing TTY-gated PowerShell rewrite in ps1_shim.rs can stay as the fallback there. Net result: the highest-value path (the package-manager invocation that caused the CI hang) becomes bulletproof, and the general case keeps today's behavior.
Implementation sketch
- In
crates/vite_command/src/lib.rs resolve_program (or a layer above it): when the resolved bin is a vp-managed PM / npm / npx shim, resolve to the sibling <name>.cjs plus the managed node, and return (node_path, [cjs_path, ...args]) instead of the .cmd / PowerShell rewrite.
- Keep
ps1_shim::rewrite_cmd_to_powershell for the node_modules/.bin/* fallback.
Notes
Summary
Proposal: invoke vp-managed package managers (and
npm/npx) by spawning the real JS entry with the managed Node directly (node <pm>.cjs <args>), instead of resolving to the Windows.cmdshim and then routing through PowerShell. This removes the root cause behind both the Ctrl+C terminal corruption and the CI stdin hang in every stdio configuration, and drops the PowerShell dependency.Background
.cmdshims through PowerShell to fix Ctrl+C terminal corruption #1414 routed.cmdshims through PowerShell to fix thecmd.exe"Terminate batch job (Y/N)?" Ctrl+C corruption..ps1wrappers introspect stdin ($MyInvocation.ExpectingInput) and block forever on an open, empty stdin pipe (Ctrl+C breaks the terminal on Windows for commands delegated through package managers #1489, Pressing Ctrl+C after vp run dev, pnpm run dev, or npm run dev leaves the terminal in a broken state on Windows #1176, downstream Bundling on Windows runners with Vite+ will be stuck for several hours until canceled by GitHub tauri-apps/tauri-action#1308 and Using on Windows runners will be stuck for several hours until canceled by GitHub setup-vp#90). Jobs hung ~6h until GitHub cancelled them.crates/vite_command/src/ps1_shim.rs: skip the PowerShell rewrite whenstdin.is_terminal()is false. It is sound and well-tested.Why the current approach has structural limits
-ExecutionPolicy Bypassfor the interactive path.cmd.execorruption in place for non-TTY-but-console cases (e.g.vp install < input.txtfrom a real terminal).Proposal: bypass the shims for vp-managed binaries
vp already has everything needed to skip the shim layer for the binaries it manages:
crates/vite_install/src/package_manager.rs,crates/vite_install/src/shim.rs):<pm>.cjs,<pm>.cmd, and<pm>.ps1all just runnode <pm>.cjs, e.g.$VP_HOME/package_manager/pnpm/<ver>/pnpm/bin/pnpm.cjs.$VP_HOME/js_runtime/node/<ver>/node(.exe).So for the managed scope, spawn:
This is byte-for-byte what the shims do internally, minus the wrapper.
Benefits:
cmd.exe, so no Ctrl+C "Terminate batch job (Y/N)?" corruption, in every stdio config..ps1, so no$MyInvocation.ExpectingInputstdin hang, in every stdio config.execute_with_terminal_guard,fix_stdio_streams).Scope and fallback
Apply this only to what vp owns (the
$VP_HOMEmanaged shims: package managers plusnpm/npxunderjs_runtime), where the.cjstarget is known. For arbitrary third-partynode_modules/.bin/*.cmd, parsing the shim to find the node target is more involved, so the existing TTY-gated PowerShell rewrite inps1_shim.rscan stay as the fallback there. Net result: the highest-value path (the package-manager invocation that caused the CI hang) becomes bulletproof, and the general case keeps today's behavior.Implementation sketch
crates/vite_command/src/lib.rsresolve_program(or a layer above it): when the resolved bin is a vp-managed PM / npm / npx shim, resolve to the sibling<name>.cjsplus the managednode, and return(node_path, [cjs_path, ...args])instead of the.cmd/ PowerShell rewrite.ps1_shim::rewrite_cmd_to_powershellfor thenode_modules/.bin/*fallback.Notes
.ps1wrappers see EOF) for users who pin vp < 0.1.21: fix: close stdin on vp invocations to avoid Windows runner hang setup-vp#91.