claude-code-pathfix
v2.0.5
Published
Claude Code hook that transparently converts Windows paths to POSIX in Bash commands — eliminating retry loops and saving tokens
Maintainers
Readme
claude-code-pathfix
Stop burning tokens on Windows path errors.
Every time Claude Code generates a Bash command on Windows, there's a coin flip: will it use D:\Users\Tom\file.txt or /d/Users/Tom/file.txt? The wrong one fails, Claude reasons about the error, retries, sometimes fails again — and you pay for every token.
claude-code-pathfix makes the problem disappear. It's a zero-config PreToolUse hook that silently rewrites Windows paths to POSIX format before Bash commands execute. No errors. No retries. No wasted tokens.
The problem
Claude Code runs Bash (Git Bash / MSYS2) on Windows, but the AI constantly generates Windows-style paths:
# What Claude generates
cat D:\Users\Tom\.claude\settings.json
git -C C:\dev\myproject status
ls "C:\Program Files\Git\bin"
# What Git Bash actually needs
cat /d/Users/Tom/.claude/settings.json
git -C /c/dev/myproject status
ls "/c/Program Files/Git/bin"Each failure triggers a retry loop:
- Command fails → error message (tokens spent)
- Claude reasons about the error (more tokens spent)
- Claude retries with a corrected path (even more tokens)
- Sometimes repeats 3-4 times before succeeding
This is the most common source of wasted tokens on Windows. There are 15+ open issues about it in the Claude Code repo.
The fix
Install claude-code-pathfix and add two lines to your settings. Every Bash command is silently intercepted, paths are converted, and the corrected command executes on the first try.
Before: 3-4 attempts per path error × multiple errors per session = hundreds of wasted tokens
After: zero failures, zero retries, zero wasted tokensPrior art
The blog post Fixing Claude Code's PowerShell Problem with Hooks pioneered the idea of using hooks to catch Windows path errors. That approach blocks commands with bad paths and forces Claude to retry with the correct format — a "block-and-correct" pattern. The author reported going from "three attempts per PowerShell operation" to "zero failures."
claude-code-pathfix takes this further:
| | netnerds.net approach | claude-code-pathfix | |---|---|---| | Mechanism | Block the command, force a retry | Transparently rewrite the command | | Retries needed | 1 (Claude must resubmit) | 0 (fixed before execution) | | Tokens saved | ~60% of retry cost | ~100% of retry cost | | Claude awareness | Claude sees the block, reasons about it | Claude never knows — command just works | | Scope | PowerShell commands | All Bash commands |
The key difference: blocking still costs a round-trip. Claude sees the error, thinks about it, and resubmits — that's tokens spent on reasoning about a problem that could have been silently fixed. claude-code-pathfix uses Claude Code's updatedInput feature (shipped in v2.0.10) to rewrite the command in-flight. The AI never sees an error because there isn't one.
Install
Option 1: Claude Code plugin (recommended)
Inside Claude Code, run:
/plugin marketplace add fnrhombus/claude-plugins
/plugin install claude-code-pathfix@fnrhombus-pluginsDone. The hook is registered automatically — no settings.json edits required. Uninstall with /plugin uninstall claude-code-pathfix@fnrhombus-plugins.
The marketplace at
fnrhombus/claude-pluginsauto-discovers everyfnrhombusplugin tagged with theclaude-code-plugintopic, so adding it once gives you access to all of them.
Option 2: npm + settings.json
Add this to your Claude Code settings (~/.claude/settings.json):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "npx -y claude-code-pathfix"
}
]
}
]
}
}npx downloads and caches the package automatically on first run — no global install needed.
Or, have Claude Code configure itself:
claude -p --allowedTools "Edit(~/.claude/settings.json)" \
"Add a PreToolUse hook to your user settings.json: matcher 'Bash', type 'command', command 'npx -y claude-code-pathfix'"What it converts
| Input | Output |
|---|---|
| D:\Users\Tom\file.txt | /d/Users/Tom/file.txt |
| C:\dev\myproject\src\index.ts | /c/dev/myproject/src/index.ts |
| "C:\Program Files\Git\bin" | "/c/Program Files/Git/bin" |
| E:\data\export\ | /e/data/export/ |
What it doesn't touch
- Paths that are already POSIX (
/d/Users/Tom/...) - Relative paths with backslashes (
src\file.ts) — see tip below - URLs (
https://example.com) - Escape sequences (
\n,\t) - Non-path backslash usage (
grep 'foo\|bar')
Tip: This hook can only reliably detect Windows paths that start with a drive letter (
C:\...). Relative paths with backslashes (src\components\App.tsx) are ambiguous in bash and can't be safely distinguished from escape sequences. To get the most out of this hook, add the following to your project'sCLAUDE.md:Always use absolute paths in shell commands.
Escape hatch
If a specific command needs its Windows paths left alone (e.g., passing a path to a Windows-native tool), prefix it with ⟪!⟫:
⟪!⟫somecommand C:\Users\Tom\file.txtThe prefix is stripped before execution — the command runs as somecommand C:\Users\Tom\file.txt with no path conversion. No restart or settings change needed.
Cross-platform safe
If your Claude Code settings are symlinked across Windows and WSL (a common setup), claude-code-pathfix detects the platform at startup and silently exits on non-Windows systems. It will never interfere with native Linux or macOS Bash commands.
Performance
The hook is a tiny Node.js script built on @fnrhombus/claude-code-hooks, a strongly-typed wrapper for the Claude Code hook API. It starts in under 20ms — compared to 200-500ms for hooks that spawn a Bash subshell (#34457). You won't notice it's there.
How it works
- Claude Code's
PreToolUseevent fires before every Bash command claude-code-pathfixreads the command from stdin- A two-pass regex converts Windows absolute paths to POSIX format:
- Pass 1: quoted paths (handles spaces in paths like
"C:\Program Files\...") - Pass 2: unquoted paths
- Pass 1: quoted paths (handles spaces in paths like
- If any paths were converted, the fixed command is returned via
updatedInput - If nothing changed, the hook exits silently (no output = no modification)
Requirements
- Claude Code v2.0.10+ (for
updatedInputhook support) - Node.js 20+
- Windows with Git Bash or MSYS2
Support
If you find this project useful, consider supporting its development:
- GitHub Sponsors — Monthly sponsorship with public recognition
- Buy Me a Coffee — One-time or recurring donations
Your support helps maintain and improve this project!
License
MIT
