Skip to content

Cursor project rules are .mdc files in a repository’s .cursor/rules/ folder that give Cursor’s Agent standing instructions. Frontmatter sets when each rule loads: always, for matching files, on Agent’s judgment, or on an @-mention. Rules steer Agent; tests and CI still decide whether its output ships.

This page is for a developer setting up Cursor on a real repository. The pattern is familiar: for the third time this week you type “validate the body with Zod first” and “run the tests before you say done”, and Agent still writes a route that skips the tenant filter. Project rules put those corrections in the repository once, so every Agent session starts from them and every teammate gets the same ones.

  • Three working rule files: a short always-on core rule, a rule scoped to one area of the code, and a manual procedure you load on demand.
  • A decision table for which load mode each instruction needs, so the context is not filled with rules that do not apply.
  • A canary check that proves which rules Agent actually loaded, in under a minute and without guessing.
  • A probe task with a command that decides whether a rule changed Agent’s output, so you verify behaviour instead of reading the diff.
  • Copy-paste prompts that draft the core rule from your repository and test every rule you write.

How does Cursor decide when a project rule loads?

Section titled “How does Cursor decide when a project rule loads?”

Three frontmatter fields decide it: alwaysApply, description, and globs. The type dropdown in Cursor’s rule editor sets them for you; in a text editor you write them yourself.

Type in the dropdownalwaysApplydescriptionglobsWhen the rule loadsUse it for
Always ApplytrueignoredignoredEvery Agent sessionCommands that prove work is done, hard boundaries
Apply to Specific Filesfalse—setA file matching the pattern is in contextConventions for one area: API routes, migrations, UI
Apply Intelligentlyfalseset—Agent reads the description and decidesA procedure tied to a kind of task
Apply Manuallyfalse——You @-mention the rule in chatRare, risky procedures: backfills, releases

Five details trip up most first rule sets:

  • Only .mdc counts. Cursor’s rules documentation says “Project rules live in .cursor/rules as .mdc files” and “A plain .md file in .cursor/rules is ignored by the rules system” (checked 2026-10-12).
  • Multiple globs are comma-separated, as in src/**/*.ts, src/**/*.tsx. A bare *.ts matches files in the repository root only.
  • Rules reach Agent only. Cursor’s help says “Rules only apply to Agent (Chat). They do not apply to Tab completion, Inline Edit, or Bugbot PR reviews” (checked 2026-10-12). Bugbot takes its own guidance from .cursor/BUGBOT.md, a file described second-hand and not re-checked on 2026-10-12; see Bugbot.
  • Subfolders work. .cursor/rules/frontend/components.mdc is a project rule too, which helps once you have more than a handful.
  • Other instruction files count too. Cursor’s rules documentation says “Cursor supports AGENTS.md in the project root and subdirectories”, and nested files “are combined with parent directories, with more specific instructions taking precedence”. Cursor “reads CLAUDE.md files the same way it reads AGENTS.md”, and “CLAUDE.md files are always applied to every conversation, regardless of any alwaysApply frontmatter setting” (checked 2026-10-12).

Start from the corrections you repeat, not from a style guide. Agent already knows common conventions; a rule earns its place only when it changes what Agent would otherwise do.

  1. Create the folder and move any old rules file. In a terminal at the repository root:

    Terminal window
    mkdir -p .cursor/rules
    ls .cursorrules 2>/dev/null && echo "move its content into .cursor/rules/core.mdc"

    If the repository still has a single-file .cursorrules, move its content into the core rule below and delete the file, so all rules live in one place that the team reviews. Cursor’s help calls the .cursorrules file in the project root “legacy and will be deprecated” and recommends migrating it to a project rule set to Always Apply (checked 2026-10-12).

  2. Write a short core rule. Save it as .cursor/rules/core.mdc. It carries only what every task needs: the stack in one line, the commands that define “done”, and the boundaries Agent must not cross. Keep it under about 40 lines, because every session pays for it.

    ---
    description: Stack, done criteria, and hard boundaries for every task
    alwaysApply: true
    ---
    Stack: TypeScript (strict), Next.js App Router, Drizzle on PostgreSQL, Vitest, pnpm.
    Done means all of these pass, and you paste the last lines of each:
    - pnpm lint
    - pnpm typecheck
    - pnpm test
    Boundaries:
    - Never add a dependency without naming it and the reason in your summary.
    - Never edit a merged migration in src/db/migrations/; add a new one.
    - Never weaken, skip, or delete an existing test to make a check pass.
    - Log with the logger in @src/lib/logger.ts, never console.log.

    The @src/lib/logger.ts reference points Agent at the real file instead of copying code into the rule, so the rule does not go stale when the file changes. An @ reference loads that file whenever the rule loads, so in an Always Apply rule keep references to small files.

  3. Scope an area rule to its files. Save it as .cursor/rules/api-routes.mdc. It loads only when a matching file is in context, so it costs nothing while Agent works on the UI.

    ---
    globs: src/app/api/**/*.ts
    alwaysApply: false
    ---
    API route conventions:
    - Validate the request body with a Zod schema from @src/lib/validators/index.ts before any other work.
    - Every database query filters by organizationId. A route without it is a security bug.
    - Throw AppError from @src/lib/errors.ts; never return raw database errors or stack traces.
    Canonical route: @src/app/api/users/route.ts
  4. Keep a risky procedure manual. Save it as .cursor/rules/data-backfill.mdc. With no description and no globs, it loads only when someone types @data-backfill in chat.

    ---
    alwaysApply: false
    ---
    Data backfill procedure:
    - Write the backfill as a plain Node script (.mjs) in scripts/backfills/ with a --dry-run flag that prints row counts.
    - Batch writes in chunks of 1,000 and make the script safe to re-run.
    - Stop after the dry run and show its output. A human runs the real backfill.
  5. Commit the rules. Rules in .cursor/rules/ are version-controlled, so review a rule change like a code change. When the team grows, add .cursor/rules/ to CODEOWNERS so one named person signs off on rule edits.

Adapt the paths and commands to your stack; the structure is what matters. You can also type /create-rule in Agent and describe the rule; it writes the file with frontmatter into .cursor/rules/. Review what it writes before you commit it, because a generated rule tends to restate conventions Agent already follows.

How do you prove a project rule loaded and changed the output?

Section titled “How do you prove a project rule loaded and changed the output?”

A rule is an untested instruction until something checks it. Two quick checks cover a first rule set: a canary that proves which rules loaded, and a probe task whose result a command decides.

Cursor puts applied rule contents into the model context, so Agent can quote from a rule it has. Asking “which rules do you see?” invites a plausible guess; asking for a random token it cannot guess does not.

  1. Add a temporary last line to each rule with a unique token, for example Canary: core-7f3a in core.mdc, Canary: api-19c2 in api-routes.mdc, and Canary: backfill-d04e in data-backfill.mdc.
  2. Close any editor tabs under src/app/api/, open a new Agent chat with no files attached, and paste the prompt below. Only core-7f3a should come back.
  3. In another new chat, attach src/app/api/users/route.ts and ask again. Now api-19c2 should appear too.
  4. In a third chat, type @data-backfill and ask again. backfill-d04e should appear.
  5. For an Apply Intelligently rule, give it a canary too, open a new chat, type a request that matches its description (for example “Add a new API endpoint end to end”), and paste the prompt again. Its token should appear; if it does not, Agent skipped the rule for that request.
  6. Remove the canary lines and commit.

If a token is missing, the rule did not load; the failure modes below tell you why. For a second opinion, Customize → Rules lists every rule Cursor found and its status.

Loading is not obeying. Give Agent a small task that the rule should shape, then let a command decide instead of reading the diff.

Probe task in a new chatRule it exercisesCommand that decides
“Add GET /api/invoices/[id] that returns one invoice”api-routes.mdcpnpm test src/app/api/invoices passes, including a test where a user requests another organization’s invoice and gets 404; rg -c organizationId src/app/api/invoices is only a quick pre-check
“Log the retry count in the payment service”core.mdcgit add -N src/lib/services && git diff -U0 -- src/lib/services | rg '^\+.*console\.log' returns nothing, so Agent added no console.log, not even in a new file (git add -N makes untracked files visible to git diff)
“@data-backfill fill invoices.currency from orders”data-backfill.mdcnode scripts/backfills/<name>.mjs --dry-run exits 0 and prints row counts, and git status --porcelain --untracked-files=all lists only ?? scripts/backfills/<name>.mjs, with no path under src/db/migrations/

Run the probes again whenever you change a rule. A probe that fails means the rule is unclear, did not load, or conflicts with another rule or with the code.

What still checks the code after the rules work?

Section titled “What still checks the code after the rules work?”

Rules steer Agent; they enforce nothing. Cursor’s own guidance says AI guidance should not be your only security control. The core rule asks Agent to run the done commands, but the same commands in CI are what block a bad merge. A boundary that must never be crossed belongs in a CI gate or a fitness function. The rule is only the hint that keeps Agent away from it. Whoever merges the pull request signs off on the green gates, not on having read every line.

Should an instruction be a rule, a skill or a linter check?

Section titled “Should an instruction be a rule, a skill or a linter check?”

Most weak rule sets come from putting a sound instruction in the wrong place.

The instruction is…Put it inWhy
Needed on almost every taskAlways Apply ruleShort, and paid for in every session
True in one area of the codeApply to Specific Files ruleLoads only where it matters
A long multi-step procedure with scriptsAn Agent SkillSkills load their resources on demand and work across agents that support the standard
Mechanically checkable: naming, imports, anyLinter or type ruleRuns on every commit, not only when Agent remembers
Something Agent does correctly alreadyNowhereIt costs context and changes nothing

Cursor’s documentation says that Cursor loads project skills from .agents/skills/ and .cursor/skills/ (user level: ~/.agents/skills/ and ~/.cursor/skills/) and, “for compatibility”, from .claude/skills/ and .codex/skills/ (and ~/.claude/skills/ and ~/.codex/skills/). Nested project skill folders are scoped to their subtree. Built-in skills include /create-rule, /create-skill and /migrate-to-skills (checked 2026-10-12). On 2026-09-26 the docs described /migrate-to-skills as converting eligible dynamic rules and slash commands into skills. How to install and share skills is covered in the skills overview.

A rule never loads. The file ends in .md, not .mdc, or the frontmatter is malformed (a missing closing ---, a tab in the YAML). Recovery: rename it to .mdc, fix the frontmatter, and repeat the canary check.

A file-scoped rule never fires. The glob is *.ts, which matches the repository root only, or it points at a folder that moved. Recovery: use **/*.ts or a directory prefix such as src/app/api/**/*.ts. For a rough check, list the files it matches with shopt -s globstar; ls src/app/api/**/*.ts in bash. Then confirm with the canary check, because bash globbing only approximates Cursor’s matcher.

An Apply Intelligently rule is skipped for the task it was written for. Its description names a topic (“API stuff”) instead of a task. Recovery: rewrite the description as the request a developer would type, such as “Adding a new API endpoint end to end”, or scope the rule with globs instead.

Agent follows a rule that contradicts the code. The rule describes last quarter’s pattern. Recovery: replace pasted code with @ references to the canonical file, and delete the stale lines.

Context fills up before the task starts. The always-on rule grew one line at a time. Recovery: move area-specific lines into glob rules and procedures into skills, and move @ references out of the always-on rule; context management in Cursor shows how to see what the session carries.

The team expects rules in Tab, Inline Edit or Bugbot. Rules apply to Agent only (Cursor help, checked 2026-10-12). Recovery: put pull request review guidance in Bugbot’s own guidance file (see Bugbot) and enforce style with the linter.

A Team Rule beats your project rule. On Teams and Enterprise plans, Team Rules take precedence over project rules, which take precedence over User Rules: Cursor’s rules help page says “When rules conflict, precedence is: Team Rules > Project Rules > User Rules” (read 2026-10-13). Since Cursor 3.21 (2026-09-17, Cursor’s release notes), a team rule with file patterns applies only to matching files. Recovery: ask the admin to keep Team Rules to policy, and keep code conventions in the repository.

Did this guide help you?

One tap, no sign-in.

not at all a lot