<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: labyrinthlab</title>
    <description>The latest articles on DEV Community by labyrinthlab (@labyrinthlab).</description>
    <link>https://dev.to/labyrinthlab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4171171%2F42836266-d88b-4292-98ec-5b56498d0955.png</url>
      <title>DEV Community: labyrinthlab</title>
      <link>https://dev.to/labyrinthlab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/labyrinthlab"/>
    <language>en</language>
    <item>
      <title>Stop Cursor from running drizzle-kit push --force on a real database</title>
      <dc:creator>labyrinthlab</dc:creator>
      <pubDate>Thu, 08 Oct 2026 18:02:52 +0000</pubDate>
      <link>https://dev.to/labyrinthlab/stop-cursor-from-running-drizzle-kit-push-force-on-a-real-database-2mg7</link>
      <guid>https://dev.to/labyrinthlab/stop-cursor-from-running-drizzle-kit-push-force-on-a-real-database-2mg7</guid>
      <description>&lt;p&gt;Here is the failure mode.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You change a Drizzle schema and ask the Cursor agent to sync the database.&lt;/li&gt;
&lt;li&gt;It runs &lt;code&gt;npx drizzle-kit push&lt;/code&gt;. The command stops on a prompt, or errors because there is no terminal to prompt in.&lt;/li&gt;
&lt;li&gt;The agent retries with &lt;code&gt;npx drizzle-kit push --force&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It finishes. A table you cared about is now empty, or a renamed column lost its data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This post covers what &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;--force&lt;/code&gt; actually do (checked against the source of drizzle-kit 0.31.11, the current stable release), why agents end up adding the flag, and guards you can add today, including a free Cursor rule and a hook that blocks the command.&lt;/p&gt;

&lt;h2&gt;
  
  
  What push does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;drizzle-kit push&lt;/code&gt; reads your schema, introspects the live database, diffs the two and applies the SQL directly. No migration file is written. The docs recommend it for rapid prototyping, and against a local throwaway database it's fine.&lt;/p&gt;

&lt;p&gt;Before applying anything, push looks for statements that lose data. On Postgres it counts rows and flags these when existing data is affected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dropping a table, column, schema or materialized view&lt;/li&gt;
&lt;li&gt;changing a column's type&lt;/li&gt;
&lt;li&gt;adding a &lt;code&gt;NOT NULL&lt;/code&gt; column with no default&lt;/li&gt;
&lt;li&gt;dropping a primary key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the type change and the &lt;code&gt;NOT NULL&lt;/code&gt; column, push doesn't only warn. It adds &lt;code&gt;truncate table "&amp;lt;name&amp;gt;" cascade;&lt;/code&gt; to the statements it will run. &lt;code&gt;cascade&lt;/code&gt; also empties every table with a foreign key pointing at that one.&lt;/p&gt;

&lt;p&gt;When anything is flagged, push prints "Found data-loss statements", then "THIS ACTION WILL CAUSE DATA LOSS AND CANNOT BE REVERTED", and asks "Do you still want to push changes?" with "No, abort" as the first option.&lt;/p&gt;

&lt;p&gt;That prompt is the safety net. &lt;code&gt;--force&lt;/code&gt; removes it. The CLI's own description: "Auto-approve all data loss statements. Note: Data loss statements may truncate your tables and data."&lt;/p&gt;

&lt;h2&gt;
  
  
  Renames turn into drops
&lt;/h2&gt;

&lt;p&gt;push can't tell a rename from a drop plus an add. When one column disappears and another appears in the same table, it asks: "Is &lt;code&gt;display_name&lt;/code&gt; column in &lt;code&gt;users&lt;/code&gt; table created or renamed from another column?" The first option is "created". Pick it and the old column, with its data, gets dropped.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--force&lt;/code&gt; doesn't answer that question. The rename prompt runs first. But once "created" is picked, &lt;code&gt;--force&lt;/code&gt; approves the resulting drop with no second look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents reach for --force
&lt;/h2&gt;

&lt;p&gt;These prompts are arrow-key menus. An agent running shell commands usually can't drive them. Since drizzle-kit 0.31.10, the prompt library refuses to run without a TTY and throws: "Interactive prompts require a TTY terminal (...). This can happen when running in CI, piped input, or non-interactive shells."&lt;/p&gt;

&lt;p&gt;So plain &lt;code&gt;push&lt;/code&gt; fails, and that failure is safe: nothing was applied. To an agent whose job is to make the command succeed, the obvious fix is the flag that skips the prompt. &lt;code&gt;--force&lt;/code&gt; skips exactly the data-loss prompt, the one that mattered.&lt;/p&gt;

&lt;p&gt;Two details make it worse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--force&lt;/code&gt; also overrides &lt;code&gt;strict&lt;/code&gt;. In the source, the strict prompt only runs &lt;code&gt;if (!force &amp;amp;&amp;amp; strict)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--verbose&lt;/code&gt; prints the SQL first, but with &lt;code&gt;--force&lt;/code&gt; there's no pause between printing and running.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What drizzle-kit already gives you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The data-loss prompt&lt;/strong&gt;, as long as nobody passes &lt;code&gt;--force&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--strict&lt;/code&gt;&lt;/strong&gt;, or &lt;code&gt;strict: true&lt;/code&gt; in &lt;code&gt;drizzle.config.ts&lt;/code&gt;: asks before running any push, even one with no data loss. It's in the 0.31.x CLI and config type, though the current config docs page doesn't list it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--verbose&lt;/code&gt;&lt;/strong&gt;, or &lt;code&gt;verbose: true&lt;/code&gt;: prints every statement before executing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--explain&lt;/code&gt;&lt;/strong&gt;: prints the planned SQL without applying it. It's on the current push docs page and in the 1.0 release candidate, but not in 0.31.11, so check your version before relying on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;generate&lt;/code&gt; + &lt;code&gt;migrate&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;drizzle-kit generate&lt;/code&gt; writes SQL to your migrations folder, you read it, and &lt;code&gt;drizzle-kit migrate&lt;/code&gt; applies it and records it in the &lt;code&gt;__drizzle_migrations&lt;/code&gt; table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For anything shared (staging, prod, a teammate's database), use &lt;code&gt;generate&lt;/code&gt; + &lt;code&gt;migrate&lt;/code&gt;. A destructive statement in a committed &lt;code&gt;.sql&lt;/code&gt; file shows up in review. The same statement inside a forced push only shows up in terminal scrollback.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;generate&lt;/code&gt; asks the same rename question, so the agent can hit the same TTY error there. That's fine. The right move is to hand that one prompt to you.&lt;/p&gt;

&lt;p&gt;The 1.0 release candidate (rc.4) changes this for agents: in non-interactive mode, &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;generate&lt;/code&gt; stop prompting and report unresolved renames and data-loss confirmations as &lt;code&gt;missing_hints&lt;/code&gt;, which the caller has to resolve explicitly with &lt;code&gt;--hints&lt;/code&gt;. Until that ships as stable, plan for 0.31.x behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard 1: a rule that makes the agent stop and ask
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;.cursor/rules/drizzle-push-safety.mdc&lt;/code&gt;. It's free, written for this post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Drizzle&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;push&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;safety.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Stop&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ask&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;before&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;any&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;drizzle-kit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;that&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;could&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lose&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data."&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Drizzle push safety&lt;/span&gt;

&lt;span class="gu"&gt;## Never run without asking first&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`drizzle-kit push --force`&lt;/span&gt;, including through an npm script
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`drizzle-kit push`&lt;/span&gt; against anything that is not a local throwaway database
&lt;span class="p"&gt;-&lt;/span&gt; Answering a drizzle-kit rename or data-loss prompt on the user's behalf

&lt;span class="gu"&gt;## Before any drizzle-kit command&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Open &lt;span class="sb"&gt;`drizzle.config.*`&lt;/span&gt; and find &lt;span class="sb"&gt;`dbCredentials`&lt;/span&gt;. If it reads an env var,
   name the variable and the file that sets it. Print host and database name,
   never the password.
&lt;span class="p"&gt;2.&lt;/span&gt; If the host is not localhost or a known dev database, stop and ask.

&lt;span class="gu"&gt;## If push stops on a prompt or a TTY error&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Do not add &lt;span class="sb"&gt;`--force`&lt;/span&gt;. Do not pipe input into the prompt.
&lt;span class="p"&gt;-&lt;/span&gt; Quote the prompt to the user: rename question, data-loss list,
  or unique-constraint truncate question.
&lt;span class="p"&gt;-&lt;/span&gt; Offer the tracked path:
&lt;span class="p"&gt;  1.&lt;/span&gt; &lt;span class="sb"&gt;`drizzle-kit generate`&lt;/span&gt;
&lt;span class="p"&gt;  2.&lt;/span&gt; Show the new .sql file. Point out DROP, TRUNCATE,
     ALTER COLUMN ... TYPE and ADD COLUMN ... NOT NULL without DEFAULT.
&lt;span class="p"&gt;  3.&lt;/span&gt; Wait for approval, then &lt;span class="sb"&gt;`drizzle-kit migrate`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; If &lt;span class="sb"&gt;`generate`&lt;/span&gt; asks the rename question, ask the user to run it
  in their own terminal.

&lt;span class="gu"&gt;## Renames&lt;/span&gt;
If a column or table was renamed in the schema, say so before running
anything. Answering "created" to drizzle-kit's rename question drops
the old column and its data.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;alwaysApply: true&lt;/code&gt; matters here. The agent decides to run push from a shell, often with no Drizzle file in context, so a glob-scoped rule might not be loaded at that moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard 2: a hook that blocks the command
&lt;/h2&gt;

&lt;p&gt;A rule is text in the prompt, and a model can still talk itself past it. A Cursor hook runs outside the model. Project hooks live in &lt;code&gt;.cursor/hooks.json&lt;/code&gt;, and a &lt;code&gt;beforeShellExecution&lt;/code&gt; hook runs before each shell command the agent wants to execute. Its &lt;code&gt;matcher&lt;/code&gt; is a regex tested against the full command string, so the script only runs on matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"beforeShellExecution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".cursor/hooks/block-drizzle-force.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"drizzle-kit push.*--force"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"failClosed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .cursor/hooks/block-drizzle-force.sh&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="sh"&gt;'
{
  "permission": "deny",
  "user_message": "Blocked drizzle-kit push --force. Run it in your own terminal if you mean it.",
  "agent_message": "drizzle-kit push --force is blocked in this repo. Do not retry with other flags. Run drizzle-kit generate, show the SQL, and wait for approval."
}
&lt;/span&gt;&lt;span class="no"&gt;JSON
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;chmod +x .cursor/hooks/block-drizzle-force.sh&lt;/code&gt;. Details from the hooks docs worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exit code 2 also blocks, the same as returning &lt;code&gt;"permission": "deny"&lt;/code&gt;. A script that is just &lt;code&gt;cat &amp;gt; /dev/null; exit 2&lt;/code&gt; works if you don't need the messages.&lt;/li&gt;
&lt;li&gt;Other non-zero exits, crashes and timeouts fail open by default. &lt;code&gt;failClosed: true&lt;/code&gt; makes them block instead.&lt;/li&gt;
&lt;li&gt;Project hooks only run in a trusted workspace.&lt;/li&gt;
&lt;li&gt;The matcher only sees the literal command. If push hides behind &lt;code&gt;npm run db:push -- --force&lt;/code&gt;, add &lt;code&gt;|db:push.*--force&lt;/code&gt; to the regex.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you do want a forced push against your local database, run it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard 0: credentials
&lt;/h2&gt;

&lt;p&gt;The guard nothing can argue with is still the connection string. If &lt;code&gt;dbCredentials.url&lt;/code&gt; reads &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; and the &lt;code&gt;.env&lt;/code&gt; your editor loads points at a shared database, the guards above are all that stands between the agent and that data. I covered that, and the Prisma version of this problem, in &lt;a href="https://dev.to/labyrinthlab/stop-cursor-from-running-prisma-migrate-reset-on-the-wrong-database-3pch"&gt;Stop Cursor from running prisma migrate reset on the wrong database&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Written with AI assistance and checked against the Drizzle and Cursor docs.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>typescript</category>
      <category>drizzle</category>
      <category>database</category>
    </item>
    <item>
      <title>Stop Cursor from running prisma migrate reset on the wrong database</title>
      <dc:creator>labyrinthlab</dc:creator>
      <pubDate>Thu, 08 Oct 2026 13:15:03 +0000</pubDate>
      <link>https://dev.to/labyrinthlab/stop-cursor-from-running-prisma-migrate-reset-on-the-wrong-database-3pch</link>
      <guid>https://dev.to/labyrinthlab/stop-cursor-from-running-prisma-migrate-reset-on-the-wrong-database-3pch</guid>
      <description>&lt;p&gt;Here is the failure mode, step by step.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your migration history drifts. Someone edited an applied migration, or you ran &lt;code&gt;db push&lt;/code&gt; to try something.&lt;/li&gt;
&lt;li&gt;You ask the Cursor agent to fix it. It runs &lt;code&gt;prisma migrate dev&lt;/code&gt;, which reports drift and says the database needs a reset.&lt;/li&gt;
&lt;li&gt;The agent can't answer an interactive prompt, so it runs &lt;code&gt;prisma migrate reset --force&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;.env&lt;/code&gt; still has the staging or production &lt;code&gt;DATABASE_URL&lt;/code&gt; you pasted in last week to debug something.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;migrate reset&lt;/code&gt; drops the database (or schema), recreates it and applies every migration. On Prisma 6 and earlier it also runs your seed. On dev that's a fine way to get unstuck. Anywhere else it's a restore-from-backup afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents walk into this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The error message suggests it.&lt;/strong&gt; Prisma says a reset is needed. An agent trying to make the command succeed takes that literally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents can't see intent, only the connection string.&lt;/strong&gt; Nothing in &lt;code&gt;postgresql://app@db.internal:5432/app&lt;/code&gt; says "this one has customers in it."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-interactive shells push toward &lt;code&gt;--force&lt;/code&gt;.&lt;/strong&gt; The confirmation prompt that would have saved you is the first thing that gets skipped.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Prisma already does
&lt;/h2&gt;

&lt;p&gt;Since Prisma ORM 6.15.0, the CLI looks for environment variables that coding agents set (for Cursor, &lt;code&gt;CURSOR_AGENT&lt;/code&gt;). When it finds one, &lt;code&gt;prisma migrate reset&lt;/code&gt; stops with an error telling the agent to explain what it was about to do and ask you first. The agent can only rerun it with &lt;code&gt;PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION&lt;/code&gt; set to the exact text of your consent message. The same check covers &lt;code&gt;prisma db push --force-reset&lt;/code&gt;, and since 7.9.0, &lt;code&gt;prisma db push --accept-data-loss&lt;/code&gt;. That is a good backstop. It still leaves gaps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The agent asks "ok to reset?" and you say yes without checking which database the URL points at.&lt;/li&gt;
&lt;li&gt;It doesn't cover raw SQL, &lt;code&gt;db execute&lt;/code&gt;, or Drizzle.&lt;/li&gt;
&lt;li&gt;It doesn't make the agent read the generated SQL before &lt;code&gt;migrate deploy&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You want the agent checking the target before it reaches that prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: keep production credentials out of reach
&lt;/h2&gt;

&lt;p&gt;The cheapest fix isn't a rule. Don't keep prod or staging URLs in the &lt;code&gt;.env&lt;/code&gt; your editor loads, and give your day-to-day role no &lt;code&gt;DROP&lt;/code&gt; rights on shared databases. Rules steer the agent. Missing credentials can't be talked around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: a project rule
&lt;/h2&gt;

&lt;p&gt;Cursor loads project rules from &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;. Each file is markdown with a small frontmatter block:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: what the rule is for.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;globs&lt;/code&gt;: file patterns that pull the rule in.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alwaysApply&lt;/code&gt;: &lt;code&gt;true&lt;/code&gt; attaches it to every agent request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .cursor/rules
&lt;span class="c"&gt;# save the rule below as .cursor/rules/migration-checklist.mdc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open Customize in the sidebar, go to Rules, and confirm it's listed. Test with "migrations are out of sync, reset the db." A working setup asks about the target database instead of running a command.&lt;/p&gt;

&lt;p&gt;Here is the full rule. Copy it as is or trim what you don't need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pre-push&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pre-migrate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;checklist&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;schema&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changes"&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/migrations/**"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/prisma/migrations/**"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/drizzle/**"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Migration Checklist&lt;/span&gt;

&lt;span class="gu"&gt;## PRE-MIGRATION CHECKLIST&lt;/span&gt;

Before running any migration command, verify:

&lt;span class="gu"&gt;### 1. Environment Check&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Which database am I targeting?**&lt;/span&gt; (dev/staging/prod)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Is this the correct connection string?**&lt;/span&gt; Check &lt;span class="sb"&gt;`DATABASE_URL`&lt;/span&gt; or config
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Do I have a backup?**&lt;/span&gt; (Required for staging/prod)

&lt;span class="gu"&gt;### 2. Schema Review&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Did I read the generated SQL?**&lt;/span&gt; (Not just the schema diff)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Are there destructive operations?**&lt;/span&gt; (DROP, TRUNCATE, DELETE)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Will existing data survive?**&lt;/span&gt; (Type changes, NOT NULL additions)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Are indexes appropriate?**&lt;/span&gt; (Not missing, not excessive)

&lt;span class="gu"&gt;### 3. Data Considerations&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Will this migration lock tables?**&lt;/span&gt; (Large tables = long locks)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Is there a data migration needed?**&lt;/span&gt; (Backfill, transform)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**What's the rollback plan?**&lt;/span&gt; (Reverse migration or restore)

&lt;span class="gu"&gt;## PRE-PUSH CHECKLIST&lt;/span&gt;

Before pushing migration files to the repository:

&lt;span class="gu"&gt;### 1. File Hygiene&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Migration file is committed**&lt;/span&gt; (Not in .gitignore)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Migration name is descriptive**&lt;/span&gt; (Not "migration_1" or "fix")
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**No sensitive data in migration**&lt;/span&gt; (No hardcoded credentials)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**SQL is reviewed and correct**&lt;/span&gt; (Read the actual file)

&lt;span class="gu"&gt;### 2. Local Verification&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Migration applies cleanly locally**&lt;/span&gt; (Tested on fresh DB)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**App still works after migration**&lt;/span&gt; (Run tests)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Migration is idempotent or guarded**&lt;/span&gt; (Won't fail if run twice)

&lt;span class="gu"&gt;### 3. Team Coordination&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**No conflicting migrations from teammates**&lt;/span&gt; (Pull latest first)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Migration order is correct**&lt;/span&gt; (Timestamp/sequence is right)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Breaking changes documented**&lt;/span&gt; (If API changes needed)

&lt;span class="gu"&gt;## PRODUCTION DEPLOYMENT CHECKLIST&lt;/span&gt;

Before deploying migrations to production:

&lt;span class="gu"&gt;### 1. Pre-Deploy&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Database backup completed**&lt;/span&gt; (Verified, not just scheduled)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Maintenance window scheduled**&lt;/span&gt; (If needed for locks)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Rollback plan documented**&lt;/span&gt; (Restore steps or reverse migration)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Team notified**&lt;/span&gt; (On-call aware of deployment)

&lt;span class="gu"&gt;### 2. During Deploy&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Monitor migration progress**&lt;/span&gt; (Watch for locks, errors)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Check application health**&lt;/span&gt; (Errors, latency spikes)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Have rollback ready**&lt;/span&gt; (Don't walk away mid-migration)

&lt;span class="gu"&gt;### 3. Post-Deploy&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Verify migration status**&lt;/span&gt; (&lt;span class="sb"&gt;`migrate status`&lt;/span&gt; shows clean)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Test affected features**&lt;/span&gt; (Not just "app starts")
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="gs"&gt;**Monitor for delayed issues**&lt;/span&gt; (Slow queries, missing data)

&lt;span class="gu"&gt;## DESTRUCTIVE OPERATION ESCALATION&lt;/span&gt;

When a migration contains destructive SQL:

&lt;span class="gu"&gt;### Level 1: Column Drop&lt;/span&gt;
&lt;span class="gs"&gt;**Required:**&lt;/span&gt; Read SQL twice, confirm column name, verify no app references.
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;Migration contains: ALTER TABLE "users" DROP COLUMN "legacy_field"
Confirm: "I verified no code references legacy_field. Drop it."
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;### Level 2: Table Drop&lt;/span&gt;
&lt;span class="gs"&gt;**Required:**&lt;/span&gt; Level 1 + verify no foreign keys, confirm data is backed up or worthless.
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;Migration contains: DROP TABLE "temp_imports"
Confirm: "I verified temp_imports has no important data and no references. Drop it."
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;### Level 3: Data Deletion&lt;/span&gt;
&lt;span class="gs"&gt;**Required:**&lt;/span&gt; Level 2 + explicit row count awareness, backup verification.
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;Migration contains: DELETE FROM "audit_logs" WHERE created_at &amp;lt; '2023-01-01'
Confirm: "I understand this deletes ~50,000 rows. Backup verified. Proceed."
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;### Level 4: Full Reset&lt;/span&gt;
&lt;span class="gs"&gt;**Required:**&lt;/span&gt; All above + explicit database name confirmation.
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;Command: prisma migrate reset / drizzle-kit push --force
Confirm: Type the database name to confirm: ___________
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;## COMMON MISTAKES TO CATCH&lt;/span&gt;

&lt;span class="gu"&gt;### Prisma-Specific&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`migrate dev`&lt;/span&gt; on non-dev database
&lt;span class="p"&gt;-&lt;/span&gt; Editing migration SQL after it's been applied elsewhere
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`@@map`&lt;/span&gt; or &lt;span class="sb"&gt;`@map`&lt;/span&gt; changes that rename without data migration
&lt;span class="p"&gt;-&lt;/span&gt; Enum changes that remove values still in use

&lt;span class="gu"&gt;### Drizzle-Specific&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`push`&lt;/span&gt; instead of &lt;span class="sb"&gt;`generate`&lt;/span&gt; + &lt;span class="sb"&gt;`migrate`&lt;/span&gt; on persistent DB
&lt;span class="p"&gt;-&lt;/span&gt; Missing &lt;span class="sb"&gt;`NOT NULL`&lt;/span&gt; default for existing rows
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`drizzle-kit drop`&lt;/span&gt; on migration that's already deployed elsewhere
&lt;span class="p"&gt;-&lt;/span&gt; Introspect overwriting intentional schema divergence

&lt;span class="gu"&gt;### Universal&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Changing column type without considering data truncation
&lt;span class="p"&gt;-&lt;/span&gt; Adding unique constraint to column with duplicate data
&lt;span class="p"&gt;-&lt;/span&gt; Removing column that's still referenced in app code
&lt;span class="p"&gt;-&lt;/span&gt; Deploying migration before code that handles new schema

&lt;span class="gu"&gt;## ROLLBACK REFERENCE&lt;/span&gt;

&lt;span class="gu"&gt;### Prisma Rollback Options&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;&lt;span class="c"&gt;# Mark migration as rolled back (doesn't undo DB changes)&lt;/span&gt;
prisma migrate resolve &lt;span class="nt"&gt;--rolled-back&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;migration_name]

&lt;span class="c"&gt;# Actual rollback requires manual SQL or restore&lt;/span&gt;
pg_restore &lt;span class="nt"&gt;-d&lt;/span&gt; mydb backup.dump
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;### Drizzle Rollback Options&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;&lt;span class="c"&gt;# No built-in rollback: write reverse migration&lt;/span&gt;
drizzle-kit generate  &lt;span class="c"&gt;# Create new migration to undo&lt;/span&gt;

&lt;span class="c"&gt;# Or restore from backup&lt;/span&gt;
pg_restore &lt;span class="nt"&gt;-d&lt;/span&gt; mydb backup.dump
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;## QUICK COMMANDS REFERENCE&lt;/span&gt;

&lt;span class="gu"&gt;### Prisma&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;prisma migrate status          &lt;span class="c"&gt;# Check pending migrations&lt;/span&gt;
prisma migrate dev             &lt;span class="c"&gt;# Dev: generate + apply&lt;/span&gt;
prisma migrate deploy          &lt;span class="c"&gt;# Prod: apply pending only&lt;/span&gt;
prisma migrate diff            &lt;span class="c"&gt;# Preview changes&lt;/span&gt;
prisma migrate resolve         &lt;span class="c"&gt;# Fix migration state&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;### Drizzle&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;drizzle-kit status             &lt;span class="c"&gt;# Check state&lt;/span&gt;
drizzle-kit generate           &lt;span class="c"&gt;# Generate SQL files&lt;/span&gt;
drizzle-kit migrate            &lt;span class="c"&gt;# Apply migrations&lt;/span&gt;
drizzle-kit push               &lt;span class="c"&gt;# Direct push (dev only)&lt;/span&gt;
drizzle-kit introspect         &lt;span class="c"&gt;# Generate schema from DB&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part doing the work for &lt;code&gt;migrate reset&lt;/code&gt; is &lt;strong&gt;Level 4: Full Reset&lt;/strong&gt;: the agent has to name the database first. The rest keeps it reading generated SQL instead of trusting the schema diff, which is where dropped columns hide. Commit &lt;code&gt;.cursor/rules/&lt;/code&gt; so the whole team gets it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drizzle has the same trap
&lt;/h2&gt;

&lt;p&gt;With Drizzle the risky command is usually &lt;code&gt;drizzle-kit push&lt;/code&gt;. It applies your schema straight to the database with no migration file, and &lt;code&gt;--force&lt;/code&gt; auto-approves data-loss statements. That's fine against a throwaway local database. Against anything persistent, use the tracked flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;drizzle-kit generate   &lt;span class="c"&gt;# writes SQL to your migrations folder&lt;/span&gt;
&lt;span class="c"&gt;# read the SQL&lt;/span&gt;
drizzle-kit migrate    &lt;span class="c"&gt;# applies it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check &lt;code&gt;dbCredentials.url&lt;/code&gt; in &lt;code&gt;drizzle.config.ts&lt;/code&gt;. If it reads &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt;, it has the same "which &lt;code&gt;.env&lt;/code&gt; is loaded?" problem. The checklist above already matches &lt;code&gt;**/drizzle/**&lt;/code&gt; and flags &lt;code&gt;push&lt;/code&gt; on a persistent database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3, optional: a hard stop
&lt;/h2&gt;

&lt;p&gt;A rule is prose, so a determined agent can still ignore it. For a hard stop, add a Cursor hook. Project hooks live in &lt;code&gt;.cursor/hooks.json&lt;/code&gt;, and a &lt;code&gt;beforeShellExecution&lt;/code&gt; hook runs before each shell command the agent wants to execute. Its &lt;code&gt;matcher&lt;/code&gt; is a regex tested against the full command string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"beforeShellExecution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".cursor/hooks/block-destructive.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"migrate reset|--force-reset|--accept-data-loss|drizzle-kit push.*--force"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script only runs for matching commands, so it can simply refuse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .cursor/hooks/block-destructive.sh&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable with &lt;code&gt;chmod +x .cursor/hooks/block-destructive.sh&lt;/code&gt;. Exit code 2 blocks the command, the same as returning &lt;code&gt;"permission": "deny"&lt;/code&gt; in the hook's JSON output. Other non-zero exit codes fail open and let the command through, and project hooks only run in a trusted workspace. Run those commands yourself, in your own terminal, when you mean it.&lt;/p&gt;




&lt;p&gt;Written with AI assistance and checked against the Prisma and Cursor docs.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>prisma</category>
      <category>drizzle</category>
      <category>database</category>
    </item>
  </channel>
</rss>
