AI coding agents can run shell commands. Sometimes they run rm -rf by mistake. This deletes files forever. While of course I always read and approve all tool calls manually, by hand, and never let my agents work except under direct supervision 100% of the time, sometimes I miss things.
- Hooks catch
rm -rfbefore it runs - Rewrite to
trashso files go to Trash instead - Lock the hook files so agents cannot delete them
Add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$HOME/.claude/hooks/deny-rm-rf.py"
}
]
}
]
}
}Create ~/.claude/hooks/deny-rm-rf.py:
#!/usr/bin/env python3
import json
import re
import sys
RM_RF_PATTERN = re.compile(
r"\brm\s+-[a-z]*r[a-z]*f[a-z]*\s+|\brm\s+-[a-z]*f[a-z]*r[a-z]*\s+", re.IGNORECASE
)
def main() -> int:
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as exc:
print(f"Error: Invalid JSON input: {exc}", file=sys.stderr)
return 1
if input_data.get("tool_name") != "Bash":
return 0
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")
if RM_RF_PATTERN.search(command):
new_command = RM_RF_PATTERN.sub("trash ", command)
print(json.dumps({"updatedInput": {"command": new_command}}))
return 0
if __name__ == "__main__":
sys.exit(main())Create ~/.config/opencode/plugin/deny-rm-rf.js:
const RM_RF_PATTERN = /\brm\s+-[a-z]*r[a-z]*f[a-z]*\s+|\brm\s+-[a-z]*f[a-z]*r[a-z]*\s+/i
export const RewriteRmRf = async () => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool !== "bash") return
const command = output?.args?.command ?? ""
if (RM_RF_PATTERN.test(command)) {
output.args.command = command.replaceAll(RM_RF_PATTERN, "trash ")
}
}
}
}An agent might try to delete the hooks. Make them immutable with macOS's chflags schg:
sudo chflags schg ~/.claude/hooks/deny-rm-rf.py
sudo chflags schg ~/.config/opencode/plugin/deny-rm-rf.jsOnly root can remove the immutable flag. Agents cannot bypass this.
- macOS
- trash:
brew install trash - sudo access to lock files
- macOS only
- Only catches
rm -rf
When an agent runs rm -rf /path, the hook changes it to trash /path. Files go to Trash. You can get them back.
See the full code at nateberkopec/dotfiles.