Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Haleclipse/1c35fe7b98fe1b5ea0523eb4d3a10f0b to your computer and use it in GitHub Desktop.

Select an option

Save Haleclipse/1c35fe7b98fe1b5ea0523eb4d3a10f0b to your computer and use it in GitHub Desktop.
Fix Claude Code Windows path issue (v2.1.5) - Bash tool creates temp files in cwd instead of temp directory
<#
.SYNOPSIS
Claude Code Windows Path Fix Script
.DESCRIPTION
Fixes the Windows Bash tool temp file path issue in Claude Code.
THE BUG:
On Windows + Git Bash, Claude Code uses Node.js path.join() to generate
temp file paths. However, path.join on Windows produces backslashes (\).
When this path is passed to Bash, backslashes are interpreted as escape chars:
- \tmp\claude-xxxx-cwd -> tmpclaude-xxxx-cwd (relative path!)
This causes temp files to be incorrectly created in the current working directory.
THE FIX:
This script patches the code to convert backslashes to forward slashes
on Windows before the path is used in Bash commands:
if(LQ()==="windows")H=H.replace(/\\/g,"/");
.PARAMETER Check
Check if fix is needed without making changes
.PARAMETER Restore
Restore original file from backup
.PARAMETER Help
Show help information
.EXAMPLE
.\apply-claude-code-windows-path-fix-en.ps1
Apply the fix
.EXAMPLE
.\apply-claude-code-windows-path-fix-en.ps1 -Check
Check if fix is needed
.EXAMPLE
.\apply-claude-code-windows-path-fix-en.ps1 -Restore
Restore from backup
.NOTES
Note: This patch will be overwritten when Claude Code updates.
Re-run this script after updates if the issue reoccurs.
#>
param(
[switch]$Check,
[switch]$Restore,
[switch]$Help
)
# Color output functions
function Write-Success { param($Message) Write-Host "[OK] " -ForegroundColor Green -NoNewline; Write-Host $Message }
function Write-Warning { param($Message) Write-Host "[!] " -ForegroundColor Yellow -NoNewline; Write-Host $Message }
function Write-Error { param($Message) Write-Host "[X] " -ForegroundColor Red -NoNewline; Write-Host $Message }
function Write-Info { param($Message) Write-Host "[>] " -ForegroundColor Blue -NoNewline; Write-Host $Message }
# Show help
if ($Help) {
Get-Help $MyInvocation.MyCommand.Path -Detailed
exit 0
}
# Find Claude Code cli.js path
function Find-CliPath {
$locations = @(
(Join-Path $env:USERPROFILE ".claude\local\node_modules\@anthropic-ai\claude-code\cli.js"),
(Join-Path $env:APPDATA "npm\node_modules\@anthropic-ai\claude-code\cli.js"),
(Join-Path $env:ProgramFiles "nodejs\node_modules\@anthropic-ai\claude-code\cli.js"),
(Join-Path ${env:ProgramFiles(x86)} "nodejs\node_modules\@anthropic-ai\claude-code\cli.js")
)
# Try to get global path from npm
try {
$npmRoot = & npm root -g 2>$null
if ($npmRoot) {
$locations += Join-Path $npmRoot "@anthropic-ai\claude-code\cli.js"
}
} catch {}
foreach ($path in $locations) {
if (Test-Path $path) {
return $path
}
}
return $null
}
$cliPath = Find-CliPath
if (-not $cliPath) {
Write-Error "Claude Code cli.js not found"
Write-Host ""
Write-Host "Searched locations:"
Write-Host " ~\.claude\local\node_modules\@anthropic-ai\claude-code\cli.js"
Write-Host " %APPDATA%\npm\node_modules\@anthropic-ai\claude-code\cli.js"
Write-Host " %ProgramFiles%\nodejs\node_modules\@anthropic-ai\claude-code\cli.js"
Write-Host " `$(npm root -g)\@anthropic-ai\claude-code\cli.js"
exit 1
}
# Restore backup
if ($Restore) {
$backups = Get-ChildItem -Path (Split-Path $cliPath) -Filter "cli.js.backup-winpath-*" -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending
if ($backups.Count -gt 0) {
$latestBackup = $backups[0].FullName
Copy-Item $latestBackup $cliPath -Force
Write-Success "Restored from backup: $latestBackup"
exit 0
}
Write-Error "No backup files found"
exit 1
}
Write-Info "Found Claude Code at: $cliPath"
Write-Host ""
# Download acorn parser if needed
$acornPath = Join-Path $env:TEMP "acorn-claude-fix.js"
if (-not (Test-Path $acornPath)) {
Write-Info "Downloading acorn parser..."
try {
Invoke-WebRequest -Uri "https://unpkg.com/[email protected]/dist/acorn.js" -OutFile $acornPath -UseBasicParsing
} catch {
Write-Error "Failed to download acorn parser"
exit 1
}
}
# Create patch script
$patchScript = @'
const fs = require('fs');
const acornPath = process.argv[2];
const acorn = require(acornPath);
const cliPath = process.argv[3];
const checkOnly = process.argv[4] === '--check';
let code = fs.readFileSync(cliPath, 'utf-8');
// Strip shebang (will restore later)
let shebang = '';
if (code.startsWith('#!')) {
const idx = code.indexOf('\n');
shebang = code.slice(0, idx + 1);
code = code.slice(idx + 1);
}
// Check if already patched
// Patch signature: .replace(/\\/g,"/") near -cwd related code
const patchPattern = /\.replace\(\/\\\\\/g,["'"]\/["']\)/;
if (patchPattern.test(code)) {
// Further verify it's in the correct location
const cwdIdx = code.indexOf('-cwd');
const replaceIdx = code.search(patchPattern);
if (cwdIdx !== -1 && replaceIdx !== -1 && Math.abs(cwdIdx - replaceIdx) < 500) {
console.log('ALREADY_PATCHED');
process.exit(2);
}
}
// Parse JavaScript
let ast;
try {
ast = acorn.parse(code, { ecmaVersion: 2022, sourceType: 'module' });
} catch (e) {
console.error('PARSE_ERROR:' + e.message);
process.exit(1);
}
// AST helper functions
const src = (node) => code.slice(node.start, node.end);
function findNodes(node, predicate, results = []) {
if (!node || typeof node !== 'object') return results;
if (predicate(node)) results.push(node);
for (const key in node) {
if (node[key] && typeof node[key] === 'object') {
if (Array.isArray(node[key])) {
node[key].forEach(child => findNodes(child, predicate, results));
} else {
findNodes(node[key], predicate, results);
}
}
}
return results;
}
// Check if template literal contains specified text
function templateContains(node, text) {
if (node.type !== 'TemplateLiteral') return false;
const raw = node.quasis.map(q => q.value.raw).join('');
return raw.includes(text);
}
// Check if node (including children) contains specified template text
function containsTemplate(node, text) {
const templates = findNodes(node, n => n.type === 'TemplateLiteral');
return templates.some(t => templateContains(t, text));
}
// Find all functions (including arrow functions)
const allFunctions = findNodes(ast, n =>
n.type === 'FunctionDeclaration' ||
n.type === 'FunctionExpression' ||
n.type === 'ArrowFunctionExpression'
);
// 1. Find function containing "pwd -P >|" (bash exec function)
let bashExecFunc = null;
for (const fn of allFunctions) {
if (containsTemplate(fn, 'pwd -P >|')) {
bashExecFunc = fn;
break;
}
}
if (!bashExecFunc) {
console.error('NOT_FOUND:bash_exec_function (pwd -P >|)');
process.exit(1);
}
console.log('FOUND:bash_exec_function at position ' + bashExecFunc.start);
// 2. Find variable declaration containing "-cwd" in that function (H variable)
// Pattern: H=J?wG0(F,`cwd-${K}`):wG0(V,`claude-${K}-cwd`)
let targetVarDecl = null;
let hDeclarator = null;
const varDecls = findNodes(bashExecFunc, n => n.type === 'VariableDeclaration');
for (const decl of varDecls) {
for (const d of decl.declarations) {
if (!d.init) continue;
// Check if it's a conditional expression containing -cwd template
if (d.init.type === 'ConditionalExpression') {
const hasCwd = containsTemplate(d.init, '-cwd') ||
containsTemplate(d.init, 'cwd-');
if (hasCwd) {
targetVarDecl = decl;
hDeclarator = d;
break;
}
}
}
if (targetVarDecl) break;
}
if (!targetVarDecl || !hDeclarator) {
console.error('NOT_FOUND:cwd_path_variable (claude-*-cwd or cwd-*)');
process.exit(1);
}
const hVarName = hDeclarator.id.name;
console.log('FOUND:cwd_path_variable=' + hVarName + ' at position ' + targetVarDecl.start);
// Output matched code snippet for verification
const matchedCode = src(targetVarDecl);
console.log('MATCHED_CODE:' + matchedCode.slice(0, 100) + (matchedCode.length > 100 ? '...' : ''));
// 3. Find LQ function name (platform detection function)
// Look for pattern like LQ() === "windows"
let lqFuncName = null;
const binaryExprs = findNodes(bashExecFunc, n =>
n.type === 'BinaryExpression' &&
(n.operator === '===' || n.operator === '==') &&
n.right?.type === 'Literal' &&
n.right?.value === 'windows'
);
for (const expr of binaryExprs) {
if (expr.left?.type === 'CallExpression' && expr.left.callee?.type === 'Identifier') {
lqFuncName = expr.left.callee.name;
break;
}
}
if (!lqFuncName) {
console.error('NOT_FOUND:platform_check_function (LQ)');
process.exit(1);
}
console.log('FOUND:platform_check_function=' + lqFuncName);
// 4. Determine insert position
// Insert after variable declaration statement ends (at semicolon)
let insertPosition = targetVarDecl.end;
// Check semicolon position
const charAtEnd = code.charAt(insertPosition - 1);
const charAfterEnd = code.charAt(insertPosition);
console.log('CHAR_AT_END:' + JSON.stringify(charAtEnd));
console.log('CHAR_AFTER_END:' + JSON.stringify(charAfterEnd));
// If declaration doesn't end with semicolon but next char is semicolon, adjust position
if (charAtEnd !== ';' && charAfterEnd === ';') {
insertPosition++;
}
console.log('INSERT_POSITION:' + insertPosition);
if (checkOnly) {
console.log('NEEDS_PATCH');
process.exit(1);
}
// 5. Build patch code
// Insert after variable declaration: if(LQ()==="windows")H=H.replace(/\\/g,"/");
const patchCode = `if(${lqFuncName}()==="windows")${hVarName}=${hVarName}.replace(/\\\\/g,"/");`;
console.log('PATCH_CODE:' + patchCode);
// 6. Apply patch
const newCode = shebang + code.slice(0, insertPosition) + patchCode + code.slice(insertPosition);
// 7. Backup original file
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const backupPath = cliPath + '.backup-winpath-' + timestamp;
fs.copyFileSync(cliPath, backupPath);
console.log('BACKUP:' + backupPath);
// 8. Write patched file
fs.writeFileSync(cliPath, newCode);
// 9. Verify patch
const verifyCode = fs.readFileSync(cliPath, 'utf-8');
if (verifyCode.includes(patchCode)) {
console.log('SUCCESS');
} else {
console.error('VERIFY_FAILED');
fs.copyFileSync(backupPath, cliPath);
process.exit(1);
}
'@
$tempPatchScript = Join-Path $env:TEMP "claude-winpath-patch-$PID.js"
$patchScript | Out-File -FilePath $tempPatchScript -Encoding UTF8
# Run patch script
$checkArg = if ($Check) { "--check" } else { "" }
$output = & node $tempPatchScript $acornPath $cliPath $checkArg 2>&1
$exitCode = $LASTEXITCODE
# Cleanup temp script
Remove-Item $tempPatchScript -ErrorAction SilentlyContinue
# Process output
foreach ($line in $output) {
switch -Regex ($line) {
"^ALREADY_PATCHED" { Write-Success "Already patched"; exit 0 }
"^PARSE_ERROR:(.+)" { Write-Error "Failed to parse cli.js: $($Matches[1])"; exit 1 }
"^NOT_FOUND:(.+)" { Write-Error "Target code not found: $($Matches[1])"; exit 1 }
"^FOUND:(.+)=(.+)" { Write-Info "Found $($Matches[1]): $($Matches[2])" }
"^FOUND:(.+) at position (.+)" { Write-Info "Found $($Matches[1]) (position: $($Matches[2]))" }
"^MATCHED_CODE:(.+)" { Write-Host " Matched code: " -NoNewline; Write-Host $Matches[1] -ForegroundColor DarkGray }
"^CHAR_AT_END:(.+)" { Write-Host " Char at decl end: $($Matches[1])" -ForegroundColor DarkGray }
"^CHAR_AFTER_END:(.+)" { Write-Host " Char after decl: $($Matches[1])" -ForegroundColor DarkGray }
"^INSERT_POSITION:(.+)" { Write-Info "Insert position: $($Matches[1])" }
"^PATCH_CODE:(.+)" { Write-Info "Patch code: $($Matches[1])" }
"^NEEDS_PATCH" {
Write-Host ""
Write-Warning "Patch needed - run without -Check to apply"
exit 1
}
"^BACKUP:(.+)" { Write-Host ""; Write-Host "Backup: $($Matches[1])" }
"^SUCCESS" {
Write-Host ""
Write-Success "Fix applied successfully!"
Write-Host ""
Write-Warning "Restart Claude Code for changes to take effect"
}
"^VERIFY_FAILED" { Write-Error "Verification failed, restoring backup..."; exit 1 }
}
}
exit $exitCode
@caozhiyuan
Copy link

Do you also use Windows? I created a patch that can solve the issue of slow file reading in the native Windows environment. On Linux, it takes 100ms to read a file, while on Windows it takes 2-3 seconds, with a large number of cygpatch commands executed every time a file is read.
@Haleclipse https://github.com/caozhiyuan/claude-code-patch . there is issue anthropics/claude-code#13361

@Haleclipse
Copy link
Author

Haleclipse commented Jan 12, 2026

Do you also use Windows? I created a patch that can solve the issue of slow file reading in the native Windows environment. On Linux, it takes 100ms to read a file, while on Windows it takes 2-3 seconds, with a large number of cygpatch commands executed every time a file is read. @Haleclipse https://github.com/caozhiyuan/claude-code-patch . there is issue anthropics/claude-code#13361

Thank you for your work, even though I'm using macOS.

I'll learn to review your code.

@caozhiyuan
Copy link

Claude Code is the buggiest product I've ever used; they probably don't have QA, haha!

@cailleachzou
Copy link

yes , but how to del the "nul" in my folder , i used the following command ,but it is not work
del "\?\C:\Users\HI.claude\skills\md-translator\nul"
Remove-Item -LiteralPath "\?\C:\Users\HI.claude\skills\md-translator\nul" -Force

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