Originally published at its-tahir.com.
After a Claude Code subagent ran rm -rf ~ from inside a test last week (issue #105342), I went through every place deleted files can still be and tested each one: the checkpoint copies in ~/.claude/file-history, Git (including staged-but-never-committed files), and your editor's local history. Then I checked which Claude Code protection actually stops which kind of delete, and wrote a guard hook that I tested against 18 commands. The short version: deny rules and hooks only read the command Claude types, so a delete hidden in a script gets past them. What's your setup, and has Claude Code ever deleted something of yours?
If Anthropic's Claude Code just deleted your files, press Esc to stop it, then run /rewind: it restores files Claude changed with its own edit tools. It can't bring back a file that was only removed by a shell command such as rm, so for those look in ~/.claude/file-history/, Git, your editor's local history and your backups, in that order. Then lock it down, because the usual advice (a deny rule for rm) misses most of the ways it happens.
| What was deleted | Where to look first |
|---|---|
| Files Claude had been editing |
/rewind, then Restore code
|
Files rm or a script removed |
~/.claude/file-history/ (only if Claude edited them earlier), then Git |
Committed files, or commits lost to git reset --hard
|
git restore, git reflog
|
Files you'd git add-ed but never committed |
git restore, or git fsck --lost-found once unstaged |
| Untracked files you'd opened in VS Code or JetBrains | The editor's Local History |
Anything else, including .git itself |
Backups, then the remote |
| A database | Your provider's point-in-time restore |
Last checked 30 October 2026, against the Claude Code docs and changelog up to 2.1.314. Every recovery command and the hook below were tested first on my own Windows 11 machine, in Git Bash with Git 2.54.
What to do right after Claude Code deletes files
Press Esc to interrupt Claude, or Ctrl+C to exit. Speed matters: in a September 2026 incident where Claude Code deleted 48,218 files, the whole delete took 103 seconds. Don't ask Claude to recreate the files yet, because that writes new files over the old ones. Then stop writing to the disk you lost files on until you've tried the steps below. Every new file can overwrite the blocks an undelete tool would need.
Shell deletes skip the bin. rm on macOS and Linux, rm in Git Bash and Remove-Item in PowerShell all delete permanently, so the Recycle Bin or Trash will usually be empty.
Can /rewind restore files Claude Code deleted?
Only files Claude Code changed with its own Edit, Write or NotebookEdit tools. A file removed only by a shell command such as rm was never checkpointed, so /rewind can't restore it.
Run /rewind, or press Esc twice with an empty prompt. Pick the prompt before the damage and choose Restore code (keeps the conversation) or Restore code and conversation.
It covers less than its name suggests. Per the checkpointing docs:
- Tracked: files Claude changed with its file tools (Edit, Write, NotebookEdit), and later shell changes to those same files.
-
Not tracked: a file that "only Bash commands changed". If Claude never edited it,
rmdeleting it leaves nothing to rewind. - Not restored: edits by a subagent running in the background, including background forked skills.
-
Skipped: symlinked and hard-linked files, with a
Restored the code, but skipped N fileswarning. -
Gone after about 30 days: the snapshots are deleted in Claude Code's retention sweep, and rewinding then fails with
No files were restored.
If the menu only offers Restore conversation, the checkpoint has no tracked file changes, which usually means the files went through the shell. Try the checkpoint folder next.
Why /rewind isn't working
- The menu offers only Restore conversation: no file Claude edited changed after that prompt, so there's nothing to restore. Shell-only deletes look like this.
-
No files were restored: the snapshots were removed by the retention sweep, about 30 days after the session last saved one. RaisecleanupPeriodDaysto keep them longer. - Some files came back, others didn't: a background subagent made the missing changes, or the files are symlinks or hard links, which rewind skips.
Find deleted files in Claude Code's checkpoint folder
The snapshots behind /rewind are ordinary files, and you can read them without the menu. That helps when the session is gone, or when Claude edited a file in an earlier prompt before deleting it.
They live in ~/.claude/file-history/<session-id>/. On my Windows machine on 30 October 2026, that folder held 40 sessions and 39 MB of snapshots. Each file is a full plain copy, named with a hash and a version, such as 1d9a83bb87b6a918@v2. I checked one against the post it came from, and apart from line endings it was identical.
To find a lost file, search for a line you remember from it:
grep -rl "a line you remember from the lost file" ~/.claude/file-history/
Then open each match and copy back the one you want. The highest @vN for a hash is its newest version. In PowerShell:
Get-ChildItem "$HOME\.claude\file-history" -Recurse -File | Select-String -List "a line you remember" | Select-Object Path
This only finds files Claude edited with its file tools at some point. A file it never touched was never copied here.
Recover deleted files with Git
What Git can give back depends on how far the file got. I tested each case in a scratch repository: created files in different states, ran rm -rf on all of them, then tried to get them back.
State of the file before rm -rf
|
Result | Command |
|---|---|---|
| Committed | Back, as of the last commit. Later edits are lost | git restore <path> |
Staged with git add, never committed |
Back, from the index | git restore <path> |
| Staged, then unstaged too | Back, as a dangling blob | git fsck --lost-found |
| Untracked, never added | Gone. Git never saw it | none |
git fsck --lost-found writes each orphaned file into .git/lost-found/other/, named by hash and without its file name, so you identify them by content.
If Claude ran git reset --hard and your commits seem gone, they aren't. In my test, after git reset --hard HEAD~2 the reflog still listed both "lost" commits, and this brought them back:
git reflog # find the line before "reset: moving to"
git reset --hard 'HEAD@{1}' # or the hash from that line
None of this works if .git itself was deleted, which is why Git couldn't help in the 48,000-file case. Then your only copy is the remote: clone it again, and you lose only what wasn't pushed.
Restore untracked files from your editor's local history
VS Code and JetBrains IDEs keep their own copies of files you saved in them, separate from Git and from Claude Code. That's the best chance for untracked files.
-
VS Code: open the Timeline view in the Explorer for a file that still exists, or run Local History: Find Entry to Restore from the Command Palette for one that doesn't. The copies are in
%APPDATA%\Code\User\Historyon Windows (I checked: one folder per file, with anentries.jsonnaming the original path),~/Library/Application Support/Code/User/Historyon macOS and~/.config/Code/User/Historyon Linux. - JetBrains IDEs: right-click the project folder and choose Local History, then Show History. Deleted files are listed there too.
Backups, and a deleted database
After that, it's your normal backups: Time Machine on macOS, File History or "Previous Versions" on Windows, or snapshots on Linux filesystems that have them. A file-recovery tool is the last resort, and it only works if you stopped writing to the disk.
For a database, Claude Code can't help: recovery is your provider's point-in-time restore or your own dumps. In the case Tom's Hardware reported in March 2026, the snapshots went with the database, which is why a backup has to live somewhere the same credentials can't delete.
Can Claude Code delete files outside your project?
Yes. Claude Code runs shell commands as your OS user, so a script or test it writes can delete anything you can, including your home folder. In each reported case below, the delete was inside a script, not a command anyone would have reviewed.
-
48,218 files lost in 103 seconds (TechRadar, September 2026). Asked to rebuild a mirror of a project, Claude Code wrote its own cleanup script. The script protected linked folders at the top level but not the folders nested under them. It deleted 55,550 files, restored 7,332 from an old copy (a net loss of 48,218), and took the
.gitfolder'sobjects,refsandlogswith it. -
rm -rf ~from a test (issue #105342, 27 October 2026, Claude Code 2.1.311). A subagent running withisolation: "worktree"wrote a test that passed the argument20; rm -rf ~to a shell fixture.cargo testran it with the real$HOME, and over about six minutes, before it was stopped, it deleted the project, its worktrees, other projects next to it and~/.config. Therm -rf ~never appeared in a tool call. -
kill(-1, SIGKILL)from a script (issue #104156). Not files, but the same mechanism: a placeholder PID of-1in a Node script killed every process the user owned, including every other Claude Code session.
Worktree isolation gives a subagent its own Git checkout. It doesn't limit which files a process can reach, which is what the reporter of #105342 assumed.
What each protection actually stops
A delete reaches your disk three ways: a command Claude types in a Bash call, a command inside a script or test it runs, or its own Write and Edit tools. Claude Code has six places you can stop one, and they catch different kinds. This is what each covers, from the docs and my own tests:
No single layer covers all three kinds of delete. The sandbox is the only one that sees inside scripts, and nothing protects the project folder from a script.
-
Deny and ask rules match the command text. The permissions docs say outright that
Bash(rm *)stopsrm -rf build/but not/bin/rm -rf build/orbash -c 'rm -rf build/', and that a Bash rule "isn't a security boundary around the program". -
The critical-path check is built in and always on. A recognised
rmorrmdirof/, your home folder, a drive, a mounted volume or the working directory itself prompts or is denied in every mode,bypassPermissionsincluded: no allow rule approves it. In the VS Code panel and the desktop app, where there's no terminal prompt, auto and bypass modes deny it. Like rules, it only sees the command text. -
A
PreToolUsehook reads the same text, but with your own logic, and it runs before your allow rules. Good for patterns rules can't express. Blind to scripts. -
permissions.blockWritesOutsideWorkingDirectories, added in 2.1.314, makes Write, Edit and NotebookEdit refuse paths outside your working directories in every mode. Shell commands aren't covered. -
The Bash sandbox is enforced by the operating system, so it holds for child processes: test runners, build scripts,
nodescripts. By default, sandboxed commands can write only to the working directory, a temp folder and directories you added. It doesn't cover Claude's file tools, and it doesn't run on native Windows. - A container, VM or separate OS user puts all of Claude Code behind one boundary. It's the only option that also covers the file tools, MCP servers and hooks.
How to stop Claude Code deleting files: sandbox, permissions and hooks
Turn on the Bash sandbox with allowUnsandboxedCommands: false, so shell commands and their child processes can only write inside the project. Set permissions.blockWritesOutsideWorkingDirectories (Claude Code 2.1.314+) for Claude's own file tools. Add ask rules for rm, git reset, git clean and force pushes. All of it goes in ~/.claude/settings.json, so it applies to every project:
{
"permissions": {
"ask": [
"Bash(rm *)",
"Bash(git reset *)",
"Bash(git clean *)",
"Bash(git push --force *)"
],
"deny": ["Read(./.env)", "Read(./.env.*)"],
"blockWritesOutsideWorkingDirectories": true
},
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false
}
}
What each line does:
-
askrules make Claude stop and show you the command.askrather thandenyso you can still approve a cleanup you asked for. An ask rule also prompts in auto mode, and beats a broader allow rule. -
Readdeny rules keep Claude's file tools away from your.envfiles, so production credentials aren't one read away. The docs note aReaddeny also blocks Edit and Write on the same path. -
blockWritesOutsideWorkingDirectoriesneeds Claude Code 2.1.314 or later. It's newer than the settings reference: as of 30 October 2026, the reference documents only its read-side partner,blockReadsOutsideWorkingDirectories, so checkclaude --versionfirst. -
sandbox.enabledturns the sandbox on. You can also run/sandboxin a session to try it first.allowUnsandboxedCommands: falseremoves the escape hatch where Claude retries a failed command outside the sandbox.
If you use auto mode, /permissions has a Recently blocked tab listing the calls it stopped. If you'd rather edit settings in a form than in JSON, my Claude Code settings builder writes the same keys.
A hook for what rules can't express
Rules can't say "recursive rm is fine on node_modules but nowhere else". A hook can. This one is a Node script, so it runs the same on Windows, macOS and Linux without jq. Save it as .claude/hooks/guard-destructive.mjs:
// PreToolUse hook: stop shell commands that delete or reset a lot at once.
// Exit 2 blocks the call and shows Claude the reason on stderr.
let raw = "";
for await (const chunk of process.stdin) raw += chunk;
const command = JSON.parse(raw).tool_input?.command ?? "";
// rm -rf on these folders stays allowed, so normal cleanup still works.
const SAFE = /^(\.\/)?(node_modules|dist|build|out|coverage|\.next|\.turbo|target)\/?$/;
function recursiveRmIsUnsafe(cmd) {
for (const part of cmd.split(/&&|\|\||[;|\n]/)) {
const words = part.trim().split(/\s+/);
const i = words.findIndex((w) => /(^|\/)rm$/.test(w));
if (i < 0) continue;
const args = words.slice(i + 1);
const recursive = args.some((a) => /^-[a-zA-Z]*[rR]/.test(a) || a === "--recursive");
const targets = args.filter((a) => !a.startsWith("-"));
if (recursive && !targets.every((t) => SAFE.test(t.replace(/^["']|["']$/g, "")))) return true;
}
return false;
}
const RULES = [
[/\b(bash|sh|zsh)\s+-c\b.*\brm\b/, "rm inside sh -c"],
[/\bgit\s+clean\s+-[a-zA-Z]*f/, "git clean -f"],
[/\bgit\s+reset\s+--hard\b/, "git reset --hard"],
[/\bgit\s+(checkout|restore)\s+(--\s+)?\.(\s|$)/, "discarding every uncommitted change"],
[/\bgit\s+push\b.*\s(--force|-f)\b/, "git push --force"],
[/\bfind\b.*\s-delete\b/, "find -delete"],
[/\bRemove-Item\b.*-Recurse/i, "Remove-Item -Recurse"],
[/\b(drop\s+(database|schema|table)|truncate\s+table)\b/i, "dropping database objects"],
[/\bterraform\s+destroy\b|\bkubectl\s+delete\b/, "destroying infrastructure"],
];
const reason = recursiveRmIsUnsafe(command)
? "recursive rm outside the allowed build folders"
: RULES.find(([re]) => re.test(command))?.[1];
if (reason) {
console.error(`Blocked by the guard-destructive hook (${reason}). Don't retry it another way: show the command to the user and ask them to run it.`);
process.exit(2);
}
Register it in the project's .claude/settings.json, next to the script, and commit both so everyone on the project gets it. Registering it in ~/.claude/settings.json instead would make it fail in every project that doesn't have the file:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|PowerShell",
"hooks": [
{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-destructive.mjs"]
}
]
}
]
}
}
I tested the script by piping it the same JSON Claude Code sends ({"tool_input":{"command":"..."}}) for 18 commands:
| Command | Result |
|---|---|
rm -rf node_modules dist, rm -rf ./build/, rm file.txt
|
Allowed |
rm -rf ~, rm -rf src, rm -fr ../other-project, /bin/rm -r data
|
Blocked |
npm test && rm -Rf "$HOME/.config" |
Blocked (each part of a chain is checked) |
bash -c 'rm -rf build' |
Blocked |
git clean -fdx, git reset --hard HEAD~3, git checkout -- ., git push --force origin main
|
Blocked |
find . -name "*.log" -delete, psql -c "DROP TABLE users"
|
Blocked |
git push origin main, npm run build
|
Allowed |
cargo test |
Allowed, and it would run any rm -rf ~ hidden in a test |
That last row is the gap: a hook only sees what Claude types. To build a hook like this without writing JSON by hand, try the Claude Code hooks builder; the hooks guide explains the events.
The rest of the checklist
-
Commit and push before long runs. It's the only protection for files inside the project, and the remote is the only copy a delete inside
.gitcan't reach. -
Don't run
--dangerously-skip-permissionson your own machine. It skips prompts, including for writes to.gitand.claude. The critical-pathrmprompt survives, butCLAUDE_CODE_EVEN_MORE_DANGEROUSLY_SKIP_PERMISSIONS=1(2.1.311+) turns even that off. The docs' advice is a container or VM "where Claude Code cannot damage your host system". -
Treat subagents as unsupervised. Their edits aren't in your checkpoints, and
isolation: "worktree"isn't a sandbox. My subagents guide covers scoping their tools. - Use plan mode for anything destructive. Ask for the plan, read the commands, then let it run. See the plan mode guide.
-
Have test fixtures set
HOMEto a temp folder, as a commenter on #105342 suggested, so a stray~in a test never reaches your real home. - Keep production credentials out of reach. If Claude can read them, a command it runs can use them. Give it a read-only database user, and keep admin credentials and the right to delete backups in a different account.
Does the Claude Code sandbox work on Windows?
No. Claude Code's Bash sandbox runs on macOS, Linux and WSL2; on native Windows, Anthropic's docs say "Claude Code runs commands unsandboxed". To get it, run Claude Code inside WSL2 or a dev container.
Native Windows still has the PowerShell tool's own guards. Remove-Item on the filesystem root, a drive root, top-level system folders or your home folder is denied in every mode without asking, and so is a bare * wildcard. Since 2.1.283, so are cmd /c rd, rmdir, del and erase on the same paths. A recursive Remove-Item of your working directory or one of its parents goes through the normal permission prompt.
The 48,000-file case involved Windows junction folders, so test any cleanup script that walks linked folders before it runs for real.
Recent fixes worth updating for
| Version | Fix |
|---|---|
| 2.1.281 |
rm -rf "$(pwd)" and other removals whose target is only command-substitution output now ask, even with a Bash allow rule |
| 2.1.287 | A dangerous rm no longer loses its always-ask safeguard when the same command redirects output to ~ or a wildcard path |
| 2.1.288 | A dangerous rm inside bash -c or sh -c no longer runs without a prompt in bypassPermissions mode or under a shell allow rule |
| 2.1.292 |
rm -rf on the 8.3 short name or another alternate Windows spelling of the home folder or a drive is now treated as removing it |
| 2.1.296 | Windows: rm -rf /c/Users/<name> in Git Bash now asks in bypass permissions mode |
| 2.1.304 |
rm -r of a mounted drive or a folder at its top level (/Volumes, /media, WSL's /mnt/c) now gets the safety prompt |
| 2.1.314 | New permissions.blockWritesOutsideWorkingDirectories for Write, Edit and NotebookEdit |
Key takeaways
- /rewind only restores files Claude changed with its own edit tools. A file only rm touched was never tracked, and background subagent edits aren't restored.
- Checkpoint snapshots are plain copies in ~/.claude/file-history//. grep -rl a line you remember to find a lost file, within about 30 days.
- Git recovers committed and staged files (git restore), unstaged blobs (git fsck --lost-found) and commits lost to git reset --hard (git reflog). Untracked files need your editor's Local History or a backup.
- Deny rules, the critical-path check and hooks all read the command Claude sends. A delete inside a script or test gets past them; only the sandbox, a container or a separate user limits it.
- Turn on the sandbox with allowUnsandboxedCommands false, set blockWritesOutsideWorkingDirectories (2.1.314+), add ask rules for rm and git reset, and push before long runs.
FAQ
If the delete came with an auto mode message instead, auto mode gave no verdict explains the classifier side, and the full list of Claude Code errors and fixes covers everything else. Setting up coding agents so they can work unattended without this kind of risk is part of the work I do: see recent projects.
FAQ
Can Claude Code delete files?
Yes. Claude Code runs shell commands as your user, so anything you can delete, a command it runs can delete, including files outside the project. Permission prompts, the built-in check for dangerous rm commands, deny rules and hooks all look at the command Claude sends. A delete inside a script or test that Claude writes and then runs gets past all of them. Only the Bash sandbox (macOS, Linux and WSL2) or running Claude Code in a container, VM or separate user account limits what that script can reach.
Can /rewind restore files Claude Code deleted?
Only some of them. /rewind restores files Claude changed with its own file tools (Edit, Write, NotebookEdit), plus later shell changes to those same files. A file that only a shell command such as rm touched was never tracked, so rewinding doesn't bring it back. Edits a background subagent made aren't restored either. For everything else, use Git, your editor's local history, or a backup.
Where does Claude Code keep checkpoint backups?
In ~/.claude/file-history//. Each file there is a plain copy of a file Claude edited, named with a hash and a version, such as 1d9a83bb87b6a918@v2. Search them for a line you remember from the lost file with grep -rl. Claude Code deletes them in its retention sweep, by default about 30 days after the session last saved one; cleanupPeriodDays changes that.
How do I stop Claude Code from deleting files?
Use several layers, because each one misses something. Turn on the Bash sandbox (/sandbox) so shell commands and their child processes can only write inside the project, and set allowUnsandboxedCommands to false. Set permissions.blockWritesOutsideWorkingDirectories to true (2.1.314+) for Claude's file tools. Add ask rules for rm, git reset, git clean and force pushes, and a PreToolUse hook for the patterns you care about. Commit and push often, because nothing on the list protects files inside the project from a script.
Does a deny rule like Bash(rm *) stop every delete?
No. A Bash rule matches the command text Claude writes. The docs list /bin/rm -rf build/ and bash -c 'rm -rf build/' as forms Bash(rm *) doesn't stop, and it can't see a delete inside a Python script, a test or a build tool. Use it as a speed bump, and use the sandbox when the limit has to hold.
Does the Claude Code sandbox work on Windows?
Not natively. The sandbox runs on macOS, Linux and WSL2; on native Windows, Claude Code runs commands unsandboxed. Run Claude Code inside WSL2 or a dev container to get it. On native Windows you still have the PowerShell tool's own guards, which deny Remove-Item on system paths, the home folder and bare wildcards in every mode.
What does permissions.blockWritesOutsideWorkingDirectories do?
Added in Claude Code 2.1.314, it makes the Write, Edit and NotebookEdit tools refuse paths outside your working directories in every permission mode, including bypassPermissions. It doesn't cover shell commands, so an rm outside the project still depends on the sandbox, your rules and the critical-path check. It's the write-side partner of permissions.blockReadsOutsideWorkingDirectories.
Is Claude Code safe to use?
Safe enough with the Bash sandbox on and your work committed and pushed. Claude Code runs commands as your user, so treat what it runs like a script you didn't review: prompts and rules check the command it types, not what a script or test does once it runs. In both code-loss cases covered here, the work that was lost hadn't been pushed to a remote.
Is --dangerously-skip-permissions safe?
Only in a container or VM you can afford to lose. It skips permission prompts, including for writes to .git and .claude. Recognised rm commands on critical paths such as / and your home folder still prompt in the terminal, and are denied in the VS Code panel and the desktop app, but CLAUDE_CODE_EVEN_MORE_DANGEROUSLY_SKIP_PERMISSIONS=1 removes even that, and nothing in this mode looks inside scripts.
Sources
- Checkpointing: what /rewind tracks and its limitations (Claude Code docs)
- Explore the .claude directory: file-history and the retention sweep (Claude Code docs)
- Configure permissions: deny rules and what a Bash rule doesn't match (Claude Code docs)
- Permission modes: protected paths, critical-path rm removals, Remove-Item in PowerShell (Claude Code docs)
- Configure the sandboxed Bash tool (Claude Code docs)
- Settings reference: permissions.blockReadsOutsideWorkingDirectories, sandbox settings (Claude Code docs)
- Hooks guide: block an action with exit code 2 (Claude Code docs)
- Claude Code changelog (2.1.281 to 2.1.314)
- Subagent-written test ran rm -rf ~ on the real home directory; worktree isolation didn't contain it (anthropics/claude-code #105342)
- Bash tool commands can signal every user process: kill(-1, SIGKILL) (anthropics/claude-code #104156)
- 48,000 files deleted in 103 seconds (TechRadar via Yahoo Tech, 25 September 2026)
- Claude Code deletes developer's production setup, including its database and snapshots (Tom's Hardware, March 2026)
Originally published at its-tahir.com.

Top comments (0)