Skip to content
Vibe Coders

The Cursor Rules Files We Give Every Non-Technical Founder We Work With

The full text of the .cursor/rules directory we install on every founder project. Safety, git, Supabase, and env guardrails that stop the disasters before they happen.

By WitsCode11 min read

Every non-technical founder we onboard at WitsCode gets the same four files dropped into their repo before we touch a single feature. They live at .cursor/rules/ and they are the difference between a Cursor agent that ships usable code and one that quietly drops a production table at 2am because the founder typed "clean up the database" into chat.

This post is the entire pack. Copy it, paste it, ship it. If you are building with Cursor and you do not have project rules configured, you are running without brakes. The default experience assumes the person driving knows what a foreign key constraint is, and that assumption breaks the moment the driver is the CEO.

Before the files, three things about the Cursor rule system that the docs gloss over and most blog posts get subtly wrong. These are the difference between rules that fire and rules that sit in your repo looking impressive while the agent ignores them.

How Cursor Actually Loads Rules

Cursor reads rules from .cursor/rules/ inside your repo. Each rule is a .mdc file: Markdown with a YAML frontmatter block at the top. The older single-file .cursorrules in the repo root still works but is deprecated. If you are starting fresh, use the directory. It lets you scope rules to subtrees, keep files small, and version-control individual guardrails without merge conflicts.

The frontmatter has three fields that matter: description, globs, and alwaysApply. Everything below the closing --- is the instruction text injected into the agent's system prompt when the rule fires. Cursor hands it to the model verbatim, so your rule body is just a prompt. Specific, imperative, short.

The part that trips people up is the scope hierarchy. A rule file falls into one of four categories depending on how you fill in the frontmatter, and the category decides when the rule actually attaches to a conversation.

An Always rule has alwaysApply: true. When this is set, globs is ignored entirely and the rule is injected into every agent turn in that project. Use this sparingly. If every rule is always-on you burn your context window and dilute the signal.

An Auto Attached rule has alwaysApply: false and a non-empty globs pattern. Cursor watches which files are in the current chat context, and if any of them match the glob, the rule attaches. This is how you get Supabase rules to fire only when the agent is looking at SQL, and React rules to fire only when it is looking at components.

An Agent Requested rule has alwaysApply: false, empty or missing globs, and a descriptive description. The agent reads the description, decides whether it is relevant, and pulls it in. Vague descriptions get ignored.

A Manual rule is referenced with @ruleName in chat. We do not use these in the founder pack because founders will not remember to invoke them.

One last thing: .cursor/rules/ directories can be nested. If you put one inside apps/web/, those rules only attach when the agent is working inside that subtree. For a single repo this does not matter. For a monorepo, it does.

The Pack At A Glance

The WitsCode founder pack is four files, numbered for ordering inside the directory listing. The numbering is cosmetic: Cursor loads them in no particular order, but humans read top-to-bottom, and when the founder screenshots their repo to ask a question, sequence matters.

.cursor/
  rules/
    00-safety.mdc
    01-git.mdc
    02-supabase.mdc
    03-env.mdc

Three are always-on (00-safety, 01-git, 03-env). One is auto-attached by glob (02-supabase). The always-on rules are short on purpose: long always-on rules eat context, and short rules the agent internalizes beat long rules it skims.

00-safety.mdc: The Confirmation Rule

This is the single most important file in the pack. Without it, the agent will happily run rm -rf, drop migrations, truncate tables, and force-push branches because the founder asked it to "clean up" or "start fresh". With it, the agent stops and asks before doing anything that cannot be undone by reloading the page.

---
description: Destructive command safety. Always confirm before irreversible actions.
alwaysApply: true
---

# Safety Rules

You are working on a production codebase owned by a non-technical founder.
Assume every destructive action is a mistake until the user confirms otherwise.

## Never run without explicit confirmation in the same message

- `rm -rf`, `rm -r`, or any recursive delete
- `git reset --hard`, `git clean -fd`, `git push --force`
- Any `DROP`, `TRUNCATE`, `DELETE FROM` without a `WHERE` clause
- `supabase db reset`, `prisma migrate reset`, `drizzle-kit drop`
- Overwriting migration files that have already been applied
- Running scripts that mutate production data

## How to ask for confirmation

When the user's request would require one of the above, stop. Do not run
the command. Reply with:

1. The exact command you are about to run.
2. What it will change.
3. What cannot be undone.
4. The question: "Type YES to proceed."

Only proceed after the user replies with YES (uppercase, exact match).
Phrases like "sure", "ok", "go ahead" are not sufficient. The founder may
type those while multitasking.

## Never assume intent

If the user says "reset the database", "start fresh", "clean it up", or
"wipe it", you do NOT run a destructive command. You ask what specifically
they want removed. "Reset" almost never means what a developer thinks it means
when a non-technical person says it.

## Scope discipline

Only modify files relevant to the task. If the user asks for a button color
change, do not refactor the component, rename props, or touch sibling files.

The structure is deliberate. The imperative list at the top is scanned by the model first. The confirmation protocol is concrete enough that there is no room for the agent to improvise a softer version. The final scope note exists because the second most common founder complaint (after "it deleted my data") is "it changed a bunch of files I did not ask about".

01-git.mdc: The Pull Request Rule

Founders who vibe-code tend to commit directly to main because nobody told them not to. Then the staging site breaks and there is no way back except to read a git log they do not understand. This rule makes every change a pull request. Reviewing a PR is a skill a non-technical founder can actually learn. Debugging a broken main is not.

---
description: Git workflow. Branches and PRs only. Never push to main.
alwaysApply: true
---

# Git Rules

## Branching

- Never commit directly to `main`, `master`, or `production`.
- For any change, create a branch named `feat/<short-slug>`,
  `fix/<short-slug>`, or `chore/<short-slug>`.
- Branch off the latest `main`. If the working tree is dirty, stash first
  and tell the user what you stashed.

## Commits

- Commit in small, reviewable chunks. One logical change per commit.
- Write commit messages in the imperative mood: "add login button",
  not "added login button" or "adding login button".
- Never use `--no-verify`, `--no-gpg-sign`, or skip any hook.
- Never `git add -A` or `git add .` without first listing the files you
  are about to stage. Stage files by name.

## Pull requests

- After pushing a branch, always open a pull request with `gh pr create`.
- The PR title is the change in one line. The PR body has three sections:
  Summary (2-3 bullets of what changed), Why (one sentence of motivation),
  Test plan (a checklist the founder can actually click through in the
  staging preview).
- Link the PR URL in your reply so the founder can click it.

## Never

- Never `git push --force` or `git push --force-with-lease` on any branch
  without asking first.
- Never rewrite history on a branch that has been pushed.
- Never delete branches with `-D`. Use `-d` and let git refuse if the
  branch is unmerged.
- Never merge your own PR. The founder merges. Your job is to open it.

The "Test plan the founder can actually click through" instruction is the part that earns its place. Without it, agents write test plans full of terminal commands the founder cannot run. With it, they write things like "Open the staging URL, log in as demo@witscode.dev, click Settings, confirm the avatar is round." That is a test plan a CEO can execute.

02-supabase.mdc: The Migration Rule

Supabase is where we see the most founder-destroyed-production stories, and it is worth a dedicated rule because the Supabase CLI has a handful of commands that look benign and are in fact nuclear. supabase db reset drops and recreates the local database. A founder reads "reset" and thinks "refresh the page". An agent running unsupervised reads "reset" and types supabase db reset against the linked remote project.

This rule is auto-attached rather than always-on, because it only matters when the agent is touching database code.

---
description: Supabase and SQL safety. Never migrate or reset without confirmation.
globs: ["supabase/**/*", "**/*.sql", "**/migrations/**/*", "**/schema.ts", "**/schema.prisma"]
alwaysApply: false
---

# Supabase Rules

## Migrations

- Never run `supabase db push`, `supabase migration up`, or any migration
  against a remote project without explicit YES confirmation from the user.
- Never run `supabase db reset` against a remote project. For local, still
  confirm first: founders share their machine with their real data.
- When writing a new migration, create a timestamped file under
  `supabase/migrations/` and show the SQL to the user before applying it.
- Migrations are immutable once applied. Never edit a migration file that
  has been pushed. Write a new migration that corrects the prior one.

## Row Level Security

- Every new table gets RLS enabled in the same migration that creates it.
- Every new table gets at least one policy. If you do not know what policy
  to write, ask. Do not leave a table with RLS on and no policies, because
  that silently breaks the app.
- When adding a column that stores user-owned data, verify the existing
  policies still cover the new column. Mention this in your PR description.

## Destructive SQL

- `DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, and `DELETE` without `WHERE`
  require the same YES confirmation protocol as 00-safety.mdc.
- Renaming a column is destructive for any client still reading the old
  name. Before renaming, grep the repo for the old name and report every
  usage to the user.

## Linked projects

- Before any command that touches the remote, run `supabase projects list`
  and `supabase status` and show the output. The founder should see which
  project the command is about to hit.

## Types

- After any schema change, regenerate types with
  `supabase gen types typescript --linked > src/lib/database.types.ts`
  and commit the result in the same PR.

The RLS instruction catches a specific failure mode: the agent creates a table, enables RLS because it read somewhere that this is good practice, writes no policies, and now the app silently returns empty arrays from every query. The founder cannot debug this because the error is not an error. It is absence.

03-env.mdc: The Secrets Rule

The last always-on file is short, and it is short because the instruction is narrow. Do not touch env files. Do not read them. Do not print them. Do not commit them.

---
description: Environment variable and secret handling. Never read, print, or commit secrets.
alwaysApply: true
---

# Environment Rules

## Never read

- Do not open `.env`, `.env.local`, `.env.production`, `.env.*`, or any
  file matching `.env*` with your file reading tools.
- Do not print the contents of these files in chat.
- Do not include actual secret values in commit messages, PR descriptions,
  logs, or code comments.
- If you need to know whether a variable is set, ask the user. Do not
  inspect the file yourself.

## Editing

- You may add new variable NAMES to `.env.example` (never real values).
- If a new variable is required, update `.env.example` with the name and
  a placeholder, and tell the user which real value they need to add to
  their local `.env.local` and to their hosting provider's dashboard.

## Committing

- `.env`, `.env.local`, and `.env.production` must be in `.gitignore`.
  If they are not, stop and add them before doing anything else.
- If you discover a committed secret, do NOT just delete the file in a
  new commit. Tell the user immediately. The secret is still in history
  and must be rotated at the provider.

## Runtime

- When writing code that reads env vars, fail loudly on missing values at
  startup. Do not fall back to placeholder strings or empty defaults that
  will surface as bugs in production.

That last clause, about failing loudly, is the one we added after watching a founder ship a Stripe integration where STRIPE_SECRET_KEY defaulted to an empty string and every checkout silently created a $0 session. Failing at startup turns a silent production bug into a loud deploy error, which is the bug class a non-technical founder can actually notice.

How To Install The Pack

Create the directory at the root of the project. The path is literally .cursor/rules/ with the leading dot. On macOS and Linux this is hidden by default in Finder; use the terminal or show-hidden-files. Paste each file as named. Open Cursor, open Settings, go to Rules, and confirm the four files appear under Project Rules. If they do not show up, the most common cause is that the file extension is .md instead of .mdc. Cursor only parses .mdc.

Once installed, test them. Open a new chat and ask the agent to "reset the database". It should refuse and ask what you mean. Ask it to "commit this to main". It should push you to a branch. Ask it to "show me the .env". It should decline. If any of these fail, check the frontmatter syntax. A common failure is a missing space after the colon in alwaysApply: true, which silently breaks the YAML and causes the rule to be ignored entirely.

Why These And Not Others

You will find Cursor rules packs online with thirty files covering TypeScript style, React patterns, Tailwind conventions, and the correct way to name hooks. Those packs are written by developers for developers. They make an already-careful coder slightly more consistent. They do nothing for a founder who is about to type "drop the users table, we are starting over" at 11pm on a Friday.

The four files above are the minimum pack that prevents the four failure modes we see in founder repos every single week: destroyed data, broken main branch, orphaned migrations with no RLS, and leaked secrets. Everything else is optimization. These four are survival.

Once a founder is shipping steadily, we add a 10-style.mdc auto-attached on **/*.{ts,tsx} and a 11-testing.mdc that nudges the agent toward a Playwright test for every user-facing change. Those are the next layer, specific to each client. The survival pack is not.

If you would rather not copy-paste four files out of a blog post, we ship this exact pack as part of every WitsCode engagement. We install it, configure Cursor to load it, show the founder how to test that each rule fires, and add project-specific guardrails based on the stack the founder is actually on.

Want the WitsCode Cursor rules pack installed on your repo, plus a thirty-minute walkthrough with your founder? Book a setup call and we will have you running with guardrails before the end of the day.

The fastest way to stop vibe-coding disasters is to make the agent refuse to cause them. These four files are how we do it.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

MVP Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss vibe coders for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.