TL;DR
Cursor reads four kinds of rules, and choosing the wrong type is why most people’s rules get ignored. As of June 2026:
- Project rules live in
.cursor/rules/*.mdc(version-controlled, scoped) and have four activation modes set by frontmatter: Always, Auto Attached (globs), Agent Requested (description), and Manual (@rule-name). - The legacy single
.cursorrulesfile at the repo root still works but has no scoping. Don’t run both formats at once — mixing them produces undefined behavior. - Plain-markdown
AGENTS.mdis now a supported alternative if you don’t want frontmatter. - The rules that change behavior are concrete, file-scoped, and short (Cursor recommends keeping each rule under ~500 lines; in practice, instruction-style rules work best well under 100). The 200-line “be a senior engineer, write clean code” wall is ignored.
This guide covers all four modes, the precedence between User / Project / Team rules, how to verify a rule is actually loading, and the current-version gotcha where alwaysApply: true gets silently downgraded.
What this covers
How to write Cursor rules that actually change model behavior, instead of the aspirational walls that get politely ignored. The rules system has three places it reads from in current Cursor: project rules under .cursor/rules/*.mdc with frontmatter, the legacy single .cursorrules file in the repo root, and the newer AGENTS.md plain-markdown file. This guide covers each, when to pick which, what to put in them, and the anti-patterns that produce zero behavior change.
If you have not done basic onboarding yet, run Cursor for beginners first.
The four rule types
This is the part most tutorials skip, and it is the whole game. A .mdc rule’s activation mode is not a separate setting — it is inferred from which frontmatter fields you fill in:
| Type | Frontmatter | When it loads |
|---|---|---|
| Always | alwaysApply: true | Injected into every chat, regardless of context. description/globs ignored. |
| Auto Attached | alwaysApply: false + globs set | Loads when an open/in-context file matches the glob. |
| Agent Requested | alwaysApply: false + description set, no globs | The agent reads the description and decides if the rule is relevant to the task. |
| Manual | alwaysApply: false, no description, no globs | Dormant until you @-mention it in chat (e.g. @my-rule). |
Practical mapping:
- Tech-stack declaration and a tiny “never” list → Always (one or two files, no more).
- Per-area conventions (API handlers, React components, migrations) → Auto Attached with a precise glob. This is the workhorse mode.
- Task-shaped knowledge (“how we wire a Stripe checkout”) → Agent Requested with a sharp description, so it loads when the agent recognizes the task even if no matching file is open yet.
- One-off or rarely-needed context → Manual, pulled in with
@.
Overusing Always is the single most common mistake: every always-apply rule eats context budget on every request, and when five of them fire at once the model averages across all of them and partially violates most.
Who this is for
Cursor users whose agent keeps making the same mistake — wrong import style, wrong test runner, wrong file location — and who have realized re-prompting every chat is not scalable. Especially valuable on teams: the shared rules folder is how project conventions stop relying on tribal memory.
When to reach for it
Reach for a rule when you see Cursor making the same correctable mistake twice in one week — that is your signal the constraint belongs in a rule, not a prompt. Also when a teammate’s Cursor session produces visibly different code style than yours; rules are how you sync defaults. They lose to inline @File mentions when the constraint is one-off; don’t write a rule for a one-time task.
Project vs User vs Team rules (and precedence)
Cursor reads rules from several scopes, and they stack rather than replace:
- Project rules —
.cursor/rules/*.mdc, committed to the repo, shared with everyone who clones it. - User rules — set in Cursor Settings, apply across all your projects. Note: User rules apply to Agent/Chat only, not inline edit (Cmd/Ctrl+K).
- Team rules — managed from the dashboard on Team/Enterprise plans, and can be marked required for all members.
When scopes conflict, precedence runs Team → Project → User. A required Team rule wins over your personal preference; your committed Project rule wins over your global User setting. Keep personal taste (e.g. “explain your reasoning”) in User rules and shared conventions in Project rules so they don’t fight.
Before you start
- A
.cursor/folder at the repo root (Cursor creates it on first use, or runmkdir .cursor/rules). - A list of specific complaints you have with Cursor’s current output — these become rule candidates. Vague complaints don’t make good rules.
- Knowledge of your repo’s globs: which paths are tests, which are server code, which are generated. Auto-attached rules load by glob.
- A teammate or two to dogfood the rules with. Solo-author rules tend to be over-tuned to one person’s style.
Step by step
- Pick a format. New projects:
.cursor/rules/*.mdc. Tiny solo projects:.cursorrulesis fine. Teams: definitely.cursor/rules/so you can scope and split. Either way, don’t keep.cursorrulesand.cursor/rules/populated at the same time. - Write your first scoped rule. Create
.cursor/rules/typescript.mdcwith frontmatter and a focused body. Keep it short — long rules get skimmed like documentation instead of followed like instructions:
---
description: TypeScript conventions for src/
globs: ["src/**/*.ts", "src/**/*.tsx"]
alwaysApply: false
---
# TypeScript rules
- Use named exports. Default exports are banned in `src/` except `pages/`.
- All async functions must have an explicit return type.
- Prefer `unknown` over `any`. If `any` is unavoidable, leave a `// eslint-disable-next-line @typescript-eslint/no-explicit-any` with a one-line reason.
- Imports: absolute paths via `@/` for anything in `src/`, relative for siblings.
- Errors thrown inside HTTP handlers must extend `AppError`.
This is an Auto Attached rule: globs is set and alwaysApply is false, so it loads only when a src/ TypeScript file is in context.
- Add a test rule. Create
.cursor/rules/tests.mdcwithglobs: ["**/*.test.ts", "**/*.spec.ts"]. Keep test-only conventions out of the general TypeScript rule. - Add your hard “never” list. Create
forbid.mdcwithalwaysApply: truefor the small set of repo-wide hard rules — paths that must not be edited, secrets that must not be inlined, commands that must not be run. This is one of the only files that should be Always. - Verify rules are loading. Open Cursor Settings → Rules, and confirm each
.mdcfile is listed with the activation type you expect (Always / Auto Attached / Agent Requested / Manual). If a rule should be active but shows the wrong type, check the glob and frontmatter for typos (rules not loading troubleshooting). - Test on a real prompt. Run a prompt that previously produced the wrong pattern, and check the output. If the rule didn’t help, it’s too vague or too long. Tighten and retry — don’t add a second rule.
- Commit
.cursor/rules/. Treat it like any other source file in code review.
You can also generate a starter rule from a back-and-forth chat using the /create-rule command in Agent mode (earlier builds shipped this as /Generate Cursor Rules; the command label has moved around between Cursor versions, so if you don’t see it, add the rule manually via Settings → Rules → Add Rule). Always read what it produces — generated rules tend to be longer and vaguer than what you’d write by hand.
A rule that produces honest output
The “be helpful, write clean code” rule is useless. This pattern is what actually moves model behavior — concrete, file-scoped, with a worked example:
---
description: API handler conventions
globs: ["src/api/**/*.ts"]
alwaysApply: false
---
# API handlers
All handlers in `src/api/` must:
1. Validate input with `zod` schemas defined in the same file. No inline `as` casts.
2. Wrap business logic calls in try/catch and re-throw as `AppError` with a stable error code.
3. Return responses via `respond.ok(data)` / `respond.error(code, message)` — never construct Response objects directly.
4. Log entry with `logger.handler(req, "name")` at the top of the function.
Example shape:
```ts
export const POST = async (req: Request) => \{
logger.handler(req, "createOrder");
const input = OrderSchema.parse(await req.json());
try \{
const order = await orders.create(input);
return respond.ok(order);
\} catch (e) \{
throw new AppError("ORDER_CREATE_FAILED", \{ cause: e \});
\}
\};
Concrete beats aspirational. The model copies what you show, not what you tell. If your conventions live in real files, reference them with `@filename.ts` inside the rule instead of pasting the whole file — it keeps the rule short and always current.
## AGENTS.md: the no-frontmatter option
If you don't want to learn MDC frontmatter, Cursor also reads an `AGENTS.md` file — plain markdown, no frontmatter, at the repo root or in subdirectories. It behaves like an Always rule for its directory, and a nested `backend/AGENTS.md` takes precedence over a root one for files under `backend/`. It is the lowest-friction way to give the agent project context, and it is portable: several other AI coding tools read the same `AGENTS.md` convention. The tradeoff is no glob scoping — for fine-grained control you still want `.cursor/rules/*.mdc`.
## Quality check
- Each `.mdc` file is focused on one concern. If a rule covers "TypeScript and tests and API and styling," split it.
- Globs are precise — a `src/**/*.ts` rule doesn't accidentally attach to `node_modules` (Cursor ignores those by default, but check).
- `alwaysApply: true` is used on at most one or two files. Every always-apply rule eats context budget.
- Rule bodies are short — well under the ~500-line ceiling Cursor recommends, ideally under 100 for instruction-style rules. Longer means the model treats them like documentation, not instructions.
- A representative test prompt produces the desired behavior. If not, rewrite the rule, don't add another.
- You are running one format, not two. `.cursorrules` and `.cursor/rules/` populated together is the most common "Cursor ignores my rules" cause.
## Common mistakes
- **200-line walls of aspirational text** — "be a senior engineer, write clean and maintainable code." The model ignores them.
- **Overusing `alwaysApply: true`.** Every always-apply rule shrinks the context budget for actual code, and stacking many of them makes the model average across and partially violate most.
- **Vague rules ("write good tests") with no example.** A rule the model can't compare against the current task blends into its general behavior and effectively vanishes. Show the shape you want.
- **One mega-rule covering the whole project.** Split by concern; the model attends better to focused files.
- **Wrong rule type.** Putting task knowledge in an Auto Attached rule means it stays dormant if no matching file is open. If you want the agent to pull it in by relevance, make it Agent Requested (set `description`, drop `globs`).
- **Running both `.cursorrules` and `.cursor/rules/`.** Behavior is undefined; pick one.
- **Conflicting rules across Team, Project, and User scopes.** Symptoms read as "Cursor ignores my rules" — see [Cursor rules not loaded](/en/articles/cursor-rules-not-loaded/).
## Known gotcha (current versions)
In some recent 3.x builds, multiple users reported that rules with `alwaysApply: true` (and the legacy `.cursorrules` file) were silently treated as **Agent Requested** instead of being auto-injected — meaning a rule you marked Always would only load when the agent decided it was relevant. If your Always rules seem to be ignored after a Cursor update:
1. Open Settings → Rules and confirm the rule's type still reads **Always**, not Agent Requested.
2. As a workaround, also encode the critical constraints in an `AGENTS.md` at the repo root, which is harder to silently downgrade.
3. Watch the Cursor changelog and forum — this behavior has shifted between releases, so the exact symptom depends on your build.
## FAQ
- **Should I migrate `.cursorrules` to `.cursor/rules/`?** If the file is over ~50 lines or has multiple concerns, yes — you get scoping and the four activation modes. Tiny single-purpose files can stay as `.cursorrules`. Just don't run both formats together.
- **Does the model see all my rules every prompt?** No. Only Always rules, plus Auto Attached rules whose glob matches a file in context, plus Agent Requested rules the agent judges relevant, plus any Manual rules you `@`-mention.
- **What's the difference between Agent Requested and Auto Attached?** Auto Attached loads by glob (a matching file must be in context). Agent Requested loads by relevance (the agent reads your `description` and decides), so it can fire before any matching file is open.
- **Can rules reference other files?** Yes — use `@filename.ts` inside a rule to pull in a real file instead of pasting its contents. That keeps rules short and always current.
- **Why don't my rules show up in the Cursor sidebar?** Usually a frontmatter typo (most often a missing closing `---`), invalid YAML (`true`, not `True`; globs as a list), or the file is in the wrong folder. Check [Cursor rules not loading](/en/articles/cursor-rules-not-loading/).
- **Do rules apply to inline edit (Cmd/Ctrl+K)?** Project rules do. User rules apply to Agent/Chat only.
## Related
- [Cursor for beginners — 30 minutes to a working loop](/en/articles/cursor-getting-started/)
- [Cursor indexing tutorial](/en/articles/cursor-indexing-tutorial/)
- [Cursor rules not loaded](/en/articles/cursor-rules-not-loaded/)
- [AI coding context management via project files](/en/articles/feed-project-reports-to-agents/)
- [Cursor Rules — Rules | Cursor Docs](https://cursor.com/docs/context/rules) Tags: #AI coding #Tutorial #Cursor