Claude Code Plugin that makes the
Bashtool work cleanly on Windows. POSIX-safe commands pass straight through to Git Bash; Unix idioms get rewritten to their PowerShell equivalents; MSYS drive paths (/c/…) becomeC:/…when routed to PS; Unix-only commands are denied with a specific hint pointing Claude at the Windows-native alternative.
On macOS and Linux the hook checks process.platform first and no-ops immediately — the original Bash command runs unchanged. Safe to install on any host.
- No more "bash: command not found" on Windows — Claude's
Bashcalls just work. - Zero-overhead fast path — POSIX-safe commands (
git,npm,grep,sed,jq, …) stay in Git Bash; PowerShell startup is only paid when needed. Unknown binaries stay there too — Git Bash sees morePATHthan PowerShell does. - Targeted PS rewrites —
xdg-open,pbcopy,uname,df,free, and friends map to clean PowerShell equivalents. - MSYS drive-path translation —
/c/Users/foo→C:/Users/foowhen the segment is routed to PS. URL-safe, packed-flag-aware (-I/c/includeworks), quote-aware. - Smart denial with hints —
sudo,systemctl,apt,chmod, etc. don't silently fail; Claude is told what to use instead (Start-Process -Verb RunAs,Get-Service,winget,icacls, …). - PS 5.1 ↔ PS 7 aware —
&&/||are used natively on PS 7, denied on PS 5.1 with a pointer tohttps://aka.ms/powershell. - Silent on errors — internal transpiler bugs never block Claude; the original command runs.
From the Claude Code REPL:
/plugin marketplace add hmennen90/claude-code-win32
/plugin install claude-code-win32@claude-code-win32
/reload-plugins
Or against a local checkout (development):
/plugin marketplace add /absolute/path/to/claude-code-win32
/plugin install claude-code-win32@claude-code-win32
npm install claude-code-win32After install:
- Library use —
import { transpile } from "claude-code-win32"(alsoclaude-code-win32/paths,claude-code-win32/chain). - Standalone hook binary —
claude-code-win32-hookis on thebinpath. Wire it into your ownhooks.jsondirectly if you prefer that to a Claude Code marketplace install.
Registers a PreToolUse hook on the Bash tool. For each command:
- Platform gate —
process.platform !== "win32"→ no-op. - Shell detection — probes for
pwsh(PS 7+), falls back topowershell.exe(5.1). Cached 24h under~/.claude/plugins/claude-code-win32.state.json. - Chain parse — quote- and paren-aware split at
|,&&,||,;,&. Redirect tokens (2>&1,1>&2,&>file,&>>file) are recognised and not misclassified as chain operators. - Per-segment classification:
- passthrough — external
.exe/ POSIX allowlist (git, node, npm, docker, grep, sed, awk, jq, rg, …). An unknown binary also passes through: the allowlist can never be complete, and Git Bash resolves both the WindowsPATHand the MSYS/usr/bintree, while PowerShell only sees the former. Only PowerShell-shaped names (Verb-Nouncmdlets,*.ps1) are routed to PS. - rewrite — known Unix idiom with a clean PowerShell equivalent (
xdg-open,pbcopy,whoami,uname,df,free, …) - deny — Unix-only with no Windows equivalent (package managers,
systemctl,sudo,chmod,dmesg,iptables, …)
- passthrough — external
- Deny gates:
- Any segment is Unix-only → deny with a per-command hint pointing Claude at the Windows-native alternative.
&&/||present AND detected shell is PS 5.1 → deny withhttps://aka.ms/powershellinstall link.
- Pass fast-path — all segments Git-Bash-safe (allowlisted or unknown), no rewrite needed,
preferPowerShell=false→ allow unchanged (avoids PS 100–500 ms startup). - MSYS path translation — for segments routed to PowerShell, leading
/c/,/d/, … prefixes are rewritten toC:/,D:/, …. Boundary-aware: URLs (https://…), UNC paths (//server/share),file:///c/…, and single-quoted literals are left intact. Packed compiler flags like-I/c/include,-Wl,/c/lib,-isystem/c/sysare recognised and translated. - Strategy A rewrite — rebuild the chain in PowerShell syntax (
&&/||native on PS 7; trailing&→Start-Job), prepend$ProgressPreference='SilentlyContinue';to suppress CLIXML progress noise on PS 5.1, wrap in<exe>.exe -NoProfile -NonInteractive -EncodedCommand <base64>.
| Variable | Default | Meaning |
|---|---|---|
WIN32_TRANSPILE |
1 |
Master switch. |
WIN32_TRANSPILE_ASSUME_GIT_BASH |
1 |
If 1, POSIX-safe segments pass through. |
WIN32_TRANSPILE_PREFER_PS |
0 |
If 1, route everything via PowerShell (no Git Bash fast-path). |
WIN32_TRANSPILE_POSIX_SAFE |
— | Extra binaries to treat as passthrough. |
WIN32_TRANSPILE_FORCE_PS |
— | Extra binaries to always route via PowerShell. |
WIN32_TRANSPILE_DENY |
— | Extra binaries to deny outright. |
git clone https://github.com/hmennen90/claude-code-win32
cd claude-code-win32
npm install
npm run build
npm testCI runs the suite on Node 20/22/24 across ubuntu-latest and windows-latest, and
checks that package.json, .claude-plugin/plugin.json and
.claude-plugin/marketplace.json carry the same version.
npm version <patch|minor|major> # bumps package.json, syncs plugin/marketplace, tags, pushesThe v*.*.* tag triggers .github/workflows/release.yml: it builds and tests on
Linux and Windows, verifies the tag matches package.json, creates the GitHub
release with generated notes, and publishes to npm with provenance. The npm step is
skipped (not failed) when no NPM_TOKEN secret is configured.
v0.1.0 — initial release. 40 unit tests, live verified on PS 5.1 (Windows 11).
Known limitation: nested commands under xargs foo are classified on the outer binary only. sudo is on the deny-list, so the most common case is already covered.
jq: command not found (or any tool that works in Git Bash but not through the hook).
Fixed in v0.1.2. Before that, any binary missing from the POSIX allowlist pulled the
entire chain into PowerShell — including the grep/sed/cat segments around it.
PowerShell does not see the MSYS /usr/bin tree, so a perfectly installed jq reported
as missing. Unknown binaries now stay in Git Bash. If a tool is genuinely not installed,
install it on the Windows PATH (winget install jqlang.jq, scoop install jq).