fix(shell): resolve pwsh.exe/.cmd/.bat on PATH for Windows shell discovery#104086
fix(shell): resolve pwsh.exe/.cmd/.bat on PATH for Windows shell discovery#104086labsclaw wants to merge 7 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 19, 2026, 3:00 PM ET / 19:00 UTC. Summary PR surface: Source +148, Tests +289. Total +437 across 2 files. Reproducibility: yes. in source terms: a Windows PATH entry containing only Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Rebase onto current Do we have a high-confidence way to reproduce the issue? Yes in source terms: a Windows PATH entry containing only Is this the best way to solve the issue? Unclear for the current branch: probing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 5a6a19c13393. Label changesLabel justifications:
Evidence reviewedPR surface: Source +148, Tests +289. Total +437 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (6 earlier review cycles)
|
On Windows, PowerShell 7 installed via the Microsoft Store / App Execution
Alias ships as pwsh.exe (most portable installs too). resolvePowerShellPath()
step 3 called resolveShellFromPath("pwsh"), which only probed a bare "pwsh"
with no extension. fs.accessSync therefore missed the real binary and
discovery fell through to Windows PowerShell 5.1.
resolveShellFromPath now also probes ".exe" on win32, matching the existing
pattern in resolveWindowsBashPath ("bash.exe" / "bash"). Only ".exe" is
added: ".cmd"/".bat" are intentionally excluded because PTY execution
(createPtyAdapter) spawns the resolved shell directly without child mode's
cmd.exe wrapping, so a pwsh.cmd selected here could fail in PTY mode. ".exe"
is directly executable in both child and PTY modes. Non-Windows behavior is
unchanged.
fdbbbfc to
35666f6
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
The Evidence from the failing job logs ( The repo root This PR only touches
So the patch itself is sound; the QA Smoke red is a pre-existing CI config gap on |
|
Hi @steipete! 👋 This PR fixes PowerShell 7 discovery on Windows by probing pwsh.exe on PATH. The problem: Windows Store / portable installs ship as pwsh.exe, but The fix: On win32, also probe .exe extension (matching the existing pattern in CI status: QA Smoke CI failure is upstream (missing packageManager field in repo root package.json), not related to this patch. Would appreciate a review when you have a chance! 🙏 |
Rebase shell-utils.ts on current main: - Add .exe extension probing in resolveShellFromPath (win32 only) - Add export keyword to resolveShellFromPath - Preserve all existing main functionality Addresses merge conflicts blocking PR openclaw#104086
Rebase shell-utils.test.ts on current main: - Add imports for resolvePowerShellPath, resolveShellFromPath, resolveShellFromWhich, resolveWindowsBashPath - Add createTempCommandDir helper - Add resolveWindowsBashPath, resolveShellFromPath, resolveShellFromWhich test suites from PR - Preserve all existing main test structure Addresses merge conflicts blocking PR openclaw#104086
Rebase shell-utils.test.ts on current main: - Add imports for resolvePowerShellPath, resolveShellFromPath, resolveShellFromWhich, resolveWindowsBashPath - Add createTempCommandDir helper for Windows-only tests - Add resolveWindowsBashPath, resolveShellFromPath, resolveShellFromWhich describe blocks - Preserve all existing main test structure Addresses merge conflicts blocking PR openclaw#104086
Rebase PR changes onto current main. - Export resolveShellFromPath for external use - Add .exe probing on Windows for pwsh discovery - Add tests for resolveWindowsBashPath, resolveShellFromPath, resolveShellFromWhich, resolvePowerShellPath
|
Closing in favor of #111500 which is rebased on current main with resolved conflicts. Same changes, clean merge state. |
|
Closing in favor of #111500 which is rebased on current main with resolved conflicts. Same changes, clean merge state. |
What Problem This Solves
On Windows, PowerShell 7 delivered via the Microsoft Store / App Execution Alias (and most portable installs) ships as
pwsh.exe.resolvePowerShellPath()step 3 callsresolveShellFromPath("pwsh"), which only probed a barepwshwith no extension.fs.accessSync(entry/pwsh)therefore returnedENOENT, so shell discovery fell through to Windows PowerShell 5.1 instead of using pwsh 7. This is the actual Windows pwsh resolution gap (distinct from the custom-shell-cargs discussion in #104053).Evidence
Local reproduction on this Windows host (PowerShell 7.6.3 present only as the Store
pwsh.exealias, noC:\Program Files\PowerShell\7):fs.accessSync("...\pwsh")->ENOENT(bare name missed by discovery)fs.accessSync("...\pwsh.exe")->OK(extension probe hits it)resolveShellFromPath("pwsh")returns thepwsh.exepath on PATH, andresolvePowerShellPath()prefers pwsh 7 over the 5.1 fallback.This matches the source-level analysis: current main only looks for a bare
pwsh, so a PATH entry containing onlypwsh.exeis skipped before the Windows PowerShell 5.1 fallback (noted by the ClawSweeper review). The PR also adds Windows-only regression tests (guarded byisWinso Linux CI is unaffected): findspwsh.exewhen a barepwshis absent, prefers a barepwshmatch overpwsh.exewhen both exist, andresolvePowerShellPath()resolvespwsh.exeon PATH instead of 5.1.(Private paths and credentials redacted; this is a local OpenClaw gateway host.)
Root cause
resolveShellFromPathjoined the entry with the bare name and never tried executable extensions on Windows:Fix
On
win32, also probe.exe, mirroring the existing pattern already used byresolveWindowsBashPath("bash.exe"/"bash").Scope note (addressed ClawSweeper review): only
.exeis added..cmd/.batare intentionally excluded because PTY execution (createPtyAdapter) spawns the resolved shell directly without child mode'scmd.exewrapping, so apwsh.cmdselected here could be chosen and then fail in PTY mode..exeis directly executable in both child and PTY modes. Non-Windows behavior is unchanged.This makes a Store/portable
pwsh.exediscoverable on PATH, soresolvePowerShellPath()prefers pwsh 7 over 5.1.Test plan
Windows-only tests in
shell-utils.test.ts(guarded byisWinso Linux CI is unaffected):pwsh.exewhen a barepwshis absentpwshmatch overpwsh.exewhen both existresolvePowerShellPath()resolvespwsh.exeon PATH instead of 5.1 when standard install dirs are emptyExisting tests unchanged; non-Windows
resolveShellFromPathbehavior preserved.