Ran an irreversible command without asking
没问一声就跑了不可逆命令
The agent force-pushes, runs `rm -rf`, `git reset --hard`, or drops a table to get unstuck, and work or data is gone.
Applies to: Claude Code · Cursor · Codex · Copilot agent
Symptom
Uncommitted work vanishes, a remote branch is rewritten, a dev or production database is emptied.
Why it happens
A clean slate is a fast way past a confusing state, and the agent doesn't weigh reversibility unless forced to. agent-failure-modes lists AFM-15 Destructive Overreach; developertoolkit.ai puts unsafe agent actions under security, controlled in the session (permissions, sandbox).
Example
Illustrative.
bash# Agent, after a merge conflict:
git reset --hard origin/main && git push --force
Copy-paste fixes
Pick the files for the agents you use. Snippets are in English so they work in any repo.
Claude Code: add to CLAUDE.md
CLAUDE.md## Irreversible actions need a human
- Never run destructive or irreversible commands without explicit approval in this conversation:
`rm -rf`, `git push --force`, `git reset --hard`, `git clean -f`, history rewrites, `DROP`/`TRUNCATE`,
`DELETE` without `WHERE`, infrastructure destroy, or anything touching production.
- When stuck, stop and describe the state and options instead of wiping it.
- Before risky local operations, make a backup branch (`git branch backup/<date>`).
Codex, Copilot coding agent and others: add to AGENTS.md
AGENTS.md## Irreversible actions need a human
- Never run destructive or irreversible commands without explicit approval in this conversation:
`rm -rf`, `git push --force`, `git reset --hard`, `git clean -f`, history rewrites, `DROP`/`TRUNCATE`,
`DELETE` without `WHERE`, infrastructure destroy, or anything touching production.
- When stuck, stop and describe the state and options instead of wiping it.
- Before risky local operations, make a backup branch (`git branch backup/<date>`).
Cursor: project rule
.cursor/rules/destructive-command.mdc---
description: "AgentFails: Ran an irreversible command without asking"
alwaysApply: true
---
## Irreversible actions need a human
- Never run destructive or irreversible commands without explicit approval in this conversation:
`rm -rf`, `git push --force`, `git reset --hard`, `git clean -f`, history rewrites, `DROP`/`TRUNCATE`,
`DELETE` without `WHERE`, infrastructure destroy, or anything touching production.
- When stuck, stop and describe the state and options instead of wiping it.
- Before risky local operations, make a backup branch (`git branch backup/<date>`).
Claude Code PreToolUse hook: stop destructive shell commands
Register with matcher "Bash". A pattern list is a seatbelt, not a sandbox; keep real backups and least-privilege credentials too.
.claude/hooks/block_destructive.py#!/usr/bin/env python3
"""Block high-blast-radius shell commands; the agent must ask the user instead."""
import json, re, sys
call = json.load(sys.stdin)
if call.get("tool_name") != "Bash":
sys.exit(0)
cmd = call["tool_input"].get("command", "")
dangerous = [
r"\brm\s+-[a-zA-Z]*r[a-zA-Z]*f|\brm\s+-[a-zA-Z]*f[a-zA-Z]*r", # rm -rf / rm -fr
r"\bgit\s+push\b.*(--force\b|-f\b|--force-with-lease)",
r"\bgit\s+reset\s+--hard\b",
r"\bgit\s+clean\s+-[a-zA-Z]*f",
r"\bgit\s+checkout\s+--\s+\.",
r"\b(drop|truncate)\s+(table|database|schema)\b",
r"\bdelete\s+from\s+\w+\s*;?\s*$", # DELETE without WHERE
r"\b(terraform|tofu)\s+destroy\b",
r"\bkubectl\s+delete\b",
]
for pat in dangerous:
if re.search(pat, cmd, re.IGNORECASE):
print(f"BLOCKED destructive command: {cmd!r}\n"
"Explain what you want to run and why, and let the user run it or approve it.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
Claude Code permissions: hard-deny sensitive paths, ask before installs
Rules in CLAUDE.md are context; permission rules are enforced by the tool. Adjust the paths to your repo.
.claude/settings.json{
"permissions": {
"deny": [
"Edit(./migrations/**)",
"Edit(./.env*)",
"Edit(./package-lock.json)",
"Edit(./pnpm-lock.yaml)",
"Edit(./src/generated/**)"
],
"ask": [
"Bash(npm install:*)",
"Bash(pnpm add:*)",
"Bash(yarn add:*)",
"Bash(pip install:*)",
"Bash(git push:*)"
]
}
}