Migrating .cursorrules to .cursor/rules: A Token Diet
- Monolithic .cursorrules Bloat: Loading a single large rule file consumes 1,500–3,000 extra tokens per prompt and causes rule collisions across unrelated languages.
- Directory Scoping: Migrating to individual
.cursor/rules/*.mdcfiles leverages targetedglobs, semantic description matching, and on-demand manual triggering. - 80% Overhead Cut: Scoped rules load only when relevant files or intents match, drastically preserving context window space and preventing instruction drift.
You added a new rule to .cursorrules last sprint: "always use Zod for validation." Now Cursor is trying to slap Zod schemas onto your Go microservice's YAML config files. That's not a hallucination — that's your monolithic rules file doing exactly what you told it to do, everywhere, all the time.
If you've been staring at a bloated .cursorrules file wondering why the agent keeps ignoring half your instructions while stubbornly following the other half in the wrong context, you're not imagining things. Cursor has quietly deprecated the single-file format in favor of a scoped, directory-based system — .cursor/rules/*.mdc — and the difference in agent behavior is night and day.
This is an engineering migration guide, not a vague feature announcement. We'll break down the .mdc schema, walk through a real repository refactor, and share three battle-tested templates you can copy and deploy immediately.
Why the Monolithic .cursorrules File Falls Apart
Think of .cursorrules like a single 2,000-page employee handbook that HR photocopies and hands to every new hire — the barista, the backend engineer, and the facilities manager — regardless of their responsibilities. Everyone has to read the entire manual before doing anything, even the three sentences that actually apply to their shift.
That's precisely what happens on every single prompt turn when you rely on a root-level .cursorrules file. Cursor injects the entire file into context, whether you're editing a React component or a SQL migration script. This legacy approach creates two severe problems:
- The Token Tax: A 400-line rules file burns 1,500–3,000 tokens per prompt turn before the model even reads your actual code.
- Instruction Collision: Frontend conventions (Tailwind class ordering, JSX props) get injected into backend tasks, confusing the LLM about which constraints actually apply.
The result is the well-documented "lost in the middle" phenomenon — long-context models pay significantly less attention to instructions buried in the middle of a massive prompt. Cursor's architectural solution is straightforward: split one giant file into targeted, modular .mdc files that load only when relevant.
Anatomy of the .mdc File: Frontmatter + Activation Modes
Every rule now lives in .cursor/rules/ as an individual .mdc file — standard Markdown with a structured YAML frontmatter header. Here is the official schema:
---
description: Standards for React Server Components and UI mutations
globs: ["src/components/**/*.tsx", "src/app/**/*.tsx"]
alwaysApply: false
---
# Rule body goes here in plain Markdown.
Use named exports only. No default exports for components.
Three core frontmatter fields control rule execution:
description(string): Summary evaluated semantically by the Cursor Agent to decide if the rule matches the user's intent, even when no glob pattern matches.globs(string or array): Wildcard file path patterns. When matching files are active in the editor or referenced, the rule auto-attaches.alwaysApply(boolean): Whentrue, injects into every session universally. Use this only for foundational repository invariants.
The 4 Activation Modes
| Mode | Trigger Condition | Best Use Case | Token Overhead |
|---|---|---|---|
| Always Applied | alwaysApply: true |
Core stack invariants (package manager, monorepo layout) | Constant (< 500 tokens recommended) |
| Glob Scoped | Active file path matches globs |
Domain conventions (frontend components, API routes, DB migrations) | Zero when file is not active |
| Agent-Requested | Semantic match on description |
Cross-cutting concerns (e.g., git commit standards, test setups) | Zero until developer intent matches |
Manual @rule |
Explicit @rule-name.mdc in Composer |
One-off migration scripts, rare audit tasks | Zero unless explicitly invoked |
(Legacy) .cursorrules |
Always, unconditionally | Deprecated — avoid in new projects | Constant & unavoidable tax |
Migration Blueprint: 4 Phases
↓
[Phase 2: Scaffold .cursor/rules/ directory]
↓
[Phase 3: Write scoped frontmatter per file]
↓
[Phase 4: Verify triggers in Composer] ➔ {Rules firing?} ➔ Yes ➔ [Delete root .cursorrules]
Phase 1: Rule Inventory & Token Auditing
Open your existing .cursorrules and split each block into two categories: universal invariants (package manager, commit style) versus domain-specific rules (React, Prisma, Docker). In most repositories, over 80% of instructions are domain-specific and should never be loaded globally.
Phase 2: Directory Scaffolding
.cursor/
rules/
project-core.mdc # alwaysApply: true, <500 tokens
react-tailwind.mdc # globs: src/components/**/*.tsx
api-routes.mdc # globs: src/app/api/**/*.ts
database-migrations.mdc # alwaysApply: false, agent-requested
Phase 3: Crafting Scoped Frontmatter
Avoid generic catch-all patterns like globs: ["**/*"], which recreate the original monolithic problem. Match specific folders and file extensions precisely:
---
description: API route handler conventions for Next.js App Router
globs: ["src/app/api/**/route.ts", "src/lib/api/**/*.ts"]
alwaysApply: false
---
Phase 4: Verification & Safe Deprecation
In Cursor Composer, type @ to view available rules. Confirm that opening a .tsx file attaches the React rule without injecting database schema rules. Once verified, delete the root .cursorrules file to prevent double-injection.
Three Production-Ready .mdc Templates
project-core.mdc)
---
description: Non-negotiable project-wide invariants
globs: []
alwaysApply: true
---
# Core Standards (always loaded — keep this file lean)
- Package manager: pnpm only. Never suggest npm or yarn commands.
- Monorepo structure: apps/ for deployables, packages/ for shared libs.
- TypeScript strict mode is non-negotiable across all packages.
- Commit messages follow Conventional Commits (feat:, fix:, chore:).
- Never commit secrets or .env files directly to version control.
react-tailwind.mdc)
---
description: Standards for React components using Tailwind CSS
globs: ["src/components/**/*.tsx", "apps/web/**/*.tsx"]
alwaysApply: false
---
# React + Tailwind Component Rules
- Use named exports only; no default exports for UI components.
- Order Tailwind utility classes: Layout -> Spacing -> Typography -> Color.
- All interactive buttons and links require an aria-label or visible text.
- Prefer component composition (children props) over deep prop-drilling booleans.
- Encapsulate server-only logic outside of client components marked with "use client".
database-migrations.mdc)
---
description: Guidelines for writing and reviewing database schema migrations
globs: ["db/migrations/**/*.sql", "prisma/migrations/**/*"]
alwaysApply: false
---
# Database Migration Standards
- Every migration script must include a corresponding down/revert migration.
- Never use DROP COLUMN without a two-phase deprecate-then-remove deployment.
- Reference the active schema audit doc before altering foreign key constraints.
- Avoid locking table operations during production traffic peaks.
Token Budget Math: Why Scoping Actually Works
| Editing Context | Active Rules Loaded | Approx. Token Cost per Turn |
|---|---|---|
Legacy .cursorrules (editing Go file) |
Entire 380-line monolithic file | ~2,800 tokens |
Scoped .mdc (editing Go file) |
project-core.mdc only |
~400 tokens |
Scoped .mdc (editing .tsx file) |
project-core.mdc + react-tailwind.mdc |
~750 tokens |
- Keep individual
.mdcfiles under 150 lines. If a rule exceeds this limit, split it by submodule. - Never write
globs: ["**/*"]— that recreates the monolithic overhead under a new file extension. - Formulate
descriptionfields as clear developer queries to ensure reliable semantic agent activation.
Common Migration Questions & Troubleshooting
Temporarily during migration, yes. However, remove legacy files once verified, as overlapping rules cause duplicate instructions in prompt contexts.
Check your glob relative paths. Globs in Cursor resolve from the repository workspace root. If your project is inside a subfolder, specify apps/web/src/**/*.tsx instead of src/**/*.tsx.
No. Keep a unified .cursor/rules/ folder at the workspace root and use distinct path globs (packages/ui/**, apps/api/**) for modular scoping.
Audit your current .cursorrules file this week. Split it by domain, write tight globs, and reserve alwaysApply: true for global constraints.
For more deep dives on agent configurations and LLM engineering patterns, explore our comprehensive LLM & CODE guide category.
댓글
댓글 쓰기