Skip to content

fix(pty): prefer pwsh.exe over powershell.exe in BuildCmdLine wrapper#68

Open
AThraen wants to merge 1 commit into
mainfrom
fix/prefer-pwsh-wrapper
Open

fix(pty): prefer pwsh.exe over powershell.exe in BuildCmdLine wrapper#68
AThraen wants to merge 1 commit into
mainfrom
fix/prefer-pwsh-wrapper

Conversation

@AThraen
Copy link
Copy Markdown
Contributor

@AThraen AThraen commented May 21, 2026

Summary

  • PseudoTerminal.BuildCmdLine now wraps non-passthrough commands in pwsh.exe -NoExit -Command … when pwsh is on PATH, falling back to powershell.exe otherwise.
  • Adds nu (nushell) and fish to the passthrough shell list so they aren't double-wrapped when used as a session command.
  • Adds PseudoTerminalBuildCmdLineTests covering the passthrough list, the pwsh wrap, and the powershell.exe fallback.

Why

Users who define PowerShell functions in their PowerShell 7 profile (~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1) couldn't invoke them as bare session commands. Example: a function claudelocal lets you call Claude Code with a local AI account, but typing claudelocal in the New Session dialog failed because the wrapper used powershell.exe (Windows PowerShell 5.1), which loads a different profile (~/Documents/WindowsPowerShell/…) where the function isn't defined.

Preferring pwsh.exe matches where modern users actually put their profile customisations, while keeping a safe fallback for machines where pwsh isn't installed.

Test plan

  • dotnet test tests/CodeShellManager.Tests/ — 224 passed, 0 failed (18 new tests in PseudoTerminalBuildCmdLineTests)
  • dotnet build src/CodeShellManager/CodeShellManager.csproj — 0 errors
  • Manual: create a session with a bare PowerShell function name (e.g. claudelocal) as the command on a machine with pwsh installed — function resolves and runs
  • Manual: confirm existing flows still work (claude, cmd, ssh, pwsh-as-command)

🤖 Generated with Claude Code

Bare commands that aren't in the passthrough list get wrapped in
'<shell>.exe -NoExit -Command <cmd>' so the shell can set up the
Win32 console environment before launching the target process
(Electron/Node SEA apps like claude.exe crash with
STATUS_DLL_INIT_FAILED when launched directly inside a ConPTY).

Before this change the wrapper was always powershell.exe (Windows
PowerShell 5.1). Modern users define profile functions in the
PowerShell 7 (pwsh) profile at ~/Documents/PowerShell/, which 5.1
never sees — so commands like 'claudelocal' / 'claudework' failed
with "command not recognized" when entered as bare session commands.

The wrapper is now resolved once per process: pwsh.exe when present
on PATH, falling back to powershell.exe (always present on Windows).
Also adds 'nu' (nushell) and 'fish' to the passthrough list so those
shells aren't double-wrapped when used directly.

BuildCmdLine is bumped from private to internal and gains an
overload that accepts the wrapper shell name explicitly, so the
behaviour can be unit-tested without depending on whether pwsh.exe
happens to be installed on the test runner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant