shell: support custom shells on Windows#65271
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bf12734f2c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| !looksLikeExplicitPath(trimmed) && | ||
| runtimeShell !== "cmd" && | ||
| runtimeShell !== "powershell" && | ||
| runtimeShell !== "pwsh" && | ||
| !isUnixLikeShellName(runtimeShell) |
There was a problem hiding this comment.
Validate configured Windows shell path exists before using it
A stale OPENCLAW_SHELL/SHELL path like C:\\missing\\bash.exe is currently accepted as soon as it "looks like" a path, so getShellConfig() returns a non-existent executable and downstream spawn fails with ENOENT instead of falling back to the next candidate or PowerShell. This is a regression on Windows (previous behavior always had a working PowerShell fallback) and directly breaks command execution for users with outdated shell path env vars.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR adds Windows custom shell support to Confidence Score: 5/5Safe to merge; all remaining findings are P2 style suggestions that do not affect correctness in realistic configurations. The implementation is well-structured with correct arg-style selection, proper POSIX path rejection, quote-stripping, and ordered OPENCLAW_SHELL → SHELL fallback. Tests cover all new branches. The one flagged issue (isUnixLikeShellName substring match) only matters if a user deliberately sets SHELL to a non-shell binary on Windows, which is very unlikely in practice. No files require special attention; the shell resolution logic in shell-utils.ts is straightforward to follow and well-tested. Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/agents/shell-utils.ts
Line: 129-138
Comment:
**`name.includes("sh")` matches non-shell programs**
The substring check `name.includes("sh")` is overly broad: values like `"ssh"`, `"tsh"`, or `"tmsh"` will satisfy `isUnixLikeShellName`, causing them to be accepted as a unix-like shell on Windows. A user with `SHELL=ssh` (not implausible in developer tooling environments) would end up with `{ shell: "ssh", args: ["-c"] }` instead of falling through to the PowerShell default.
Consider using a closed allowlist of recognised short names instead:
```suggestion
function isUnixLikeShellName(name: string): boolean {
return (
name === "nu" ||
name === "nushell" ||
name === "fish" ||
name === "bash" ||
name === "zsh" ||
name === "ksh" ||
name === "sh" ||
name === "ash" ||
name === "dash" ||
name === "csh" ||
name === "tcsh" ||
name === "mksh" ||
name === "pdksh"
);
}
```
Or keep the prefix/contains approach but explicitly exclude known non-shell programs that contain "sh".
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "Merge branch 'main' into codex/windows-s..." | Re-trigger Greptile |
| function isUnixLikeShellName(name: string): boolean { | ||
| return ( | ||
| name === "nu" || | ||
| name === "nushell" || | ||
| name === "fish" || | ||
| name.includes("bash") || | ||
| name.includes("zsh") || | ||
| name.includes("ksh") || | ||
| name.includes("sh") | ||
| ); |
There was a problem hiding this comment.
name.includes("sh") matches non-shell programs
The substring check name.includes("sh") is overly broad: values like "ssh", "tsh", or "tmsh" will satisfy isUnixLikeShellName, causing them to be accepted as a unix-like shell on Windows. A user with SHELL=ssh (not implausible in developer tooling environments) would end up with { shell: "ssh", args: ["-c"] } instead of falling through to the PowerShell default.
Consider using a closed allowlist of recognised short names instead:
| function isUnixLikeShellName(name: string): boolean { | |
| return ( | |
| name === "nu" || | |
| name === "nushell" || | |
| name === "fish" || | |
| name.includes("bash") || | |
| name.includes("zsh") || | |
| name.includes("ksh") || | |
| name.includes("sh") | |
| ); | |
| function isUnixLikeShellName(name: string): boolean { | |
| return ( | |
| name === "nu" || | |
| name === "nushell" || | |
| name === "fish" || | |
| name === "bash" || | |
| name === "zsh" || | |
| name === "ksh" || | |
| name === "sh" || | |
| name === "ash" || | |
| name === "dash" || | |
| name === "csh" || | |
| name === "tcsh" || | |
| name === "mksh" || | |
| name === "pdksh" | |
| ); | |
| } |
Or keep the prefix/contains approach but explicitly exclude known non-shell programs that contain "sh".
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/shell-utils.ts
Line: 129-138
Comment:
**`name.includes("sh")` matches non-shell programs**
The substring check `name.includes("sh")` is overly broad: values like `"ssh"`, `"tsh"`, or `"tmsh"` will satisfy `isUnixLikeShellName`, causing them to be accepted as a unix-like shell on Windows. A user with `SHELL=ssh` (not implausible in developer tooling environments) would end up with `{ shell: "ssh", args: ["-c"] }` instead of falling through to the PowerShell default.
Consider using a closed allowlist of recognised short names instead:
```suggestion
function isUnixLikeShellName(name: string): boolean {
return (
name === "nu" ||
name === "nushell" ||
name === "fish" ||
name === "bash" ||
name === "zsh" ||
name === "ksh" ||
name === "sh" ||
name === "ash" ||
name === "dash" ||
name === "csh" ||
name === "tcsh" ||
name === "mksh" ||
name === "pdksh"
);
}
```
Or keep the prefix/contains approach but explicitly exclude known non-shell programs that contain "sh".
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 836d3078fc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (!looksLikeExplicitPath(value)) { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Resolve bare Windows shell names before accepting override
resolveConfiguredWindowsShellValue currently accepts any non-path shell token because hasResolvableWindowsShellPath returns true when looksLikeExplicitPath is false. That means values like OPENCLAW_SHELL=bash (or SHELL=bash) are treated as valid even when bash is not installed or not on PATH, so getShellConfig() returns an executable that later fails with ENOENT instead of falling back to the next candidate or PowerShell. Please validate bare names against PATH/PATHEXT before returning them as a resolved shell.
Useful? React with 👍 / 👎.
|
Codex automated review: keeping this open. Keep this PR open. Current main still hardcodes Windows host exec to PowerShell and does not expose a custom Windows shell selection surface in code, config, or docs. The PR is a live implementation candidate, but its proposed Best possible solution: Keep this PR open for maintainer review and consolidate direction with #39413. The best implementation should choose a stable public surface, likely config-backed What I checked:
Remaining risk / open question:
Codex Review notes: model gpt-5.5, reasoning high; reviewed against 4878d3e059ce. |
Summary
OPENCLAW_SHELLandSHELLOPENCLAW_SHELLas the highest-priority override on every platform/cforcmd, PowerShell flags forpwsh/powershell,-cfor Unix-like shells)"C:\Program Files\Git\bin\bash.exe"Problem
OpenClaw previously hardcoded PowerShell on Windows and ignored
SHELL, which made it awkward to use Git Bash or other Unix-like shells for command execution. That caused practical issues around quoting and escaping, especially for commands that behave better in a Unix-like shell.There were also a few edge cases worth handling carefully:
OPENCLAW_SHELLshould still win overSHELLpwsh/powershelloverrides must use PowerShell flags, not-c/usr/bin/bashshould not break shell resolutionWhat Changed
This PR updates
src/agents/shell-utils.tsso that:getShellConfig()first triesOPENCLAW_SHELL, thenSHELLcmd, and Unix-like shells get the correct argument style automaticallydetectRuntimeShell()stays consistent with the execution-side resolution logicOPENCLAW_SHELLoverride precedence as beforeExamples
Both forms now resolve correctly on Windows.
Testing
Local verification:
pnpm test src/agents/shell-utils.test.tsAdded coverage for:
pwsh/cmdhandlingOPENCLAW_SHELLprecedence indetectRuntimeShell()