🛡️ Claude Code · PreToolUse hook

Your AI agent shouldn't be able to rm -rf your life.

destructive-guard is a Claude Code hook that intercepts destructive shell commands — rm -rf, git reset --hard, docker prune, kubectl delete, terraform destroy, DROP TABLE — and turns them into a y/N confirmation. Even in bypassPermissions.

/plugin install destructive-guard@destructive-guard

Pure Python · zero deps · zero config · ~200 ms budget · macOS & Linux

The problem

Agents move faster than you can read.

You hand an AI coding agent the keys, walk away for coffee, and it decides the cleanest fix is git reset --hard — or DROP TABLE, or terraform destroy. In bypassPermissions mode there's nothing between the model and your data. destructive-guard puts a human back in the loop — but only for the commands that can actually hurt you.

Coverage

40+ destructive patterns across 10 categories.

Token-level parsing — so perform, transform and terraform plan never trip it. Sees through sudo, \rm, bash -c "…", here-strings and xargs.

› Files & disk

irreversible removal

rm -rfshreddd → /devmkfswipefs

› git

history you can't get back

reset --hardpush --forceclean -fbranch -Dfilter-branch

› Docker

containers & volumes

volume rmsystem prunecompose down -v

› Kubernetes & IaC

infra teardown

kubectl deleteterraform destroypulumi destroy

› Cloud

aws · gcloud · az

s3 rbterminate-instancesgcloud delete

› Databases

SQL, Mongo & Redis

DROP TABLETRUNCATEdropDatabase()FLUSHALL

How it works

One hook. Three moves.

Install

Add the plugin from the OntoShip marketplace — one command, no config, no daemon.

It registers

The hook binds to Claude Code's PreToolUse event and inspects every Bash command before it runs.

You confirm

Safe commands pass silently. Destructive ones raise a y/N + a banner — even in bypass mode. Reversible ones stay quiet there.

Rules reference

Every rule, in one place.

CRIT = irreversible / reaches outside your repo  ·  ORD = local & reversible. In bypassPermissions only CRIT asks; ORD passes silently.

Files & disk

CommandConditionSev
rm / rm -rfabs/system path, ~, *, .., or recursive on a non-regenerable dirCRIT
rm -rf ./build|dist|node_modulesrecursive only on regenerable dirsORD
rm <file> · rmdir · unlink · truncatesingle local targetORD
shred · srm · dropdb—CRIT
dd of=/dev/…target is a device / abs pathCRIT
dd of=<file>overwrite a file in cwdORD
mkfs · mkfs.* · wipefs · blkdiscard—CRIT

find

PatternConditionSev
find … -delete—CRIT
find … -exec rm|unlink|shred|srm|rmdiralso -execdir/-ok/-okdirCRIT

git (sees through -C, -c, --git-dir)

SubcommandConditionSev
git rm—ORD
git cleanwithout -fORD
git clean -f · reset --hard · push --force/-f--force-with-lease is allowedCRIT
git branch -D · filter-branch · stash clear · update-ref -d—CRIT

Docker / podman (sees through --context, -H)

PatternConditionSev
docker rm / rmi · (image|network|container) rm—ORD
docker volume rm · (…) prunevolume/image/system/network/container/builderCRIT
docker[-]compose down -vdown without -v is allowedCRIT

Kubernetes & IaC

PatternConditionSev
kubectl delete …after stripping -n/--namespace/--contextCRIT
terraform|tofu|terragrunt destroyalso apply -destroy, -chdir=CRIT
pulumi destroy—CRIT

Cloud

PatternConditionSev
aws … rb / delete-* / terminate-* / remove-*—CRIT
aws s3 rm --recursivesingle object = ORDCRIT
gcloud … delete · az … delete—CRIT

Databases (needs a db client: psql, mysql, mongosh, sqlite3…)

PatternConditionSev
SQL DROP TABLE|DATABASE|SCHEMA · TRUNCATE · DELETE FROM—CRIT
Mongo dropDatabase() · .drop() · deleteMany()—CRIT
redis-cli/valkey-cli FLUSHALL/FLUSHDB—CRIT

Misc & unwrapping

PatternNoteSev
crontab -rwipes the user's crontabCRIT
Unwraps sudo · \rm · bash -c "…" · <<< · xargsrecursive, depth ≤4CRIT
> redirects · compose down · push --force-with-lease · python x.pydeliberately not flagged (signal-to-noise)SKIP

FAQ

Questions before you install.

Does it slow me down?

No. Safe commands pass silently — you only see a prompt on genuinely destructive ones. Parsing runs under a ~200 ms budget and fails open: if anything hangs, the command proceeds rather than blocking your workflow (flip NDG_FAIL_CLOSED=1 to invert).

Won't it flag every rm and terraform?

No — it parses by token. terraform plan, transform, perform, rm ./build stay quiet. It tiers severity: reversible local deletes are ORD and pass silently in bypass mode; only irreversible CRIT commands raise a prompt.

Does it really work in bypassPermissions?

Yes — that's the point. An explicit ask from the hook survives bypass, so even a fully-autonomous agent gets stopped at a DROP TABLE. Reversible commands still pass silently there so you're not spammed.

Can I customize what it catches?

It's a single readable Python file, stdlib-only. Tune behaviour with env vars — NDG_NOTIFY, NDG_SOUND, NDG_TIMEOUT_MS, NDG_FAIL_CLOSED — or edit the rule sets directly. Dry-run any command with --test.

What about the macOS banner & sound?

On a detect you get a system sound + banner via osascript (always reliable). Want the banner click to focus your terminal instead of Script Editor? Opt in with brew install terminal-notifier + NDG_NOTIFIER=terminal-notifier.

Install

Put a y/N between your agent and a very bad day.

Two lines in Claude Code. No account, no service, no telemetry.

/plugin marketplace add vakovalskii/destructive-guard
/plugin install destructive-guard@destructive-guard