[Windows] Stray/truncated files caused by a third-party hook (`cmd.exe /d /c …` mangled by Git Bash, cmd executes hook JSON) — not a Read/Edit or model shell bug
Update (2026-09-24): root cause found. The files were not created by a shell command of the model and not by Read/Edit themselves. They were created by a project-level hook installed by the third-party app Cate. Upstream report: https://github.com/0-AI-UG/cate/issues/705. The original report is kept below the line for reference. What is left for Claude Code is listed under "Remaining asks".
What happened
During a session on Windows, empty (0-byte) files appeared in the current working directory. Their names were taken from source text that followed a > character, for example:
SomeClass.SomeConstant(from a C# comparison likex > SomeClass.SomeConstant).fooand.foo.bar(from CSS child selectors like> .foo:last-child/> .foo.bar:last-child; the:last-childpart most likely went into an NTFS alternate data stream)
A > in front of an existing file name would have truncated that file.
Root cause
Cate writes these entries into <project>/.claude/settings.local.json for SessionStart, UserPromptSubmit, PermissionRequest, PreToolUse, PostToolUse (no matcher, so every tool), Stop, StopFailure and SessionEnd:
"command": "cmd.exe /d /c \"C:\\Users\\<user>\\.cate\\agent-hooks\\cate-hook-bridge-claude-code.cmd\""
On Windows the hook command string is run through Git Bash. MSYS path conversion rewrites the switches /d and /c to D:/ and C:/, so cmd.exe starts without /c, i.e. as an interactive shell reading commands from stdin, and stdin is the hook JSON. cmd.exe runs that JSON line as a command line (the .cmd bridge is never executed; in my case it did not even exist anymore).
cmd does not understand backslash-escaped quotes, so every \" from file content flips its quote state. When a > then falls outside a quoted region, cmd applies it as an output redirection. The same would apply to <, | and & (running arbitrary commands).
Evidence
Visible symptom: hook output shows a cmd banner followed by the prompt echoing the JSON:
Microsoft Windows [Version 10.0.26200.9457]
(c) Microsoft Corporation. All rights reserved.
D:\<project>>{"session_id":"…","hook_event_name":"UserPromptSubmit",…}
Argument mangling, measured from Git Bash:
printf 'echo CMDLINE=%%CMDCMDLINE%%\r\n' | cmd.exe /d /c "C:\path\to\any.cmd"
# Microsoft Windows [Version …]
# CMDLINE=C:\WINDOWS\system32\cmd.exe D:/ C:/ C:\path\to\any.cmd
Timeline: every file creation time matches a hook invocation whose JSON contained the text, not a shell command:
| File created | Hook invocation | Text source in the hook JSON |
|---|---|---|
| 08:40:05 | PostToolUse:Edit on a C# file | old_string / new_string |
| 09:11:28 | PostToolUse:Edit on the same C# file | tool response containing the original file content |
| 09:11:59 | PostToolUse:Edit on a CSS file | tool response containing the original file content |
| 09:12:00 | PreToolUse:Edit on the same CSS file | old_string / new_string |
Each file landed in the session's current working directory at that moment.
Reproduction (observed): with the hook still active, a Bash tool call whose command contained if (x > probe_test_123) created an empty file probe_test_123) in the project root. It was created by the PreToolUse hook, not by the command itself.
After removing the hooks block from .claude/settings.local.json, the cmd banner is gone. Uninstalling Cate alone did not remove the entries.
Remaining asks for Claude Code
- Model diagnosis: in the original session the model confidently blamed its own shell command and then deleted the files on its own. Both were wrong or risky: no shell command had run at those timestamps. When unexpected files appear, it would help if the model looked at hooks (and the transcript timing) before attributing them, and asked before deleting.
- Docs: please state clearly in the hooks documentation that on Windows hook commands run through Git Bash, including MSYS path conversion.
cmd.exe /c …(and any other/xswitch) does not work as written; use//c, forward-slash paths, or call the script directly. - Optional safeguard: a warning when a hook's stdout starts with a cmd.exe banner, or when a hook command matches
cmd(.exe)? /…on win32, would have made this visible right away. Currently it is reported as "hook success".
Environment
- Platform: win32 (Windows 11, 10.0.26200)
- Claude Code: 2.1.281
- Third-party: Cate (hook bridge in
~/.cate/agent-hooks/), upstream issue https://github.com/0-AI-UG/cate/issues/705 - Feedback ID: c10c785a-2af0-40ca-b7ee-8bbf81cf96ea
---
<details>
<summary>Original report (superseded diagnosis)</summary>
During a session, Claude Code accidentally created empty files in my project folder. It then explained that a > character from code text in a shell command had been interpreted as a shell redirect, and deleted the files on its own initiative.
This is a serious safety concern: an unquoted > doesn't just create files, it silently truncates any existing file with the same name. Had a real project file matched, its content would have been destroyed without warning. The follow-up cleanup (rm) depended on the model correctly judging which files were "accidental". A wrong judgment would delete legitimate files.
Expected behavior: code content passed to shell commands should always be safely quoted or written via file tools rather than echo/redirects, and the model should ask before deleting files it believes it created by mistake. A built-in safeguard (e.g. noclobber by default, or detection of unintended redirects) would be very welcome.
</details>
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗