Skip to content

fix(shell): resolve pwsh.exe/.cmd/.bat on PATH for Windows shell discovery#104086

Closed
labsclaw wants to merge 7 commits into
openclaw:mainfrom
labsclaw:fix/windows-pwsh-path-discovery
Closed

fix(shell): resolve pwsh.exe/.cmd/.bat on PATH for Windows shell discovery#104086
labsclaw wants to merge 7 commits into
openclaw:mainfrom
labsclaw:fix/windows-pwsh-path-discovery

Conversation

@labsclaw

@labsclaw labsclaw commented Jul 11, 2026

Copy link
Copy Markdown

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 calls resolveShellFromPath("pwsh"), which only probed a bare pwsh with no extension. fs.accessSync(entry/pwsh) therefore returned ENOENT, 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 -c args discussion in #104053).

Evidence

Local reproduction on this Windows host (PowerShell 7.6.3 present only as the Store pwsh.exe alias, no C:\Program Files\PowerShell\7):

  • fs.accessSync("...\pwsh") -> ENOENT (bare name missed by discovery)
  • fs.accessSync("...\pwsh.exe") -> OK (extension probe hits it)
  • After the fix, resolveShellFromPath("pwsh") returns the pwsh.exe path on PATH, and resolvePowerShellPath() 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 only pwsh.exe is skipped before the Windows PowerShell 5.1 fallback (noted by the ClawSweeper review). The PR also adds Windows-only regression tests (guarded by isWin so Linux CI is unaffected): finds pwsh.exe when a bare pwsh is absent, prefers a bare pwsh match over pwsh.exe when both exist, and resolvePowerShellPath() resolves pwsh.exe on PATH instead of 5.1.

(Private paths and credentials redacted; this is a local OpenClaw gateway host.)

Root cause

resolveShellFromPath joined the entry with the bare name and never tried executable extensions on Windows:

const candidate = path.join(entry, name); // only "pwsh", never "pwsh.exe"

Fix

On win32, also probe .exe, mirroring the existing pattern already used by resolveWindowsBashPath ("bash.exe" / "bash").

const executableExtensions = process.platform === "win32" ? [".exe"] : [];
const candidates = [base, ...executableExtensions.map((ext) => base + ext)];

Scope note (addressed ClawSweeper review): 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 be chosen and then fail in PTY mode. .exe is directly executable in both child and PTY modes. Non-Windows behavior is unchanged.

This makes a Store/portable pwsh.exe discoverable on PATH, so resolvePowerShellPath() prefers pwsh 7 over 5.1.

Test plan

Windows-only tests in shell-utils.test.ts (guarded by isWin so Linux CI is unaffected):

  • finds pwsh.exe when a bare pwsh is absent
  • prefers a bare pwsh match over pwsh.exe when both exist
  • resolvePowerShellPath() resolves pwsh.exe on PATH instead of 5.1 when standard install dirs are empty

Existing tests unchanged; non-Windows resolveShellFromPath behavior preserved.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jul 11, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 11, 2026
@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 19, 2026, 3:00 PM ET / 19:00 UTC.

Summary
The branch adds Windows .exe probing for PATH shell discovery while also carrying broad shell utility and test reconciliation changes.

PR surface: Source +148, Tests +289. Total +437 across 2 files.

Reproducibility: yes. in source terms: a Windows PATH entry containing only pwsh.exe is missed when lookup probes only the extensionless name. The supplied terminal transcript supports the scenario, but it needs rerunning from the final rebased head.

Review metrics: none identified.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🐚 platinum hermit
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Rebase onto current main and reduce the diff to the executable-extension lookup plus focused tests.
  • Attach a redacted Windows terminal result from the exact rebased head showing pwsh.exe on PATH is selected over the 5.1 fallback.

Risk before merge

  • [P1] Merging the dirty branch without a clean rebase could reintroduce unrelated shell utility, command-transport, or sanitizer changes beyond the intended Windows executable probe.
  • [P2] Selecting pwsh.exe changes affected Windows hosts from the PowerShell 5.1 fallback to PowerShell 7; final proof should come from the rebased head and cover both child-process and PTY shell paths.

Maintainer options:

  1. Rebase to a narrow shell-discovery fix (recommended)
    Resolve the dirty merge state, drop unrelated reconciliation churn, and re-prove the .exe lookup against the final rebased head before merge.
  2. Pause for a clean replacement
    Keep this branch unmerged until a replacement contains a reviewable nonzero diff and passes the same Windows proof on its exact head.

Next step before merge

  • [P2] The contributor needs to produce a clean current-main rebase and final-head Windows proof; the linked replacement is not yet safe enough to replace this PR automatically.

Security
Cleared: The proposed shell-lookup change and accompanying test edits add no dependency, workflow, permission, secret, artifact-download, or supply-chain surface.

Review details

Best possible solution:

Rebase onto current main, retain only the Windows .exe candidate and focused regression coverage, then provide a redacted Windows run from that exact head showing PATH-only pwsh.exe wins over the PowerShell 5.1 fallback.

Do we have a high-confidence way to reproduce the issue?

Yes in source terms: a Windows PATH entry containing only pwsh.exe is missed when lookup probes only the extensionless name. The supplied terminal transcript supports the scenario, but it needs rerunning from the final rebased head.

Is this the best way to solve the issue?

Unclear for the current branch: probing .exe is the narrow maintainable fix, but the dirty head includes unrelated changes that should not be accepted as part of this repair.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5a6a19c13393.

Label changes

Label justifications:

  • P2: This is a platform-specific shell-discovery bug that can make Windows hosts select an older PowerShell fallback, but it is not evidenced as a broad outage.
  • merge-risk: 🚨 compatibility: The change intentionally alters shell selection for existing Windows PATH configurations, and the current dirty branch contains additional shell-behavior churn.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🐚 platinum hermit and patch quality is 🦐 gold shrimp.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes a redacted Windows before/after terminal transcript for a PATH-only pwsh.exe installation; refresh that same proof after the dirty branch is rebased, redacting local paths and private host details.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes a redacted Windows before/after terminal transcript for a PATH-only pwsh.exe installation; refresh that same proof after the dirty branch is rebased, redacting local paths and private host details.
Evidence reviewed

PR surface:

Source +148, Tests +289. Total +437 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 182 34 +148
Tests 1 343 54 +289
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 525 88 +437

What I checked:

  • Reported fix boundary: The PR body identifies the intended behavioral change as resolving pwsh.exe from PATH so PowerShell 7 is selected before the Windows PowerShell 5.1 fallback; it deliberately excludes .cmd and .bat because PTY execution launches the resolved binary directly. (src/agents/shell-utils.ts:1, 768f9b928440)
  • Current branch scope drift: The provided head diff changes shell command transport, ANSI streaming sanitization, export visibility, and extensive test coverage in addition to the Windows PATH probe, so it is not a narrow application of the stated fix. (src/agents/shell-utils.ts:1, 768f9b928440)
  • Merge state: GitHub reports this PR as mergeable: false with mergeableState: dirty; the current main SHA is 5a6a19c, so the final merged behavior still needs a current-base rebase and review refresh. (5a6a19c13393)
  • Replacement is not yet viable: The linked replacement at fix(shell): resolve pwsh.exe/.cmd/.bat on PATH for Windows shell discovery #111500 claims supersession, but its provided state is open with zero changed files and unknown mergeability. That does not meet the safe-canonical replacement bar for closing this PR. (24a4cb2b14f9)
  • Prior review continuity: The latest prior ClawSweeper cycle found no remaining line-level defect after the author restored the test-module imports; its stated remaining work was a rebase and focused Windows validation. The new head still has a dirty merge state, so that remaining condition is not resolved. (src/agents/shell-utils.test.ts:1, 623ce867cd9f)

Likely related people:

  • steipete: The contributor explicitly requested review from this account and the timeline shows a corresponding mention/subscription; no stronger current-main ownership trail was available in the supplied review evidence. (role: review-requested area contact; confidence: low; files: src/agents/shell-utils.ts, src/agents/shell-utils.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (6 earlier review cycles)
  • reviewed 2026-07-11T02:54:31.264Z sha fdbbbfc :: needs real behavior proof before merge. :: [P1] Keep batch shells out of the PTY path
  • reviewed 2026-07-11T03:47:18.299Z sha ae580d9 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-11T03:53:56.418Z sha ae580d9 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-19T17:35:45.299Z sha 8d674b3 :: needs changes before merge. :: [P1] Restore the utility exports imported by the changed test module
  • reviewed 2026-07-19T17:42:51.277Z sha 623ce86 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-19T18:25:23.403Z sha 623ce86 :: needs maintainer review before merge. :: none

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.
@labsclaw
labsclaw force-pushed the fix/windows-pwsh-path-discovery branch from fdbbbfc to 35666f6 Compare July 11, 2026 03:18
@labsclaw

Copy link
Copy Markdown
Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jul 11, 2026
@clawsweeper clawsweeper Bot removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 11, 2026
@labsclaw

Copy link
Copy Markdown
Author

The QA Smoke CI failure on this PR is an upstream CI infrastructure issue, not related to this patch.

Evidence from the failing job logs (actions/jobs/86506687514/logs):

::error::package manager file not found: $PACKAGE_MANAGER_FILE
::error::Expected packageManager to pin pnpm, got '<empty>'
::error::Invalid frozen-lockfile input: '$FROZEN_LOCKFILE' (expected true or false)

The repo root package.json on main has no packageManager field (verified via the contents API — it lists pnpm-workspace.yaml and pnpm scripts, but no packageManager pin). The QA Smoke workflow expects that pin and fails before any test runs. This breaks for any PR against main, not just this one.

This PR only touches src/agents/shell-utils.ts (the .exe extension probe) and its test. All code-relevant checks are green:

  • Real behavior proof — PASS (body now has authored What Problem This Solves + Evidence sections)
  • check-lint, check-prod-types, check-guards, check-dependencies — PASS
  • checks-fast-* (contracts, bun-launcher, etc.) — PASS
  • Security High boundaries, preflight, runner-admission, pnpm-store-warmup — PASS

So the patch itself is sound; the QA Smoke red is a pre-existing CI config gap on main. Flagging for maintainer awareness — happy to help fix the workflow if you'd prefer that over marking it a known-infra fail.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 11, 2026
@labsclaw

Copy link
Copy Markdown
Author

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
esolveShellFromPath only probed bare pwsh without extension, causing discovery to fall through to PowerShell 5.1.

The fix: On win32, also probe .exe extension (matching the existing pattern in
esolveWindowsBashPath). Only .exe is added — .cmd/.bat are intentionally excluded because PTY execution spawns the resolved shell directly without cmd.exe wrapping.

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
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. and removed rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 19, 2026
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
@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 19, 2026
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
@labsclaw

Copy link
Copy Markdown
Author

Closing in favor of #111500 which is rebased on current main with resolved conflicts. Same changes, clean merge state.

@labsclaw labsclaw closed this Jul 19, 2026
@labsclaw

Copy link
Copy Markdown
Author

Closing in favor of #111500 which is rebased on current main with resolved conflicts. Same changes, clean merge state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant