Skip to content

[FEATURE]: Shell-aware Bash tool prompt for Windows (Git Bash / cmd.exe / PowerShell) #16479

Closed as not planned

Description

@lazyalex616
  • I have searched for existing issues and confirmed this is not a duplicate.

What

Make the Bash tool's bash.txt prompt description shell-aware on Windows, so the LLM generates correct commands for whichever shell shell.ts actually selects.

Why

Today bash.txt is a static, Unix-only tool description. On Windows, shell.ts auto-selects either Git Bash (most users — anyone with Git for Windows) or cmd.exe (Anaconda Prompt / no Git in PATH). Both shells have incompatible syntax with the Unix examples in bash.txt, and the LLM has no guidance to adjust:

Shell What breaks Example
Git Bash Backslash paths silently destroyed (\U, \v = escape sequences) ls C:\Users\name → ls C:Usersname
cmd.exe Multiline silently truncated, single quotes literal, $VAR doesn't expand echo "line1\nline2" → only "line1"

These aren't bugs in the shells — they're expected behavior. The issue is that the LLM doesn't know which shell it's talking to.

Proposed solution

Generate bash.txt at runtime based on the detected shell. Small additions per shell:

  • Git Bash → "Use forward slashes for all paths. Never use backslashes."
  • cmd.exe → "Double quotes only (no single quotes). No $VAR (use %VAR%). Never pass multiline strings inline — write to temp file."
  • PowerShell → "Use PowerShell syntax, $env:VAR for env variables."

This is a minimal change in how the tool description is loaded — no architectural changes needed.

Complementary (optional)

  • Path normalization in bash.ts — convert C:\... to C:/... before spawn() as a safety net for Git Bash users.
  • Shell selection in config — let users set preferred shell in opencode.jsonc instead of auto-detection.

Evidence

Tested on two Windows machines:

  • Machine A (Git for Windows installed) → shell.ts selects Git Bash → backslash paths corrupted
  • Machine B (Anaconda Prompt, Git not in PATH) → shell.ts selects cmd.exe → multiline/quoting issues
Full test matrix (25+ scenarios across both shells)

Git Bash (bash.exe) — default shell for most Windows users

Scenario Quoting Result
echo C:\Users\vzssyd unquoted ❌ C:Usersvzssyd (backslashes eaten)
echo "C:\Users\vzssyd" double-quoted ✅ preserved
echo 'C:\Users\vzssyd' single-quoted ✅ preserved
echo $'C:\Users\vzssyd' dollar-quoted ❌ \v → vertical tab
ls C:\Users\vzssyd\Desktop unquoted ❌ No such file
ls "C:\Users\vzssyd\Desktop" double-quoted ✅ works
mkdir C:\Users\vzssyd\test unquoted ❌ creates C:Usersvzssydtest in cwd
cp C:\a\b C:\c\d unquoted ❌ fails
myvar=C:\path unquoted assign ❌ backslashes lost
sed 's/old/C:\Users\new/' sed pattern ❌ \U→uppercase, \n→newline
node -e "..." C:\path arg to exe ❌ lost (bash parses first)
for f in C:\a C:\b for loop ❌ lost
heredoc <<EOF heredoc body ✅ preserved
workdir parameter N/A (Node cwd) ✅ works (bypasses bash)

cmd.exe — only when Git Bash not in PATH

Scenario Command Expected Actual Severity
Multiline string echo "line1\nline2\nline3" 3 lines Only "line1" — rest silently truncated 🔴 Data loss
Single quotes echo 'hello' hello 'hello' (quotes treated as literal chars) 🟡 Unexpected
jq single quotes gh --jq '.[0].title' Parsed unexpected token "'" — hard crash 🔴 Crash
$VAR expansion echo $PATH PATH value Literal $PATH (cmd uses %VAR%) 🟡 Silent fail
Same-line set+echo set "X=hi" && echo %X% hi %X% (expanded at parse time, before set runs) 🟡 Silent fail
PowerShell here-string powershell -Command "@'...'@" Multiline TerminatorExpectedAtEndOfString 🔴 Crash
jq double quotes gh --jq ".[0].title" Parsed ✅ works ✅
%VAR% expansion echo %USERNAME% Username ✅ works ✅
&& chaining echo a && echo b Two lines ✅ works ✅
Pipe echo hello | findstr "hello" hello ✅ works ✅
Unicode/emoji echo "✅ 🟢 中文" As-is ✅ works ✅
Node.js node -e "console.log('中文')" 中文 ✅ works ✅
Paths with spaces type "C:\path with spaces\f.txt" Content ✅ works (when quoted) ✅
for loop for %i in (1 2 3) do echo %i 1 2 3 ✅ works ✅
Root cause in source code

Shell selection: shell.ts

shell.ts → fallback() scans PATH at spawn time for bash.exe. The result depends entirely on what's installed and where it appears in PATH — not which Terminal app launched OpenCode.

Environment Shell selected Why
Git for Windows installed (most users) Git Bash (C:\Program Files\Git\usr\bin\bash.exe) bash.exe found first in PATH
Anaconda Prompt / no Git in PATH cmd.exe No bash.exe found, falls back to %COMSPEC%

Command execution: bash.ts line 172

const proc = spawn(params.command, {
  shell,  // ← Git Bash (bash.exe) or cmd.exe
  // ...
})

Data flow: LLM JSON {"command": "ls C:\\\\Users\\\\vzssyd"} → JSON parse → string ls C:\Users\vzssyd → spawn(command, {shell}) → shell-specific interpretation → corruption.

Tool description: bash.txt

All path examples are Unix-style (/Users/name/...). No mention of Windows backslash hazard or cmd.exe quoting/multiline limitations. The LLM has no guidance for either shell variant.

Workaround

We've been running a prompt-level workaround that injects shell-aware rules into the agent's instruction files (AGENTS.md). The LLM checks env.Platform at session start and applies the matching rules. It's been working well in daily use on both machines.

Full workaround setup

Add this to your AGENTS.md (or CLAUDE.md / system prompt):

Step 1: Platform check header

At the top of your instruction file, add a platform gate so the LLM knows to check before running any command:

> ⚠️ **PLATFORM CHECK** — If `env.Platform` is `win32`, check the Shell table below
> for which shell this host uses. cmd.exe needs workarounds; Git Bash works like normal Unix.

Step 2: Shell identification table

Create a table mapping each Windows machine to its actual shell (you can detect this by running echo $SHELL — if it returns a path, it's Git Bash; if it echoes $SHELL literally, it's cmd.exe):

| Shell | Hosts | Key Constraint |
|-------|-------|----------------|
| zsh | Mac | Standard Unix, no issues |
| Git Bash | (your Git Bash machines) | Forward slashes, quote spaces |
| cmd.exe | (your cmd.exe machines) | No multiline, no `'`, no `$VAR` |

Step 3: Shell-specific rules

## Shell Execution Rules

The Bash tool spawns different shells per platform.
**Check `env.Platform` at session start** to apply the correct rules.

### macOS — zsh (no special rules)

Standard Unix shell. Single/double quotes, `$VAR`, multiline strings, pipes — all work as expected.

### Windows — check Shell table first

OpenCode's shell depends on what's in PATH at spawn time, **not** the Terminal app.
Most Windows machines with Git for Windows get **Git Bash**;
special setups (e.g. Anaconda Prompt) get **cmd.exe**.

#### Git Bash (default for most Windows hosts)

Works like normal Unix (zsh/bash). Minor differences:

1. **Use forward slashes in paths** — `C:/Users/Alex`, not `C:\Users\Alex`
   (backslashes get interpreted as escape sequences).
   The `workdir` parameter also requires forward slashes.
2. **Quote paths with spaces** using double quotes — `"C:/Program Files/foo"`.
3. **Beware MSYS2 auto path conversion** — Git Bash silently converts `/c/...` args to `C:\...`.
   This breaks tools like Docker that take Unix-style paths as arguments.
   → Prefix with `MSYS_NO_PATHCONV=1` to disable: `MSYS_NO_PATHCONV=1 docker run -v /c/Users/...`
4. **Prefer built-in tools over Bash for file operations.**
   Read/Write/Edit/Glob/Grep tools bypass the shell entirely — safest option.
   Only use Bash for actual terminal commands (git, npm, gh, docker, etc.).

#### cmd.exe (special setups only)

1. **Never send multiline commands inline.**
   cmd.exe silently drops everything after the first `\n`.
   → Write content to a temp file, then reference it
     (e.g. `gh issue edit --body-file %TEMP%\body.md`).
   → Chain sequential commands with `&&` on a single line.

2. **Never use single quotes `'` — always use double quotes `"`.**
   cmd.exe treats `'` as a literal character, not a string delimiter.
   → `gh --jq '.name'` ✗ → `gh --jq ".name"` ✓

3. **Never use `$VAR` — use `%VAR%` or avoid shell variables entirely.**
   → `echo %USERNAME%` ✓ | `echo $USER` ✗

4. **Never use PowerShell-specific syntax inline.**
   `@"..."@`, `$(...)`, `-not`, `Where-Object` — none work in cmd.
   → `powershell -Command "simple single-line"` or write a `.ps1` file.

5. **Escape special characters in strings** — `>`, `<`, `|`, `&` are shell metacharacters in cmd.
   → Use `^` to escape: `echo foo ^& bar`, `echo foo ^| bar`.

6. **`%` in for loops doubles inside `.cmd` scripts** — `for %i` works interactively, but `.cmd` files need `%%i`.

7. **Prefer built-in tools over Bash for file operations.**
   Read/Write/Edit/Glob/Grep tools bypass the shell entirely — no cmd.exe corruption risk.

How it works

OpenCode already injects env.Platform into the context at session start. The LLM reads it, matches against the rules above, and generates shell-appropriate commands. On macOS sessions, the Windows rules are completely ignored — zero overhead.

Results

After deploying these rules, shell-related failures dropped to near zero in daily usage across both Windows machines. The only remaining edge case is very long conversations where context pressure causes the LLM to "forget" the rules, but that's rare.

This is a prompt-level workaround, not a fix. The real solution is a dynamic bash.txt that adapts to the detected shell automatically — which is what this feature request is about.

Relevant source

  • packages/opencode/src/tool/bash.txt — static Unix-only tool description
  • packages/opencode/src/tool/bash.ts line 172 — spawn(command, {shell}) with no path normalization
  • packages/opencode/src/shell/shell.ts — fallback() auto-selects bash.exe or cmd.exe

Environment

  • OpenCode 1.2.16
  • Windows 11 Enterprise, two machines (Git Bash default + cmd.exe default)

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 7, 2026
  2. github-actions commented on Mar 7, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • The title should start with [FEATURE]: for feature requests (currently starts with Feature:)
    • Feature request template requires a verification checkbox confirming you've searched for existing issues

    Please edit this issue to address the above within 2 hours, or it will be automatically closed.


    This issue might be a duplicate of existing issues. Please check:

    If you believe this was flagged incorrectly, please let a maintainer know.

  3. changed the title [-]Feature: shell-aware Bash tool prompt for Windows (Git Bash / cmd.exe / PowerShell)[/-] [+][FEATURE]: Shell-aware Bash tool prompt for Windows (Git Bash / cmd.exe / PowerShell)[/+] on Mar 7, 2026
  4. github-actions commented on Mar 7, 2026

    @github-actions
    Contributor

    Thanks for updating the issue! It now meets the contributing guidelines and looks great. The description clearly explains the problem, includes evidence from real testing, and provides a concrete proposed solution. The needs:compliance label has been removed.

  5. github-actions commented on Mar 7, 2026

    @github-actions
    Contributor

    Thanks for updating the issue! It now follows the contributing guidelines and looks great. The needs:compliance label has been removed.

  6. lazyalex616 commented on Mar 30, 2026

    @lazyalex616
    Author

    This has been addressed by #16069 (merged in v1.3.7), which adds first-class PowerShell support on Windows — including dynamic shell-aware prompt templates (\, \, \), \ ree-sitter-powershell\ command parsing, and dedicated PowerShell spawn logic. The core ask of this issue (make bash.txt adapt to the detected shell) is fully covered. Closing.

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

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)windows

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions