Skip to content

fix(shell): use PowerShell CLI args for custom pwsh/powershell shell paths on Windows#104053

Closed
lunalimadev-sketch wants to merge 1 commit into
openclaw:mainfrom
lunalimadev-sketch:fix/windows-custom-powershell-shell-args
Closed

fix(shell): use PowerShell CLI args for custom pwsh/powershell shell paths on Windows#104053
lunalimadev-sketch wants to merge 1 commit into
openclaw:mainfrom
lunalimadev-sketch:fix/windows-custom-powershell-shell-args

Conversation

@lunalimadev-sketch

Copy link
Copy Markdown

Summary

On Windows, when a user sets a custom shell path (shellPath / settings.shellPath) that points to a PowerShell binary (e.g. pwsh.exe from the Microsoft Store App Execution Alias, or a custom pwsh install), OpenClaw invoked it with POSIX-style arguments (-c) instead of PowerShell CLI arguments.

PowerShell does not accept -c (it uses -Command), so the exec command was either rejected or silently ignored — making the shellPath setting effectively broken for PowerShell on Windows.

Root cause

In src/agents/shell-utils.ts, getShellConfig(customShellPath) and getBashShellConfig(customShellPath) both delegate to getPosixShellArgs(), whose default branch returns ["-c"]. A custom pwsh.exe/powershell.exe path fell into that default, producing pwsh.exe -c "...", which is invalid.

Note that the non-custom Windows path already uses the correct args (-NoProfile -NonInteractive -Command) via resolvePowerShellPath(). The bug only affected the explicit customShellPath case.

Fix

  • Add isPowerShellShell(shellPath) helper that detects pwsh, pwsh.exe, powershell, powershell.exe.
  • When a custom shell path is a PowerShell binary, use WINDOWS_POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-Command"] instead of the POSIX -c args.
  • Applied to both getShellConfig and getBashShellConfig for consistency.

Test plan

  • Added tests in shell-utils.test.ts asserting custom pwsh.exe, powershell.exe, and pwsh (no extension) paths yield ["-NoProfile", "-NonInteractive", "-Command"].
  • Existing tests unchanged; the existing POSIX custom-shell test (zsh) still passes.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS 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. labels Jul 11, 2026
@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 10, 2026, 10:01 PM ET / July 11, 2026, 02:01 UTC.

Summary
The PR detects custom pwsh and powershell executable names and supplies -NoProfile -NonInteractive -Command in both shell configuration helpers, with unit assertions for three executable names.

PR surface: Source +15, Tests +30. Total +45 across 2 files.

Reproducibility: no. Direct PowerShell execution succeeds with current main's -c switch, and the PR supplies no Windows PowerShell 5.1 or OpenClaw before-and-after execution showing a failure.

Review metrics: 1 noteworthy metric.

  • Custom-shell semantics: 1 existing setting behavior changed. The patch changes how an existing shellPath value launches PowerShell by suppressing profiles and interaction.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
Result: blocked until real behavior proof from a real setup is added.

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

Rank-up moves:

  • [P1] Add redacted Windows before-and-after execution proof for the exact custom pwsh.exe or powershell.exe path.
  • Revise the root cause and implementation to address the reproduced failure rather than -c rejection.

Proof guidance:

  • [P1] Needs real behavior proof before merge: Only unit assertions are provided; before merge, add a redacted Windows terminal transcript, live output, or recording showing the exact custom shellPath failure on current main and the improved result after the patch. Updating the PR body should trigger a fresh review; otherwise a maintainer can comment @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Merging would disable profiles and interactive prompts for users who explicitly selected a PowerShell executable, potentially changing established custom-shell behavior without an upgrade justification.
  • [P1] The new tests assert the selected argument arrays but do not demonstrate an improved Windows execution result.

Maintainer options:

  1. Reproduce and narrow the fix (recommended)
    Require real Windows before-and-after evidence, then revise the patch to address only the demonstrated failure without unconditionally disabling profiles or interaction.
  2. Accept changed custom-shell semantics
    Merge only if maintainers intentionally want every explicitly selected PowerShell shell to ignore profiles and run noninteractively despite the unsupported root-cause explanation.
  3. Close if no failure exists
    Close the PR if the only claimed defect is PowerShell rejecting -c, because that behavior is not supported by the contract or live execution.

Next step before merge

  • [P1] Contributor-supplied Windows proof and a corrected root-cause boundary are required; automation cannot safely repair an unproven compatibility change.

Security
Cleared: The source-and-test-only diff adds no dependency, secret, permission, workflow, downloaded-artifact, packaging, or supply-chain surface.

Review findings

  • [P1] Prove a real failure before changing PowerShell semantics — src/agents/shell-utils.ts:112-113
Review details

Best possible solution:

Reproduce the exact custom-shell failure on Windows, then make the narrowest shared argument change that fixes the observed behavior while preserving intentional custom-shell profile and interaction semantics.

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

No. Direct PowerShell execution succeeds with current main's -c switch, and the PR supplies no Windows PowerShell 5.1 or OpenClaw before-and-after execution showing a failure.

Is this the best way to solve the issue?

No. The patch special-cases executable names and changes profile and interaction behavior to solve a switch rejection that PowerShell's contract and live execution disprove; the actual failing condition must be identified first.

Full review comments:

  • [P1] Prove a real failure before changing PowerShell semantics — src/agents/shell-utils.ts:112-113
    PowerShell already accepts -c as -Command: its runtime help lists -Command | -c, and a live pwsh -c invocation succeeds. This change therefore does not fix the rejection described in the PR; it instead adds -NoProfile and -NonInteractive for explicitly selected shells. Please reproduce the actual Windows failure and target that condition, or remove this special case if -c is the only problem being claimed.
    Confidence: 0.96

Overall correctness: patch is incorrect
Overall confidence: 0.96

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: The PR targets a limited Windows custom-shell path, but the claimed breakage is not reproducible and the semantic change requires correction before merge.
  • merge-risk: 🚨 compatibility: Existing users who deliberately select PowerShell through shellPath could lose profile initialization or interactive behavior after upgrading.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Only unit assertions are provided; before merge, add a redacted Windows terminal transcript, live output, or recording showing the exact custom shellPath failure on current main and the improved result after the patch. Updating the PR body should trigger a fresh review; otherwise a maintainer can comment @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +15, Tests +30. Total +45 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 17 2 +15
Tests 1 30 0 +30
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 47 2 +45

What I checked:

  • Current-main behavior: Custom shell paths currently use getPosixShellArgs, which returns -c, while default Windows execution already supplies the longer PowerShell startup arguments. (src/agents/shell-utils.ts:96, f50293c9e73b)
  • Live PowerShell execution: The installed PowerShell runtime successfully executed identical commands through both -c and -Command, so the claimed pwsh rejection was not reproduced. (src/agents/shell-utils.ts:84, d31f82115ca7)
  • Upstream PowerShell contract: PowerShell runtime help explicitly documents -Command | -c, and the upstream command-line parser accepts unambiguous abbreviated switch names. (src/agents/shell-utils.ts:84, d31f82115ca7)
  • Caller boundary: The configured path is passed to the agent's bash tool, which appends the command after the returned argument array; the patch therefore changes startup semantics rather than command construction. (src/agents/sessions/tools/bash.ts:53, f50293c9e73b)
  • Validation state: Build, lint, type, dependency, security, and test checks passed, but the dedicated Real behavior proof check failed because the PR contains no live Windows evidence. (d31f82115ca7)
  • Windows shell provenance: Commit 6a4b5fa introduced explicit Windows PowerShell resolution and arguments; commit fa525bf later added PowerShell 7 preference and fallback tests. (src/agents/shell-utils.ts:21, 6a4b5fa4b5e1)

Likely related people:

  • steipete: The history shows this contributor introduced Windows shell hardening and later carried the PowerShell 7 resolution and shell-helper consolidation work. (role: introduced and recently maintained Windows shell behavior; confidence: high; commits: 6a4b5fa4b5e1, fa525bf21280, c389839d3018; files: src/agents/shell-utils.ts, src/agents/shell-utils.test.ts)
  • myfunc: Commit b33bd6a switched Windows command execution from cmd.exe to PowerShell to preserve piped utility output. (role: introduced PowerShell-based Windows execution; confidence: high; commits: b33bd6aaeb90; files: src/agents/shell-utils.ts)
  • NewdlDewdl: The work carried forward from pull request 40200 established shell-specific startup-suppression flags, the closest historical rationale for adding -NoProfile. (role: introduced shell startup-suppression behavior; confidence: medium; commits: ea9f17256a4b; 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.

@labsclaw

Copy link
Copy Markdown

I tested the premise on a real Windows setup (PowerShell 7.6.3 and 5.1):

pwsh -NoProfile -c "Write-Output 'ok'"       # -> ok
powershell -NoProfile -c "Write-Output 'ok'" # -> ok

So PowerShell does accept -c (it is an alias for -Command), and the custom-shell path already works on current main. This matches the ClawSweeper finding. The PR's root-cause premise ("PowerShell rejects -c") is not reproducible, so the added -NoProfile -NonInteractive change is solving a non-bug while silently changing semantics for users who deliberately selected a PowerShell shellPath (they may rely on profiles / interactive behavior). I'd recommend not merging the args change as written.

The real Windows pwsh gap (which this PR does not address) is in shell discovery, not custom-shell args. resolvePowerShellPath() step 3 calls resolveShellFromPath("pwsh"), which probes path.join(entry, "pwsh") with no .exe extension. On Windows the Store/MSIX install only provides pwsh.exe, so fs.accessSync(entry/pwsh) -> ENOENT and discovery falls through to System32 WindowsPowerShell 5.1. (I hit exactly this locally and worked around it by pointing %ProgramW6432% at a directory containing a real PowerShell\7\pwsh.exe.)

If the goal is robust Windows pwsh resolution, the minimal correct fix is in resolveShellFromPath: on win32, also probe name + ".exe" (and .cmd/.bat):

function resolveShellFromPath(name, env = process.env) {
  const envPath = env.PATH ?? "";
  if (!envPath) return;
  const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
  for (const entry of envPath.split(path.delimiter).filter(Boolean)) {
    for (const ext of exts) {
      const candidate = path.join(entry, name + ext);
      try { fs.accessSync(candidate, fs.constants.X_OK); return candidate; } catch {}
    }
  }
}

That makes pwsh.exe (Store alias or any install) discoverable on PATH without changing custom-shell semantics. Happy to split this into a separate PR if useful.

@labsclaw

Copy link
Copy Markdown

Following up with the actual Windows pwsh fix, split into its own PR: #104086.

The -c args concern in this PR is a non-bug (PowerShell accepts -c as -Command on both 7.x and 5.1 — verified live), and this PR's added -NoProfile -NonInteractive changes semantics for users who deliberately set a PowerShell shellPath. But there is a real Windows pwsh discovery gap, and it's in the same file:

resolvePowerShellPath() step 3 calls resolveShellFromPath("pwsh"), which only probes a bare pwsh with no extension. On Windows, PowerShell 7 ships as pwsh.exe (Store / App Execution Alias) or pwsh.cmd, so fs.accessSync(entry/pwsh) -> ENOENT and discovery falls through to Windows PowerShell 5.1. That's the real reason the default exec shell ends up on 5.1 on a fresh Windows install with pwsh 7 present.

#104086 fixes exactly that: resolveShellFromPath now also probes .exe / .cmd / .bat on win32, matching the existing pattern in resolveWindowsBashPath ("bash.exe" / "bash"), with behavior unchanged on other platforms. It includes Windows-only tests (guarded by isWin).

Happy to coordinate if maintainers would rather fold the discovery fix into this PR instead of a separate one.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jul 26, 2026
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. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS stale Marked as stale due to inactivity status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants