How to stop Cursor from wiping your database

How to stop Cursor from wiping your database

One prompt. A minor backend bug. You look away, and the agent runs supabase db reset.

Local database gone. Seed data gone. The migration you were halfway through, gone.

That's not a bug in Cursor. That's what happens when you hand an agent a terminal and expect a text file to hold it back.

TL;DR — .cursorrules won't save you. Rules are suggestions, and a rule that says "ask first" is a rule that permits. Three hard stops instead: a rule with no escape hatch, terminal confirmation switched on in Cursor, and a shell-level block that refuses the command whatever the model decides. The real wall is database permissions.

Why .cursorrules doesn't hold

Here's the rule most people write:

Always ask for explicit confirmation before running any destructive database commands.

Read it the way a model reads it. You didn't forbid the command. You attached a condition to it.

A condition is a door.

So the agent writes the command into the terminal block and asks you about it in the same breath. You hit Accept on what you think is the question. It was both.

Then there's the incentive problem. The model is optimising for one thing: making the error go away. Messy schema, a migration that won't apply, and reset is the shortest path to green. Your seed data isn't in the objective function.

1. Take the condition out of the rule

If text is your first boundary, make it absolute. No "unless". No "ask first".

.cursorrules
# Database safety
- NEVER generate, write, or suggest `supabase db reset` or `supabase db drop`.
- No exceptions. Do not ask for confirmation. Refuse to write the command.

This still isn't a guarantee. It's a rule the model can't lawyer its way around, which is a different thing.

2. Turn auto-run off

Cursor's agent can run terminal commands on its own to test its own fixes. Convenient, right up until it isn't.

Settings → Features → Terminal (or under Composer, depending on your build). Require confirmation before running terminal commands. Leave it that way.

You'll click more. That's the trade.

3. Block it in the shell

The only rule an agent can't reinterpret is one it never gets to read. Put it in the shell.

Bash
supabase() {
  if [[ "$1" == "db" && "$2" == "reset" ]]; then
    echo "❌ BLOCKED: supabase db reset is disabled."
    return 1
  fi
  command supabase "$@"
}

source ~/.zshrc, and the command dies before it reaches Supabase — whatever the model wrote.

One caveat, because it matters. This only covers shells that read your ~/.zshrc. Cursor's integrated terminal usually does. A subprocess spawned by an agent, or anything invoking sh directly, may not. Mirror the guard in ~/.bashrc and treat it as a speed bump, not a wall.

The wall is in the database

Shell tricks stop the accident you can picture. Permissions stop the ones you can't.

The account your local environment connects with shouldn't be able to DROP. Production credentials shouldn't sit on your machine at all. Backups should run on a schedule, not on your memory.

Get that right and it stops mattering what the agent typed.

It's the same order of operations behind any AI automation worth shipping. Constraints first. Speed after. Skip the first part and you haven't automated anything — you've just moved the risk somewhere you can't see it.

Ready to let the agent run?

Zero trust isn't about distrusting the model. It's about not needing to trust it. Text tells an agent what you'd prefer. Permissions decide what's possible. Build for the second one and you can stop reading every terminal block with your finger hovering over Escape.

👉 Want the same guardrails around the automations you're running in production? Get in touch