From b654c005037ba921fa8c7492aaf6492fd957e07d Mon Sep 17 00:00:00 2001 From: Karan Parikh Date: Sun, 14 Dec 2025 18:22:31 +1100 Subject: [PATCH] feat(plugins): add windows-nul-fix plugin to auto-remove problematic nul files On Windows, when Claude Code runs bash commands using Git Bash, it sometimes uses '2>nul' for stderr redirection (Windows CMD syntax). In Unix shells, this creates a literal file named 'nul' instead of redirecting to the null device, causing several issues: - Cannot be easily deleted via Windows Explorer or PowerShell - Breaks git add/commit operations - Can corrupt source control history - Appears repeatedly after each bash command This plugin adds a PostToolUse hook that automatically removes any 'nul' files from the current working directory after Bash tool execution. Features: - Only activates on Windows (os.platform() === "win32") - Runs silently after every Bash tool execution - Does not interrupt workflow on errors - Minimal overhead (5s timeout) Fixes #4928 --- .../.claude-plugin/plugin.json | 9 ++ plugins/windows-nul-fix/README.md | 89 +++++++++++++++++++ plugins/windows-nul-fix/hooks/hooks.json | 17 ++++ plugins/windows-nul-fix/scripts/remove-nul.js | 63 +++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 plugins/windows-nul-fix/.claude-plugin/plugin.json create mode 100644 plugins/windows-nul-fix/README.md create mode 100644 plugins/windows-nul-fix/hooks/hooks.json create mode 100644 plugins/windows-nul-fix/scripts/remove-nul.js diff --git a/plugins/windows-nul-fix/.claude-plugin/plugin.json b/plugins/windows-nul-fix/.claude-plugin/plugin.json new file mode 100644 index 000000000..c257be023 --- /dev/null +++ b/plugins/windows-nul-fix/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "windows-nul-fix", + "version": "1.0.0", + "description": "Automatically removes 'nul' files created by Claude Code on Windows when using Git Bash or other Unix-like shells", + "author": "Anthropic Community", + "homepage": "https://github.com/anthropics/claude-code/issues/4928", + "keywords": ["windows", "nul", "fix", "bash", "git-bash"], + "platforms": ["windows"] +} diff --git a/plugins/windows-nul-fix/README.md b/plugins/windows-nul-fix/README.md new file mode 100644 index 000000000..9183f9ea4 --- /dev/null +++ b/plugins/windows-nul-fix/README.md @@ -0,0 +1,89 @@ +# Windows NUL Fix Plugin + +Automatically removes problematic `nul` files created by Claude Code on Windows. + +## The Problem + +When Claude Code runs bash commands on Windows (using Git Bash or similar Unix-like shells), it sometimes uses `2>nul` for stderr redirection. This is Windows CMD syntax, but in Unix shells, it creates a literal file named `nul` instead of redirecting to the null device. + +This causes several issues: +- The `nul` file cannot be easily deleted using Windows Explorer or PowerShell +- It breaks `git add` and `git commit` operations +- It can corrupt source control history +- It appears repeatedly after each bash command + +**Related Issue:** [#4928](https://github.com/anthropics/claude-code/issues/4928) + +## Solution + +This plugin adds a `PostToolUse` hook that automatically removes any `nul` files from the current working directory after each Bash tool execution on Windows. + +## Installation + +### From Marketplace +```bash +claude /plugin install windows-nul-fix +``` + +### Manual Installation +1. Copy the `windows-nul-fix` folder to `~/.claude/plugins/` +2. Restart Claude Code + +## How It Works + +``` +PostToolUse (Bash) → remove-nul.js → Delete 'nul' file if exists +``` + +The hook: +1. Only activates on Windows (`os.platform() === "win32"`) +2. Runs after every Bash tool execution +3. Checks if a `nul` file exists in the current working directory +4. Silently removes it if found +5. Does not interrupt the workflow on errors + +## Manual Workarounds + +If you prefer not to use this plugin, here are alternative solutions: + +### Option 1: Add to .gitignore +``` +nul +``` + +### Option 2: Delete with Git Bash +```bash +rm nul +``` + +### Option 3: Delete with PowerShell (elevated) +```powershell +Remove-Item "\\?\$PWD\nul" -Force +``` + +### Option 4: Add to CLAUDE.md +```markdown +Windows: use `2>/dev/null` not `2>nul` (causes bash to create literal file) +``` + +## Contents + +``` +windows-nul-fix/ +├── .claude-plugin/ +│ └── plugin.json # Plugin metadata +├── hooks/ +│ └── hooks.json # PostToolUse hook configuration +├── scripts/ +│ └── remove-nul.js # NUL file removal script +└── README.md # This file +``` + +## Requirements + +- Windows operating system +- Node.js (included with Claude Code) + +## License + +MIT - See the main Claude Code repository for license details. diff --git a/plugins/windows-nul-fix/hooks/hooks.json b/plugins/windows-nul-fix/hooks/hooks.json new file mode 100644 index 000000000..c70612e32 --- /dev/null +++ b/plugins/windows-nul-fix/hooks/hooks.json @@ -0,0 +1,17 @@ +{ + "description": "Removes 'nul' files created by Claude Code on Windows when bash commands use '2>nul' redirection", + "hooks": { + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/remove-nul.js", + "timeout": 5 + } + ] + } + ] + } +} diff --git a/plugins/windows-nul-fix/scripts/remove-nul.js b/plugins/windows-nul-fix/scripts/remove-nul.js new file mode 100644 index 000000000..90f9841bf --- /dev/null +++ b/plugins/windows-nul-fix/scripts/remove-nul.js @@ -0,0 +1,63 @@ +#!/usr/bin/env node +/** + * Windows NUL File Remover + * + * Workaround for https://github.com/anthropics/claude-code/issues/4928 + * + * On Windows, when Claude Code runs bash commands with '2>nul' redirection + * (intended for Windows CMD), it creates a literal file named 'nul' instead + * of redirecting to the null device. This is because Git Bash and other + * Unix-like shells on Windows treat 'nul' as a regular filename. + * + * This script removes any 'nul' files from the current working directory + * after Bash tool execution. + */ + +import { stdin } from "node:process"; +import { rmSync, existsSync } from "node:fs"; +import path from "node:path"; +import os from "node:os"; + +// Only run on Windows +if (os.platform() !== "win32") { + process.exit(0); +} + +let inputData = ""; + +stdin.setEncoding("utf8"); + +stdin.on("data", (chunk) => { + inputData += chunk; +}); + +stdin.on("end", () => { + try { + if (!inputData || !inputData.trim().startsWith("{")) { + // No valid JSON input, exit silently + process.exit(0); + } + + const event = JSON.parse(inputData); + const cwd = event.cwd || process.cwd(); + const nulPath = path.join(cwd, "nul"); + + // Check if nul file exists and remove it + if (existsSync(nulPath)) { + rmSync(nulPath, { force: true }); + + // Output message for transcript (optional - can be removed for silent operation) + // console.log(`Removed 'nul' file from ${cwd}`); + } + + process.exit(0); + } catch (error) { + // Exit silently on errors - don't interrupt the workflow + process.exit(0); + } +}); + +// Handle timeout gracefully +setTimeout(() => { + process.exit(0); +}, 4000);