Start Debugging

Migrate Cursor Rules to Skills, Subagents, and Plugins (Cursor 3.11)

Cursor did not delete rules. It split them. This checklist sorts a .cursorrules file and a folder of .mdc rules into the four things they should be in Cursor 3.11: an AGENTS.md, a small set of surviving rules, skills, and subagents, then bundles the result as a plugin.

If you have a .cursorrules file from 2024 or a .cursor/rules/ folder that has quietly grown to twenty .mdc files, the migration in front of you is a sorting problem, not a rewrite. Cursor 3.11 (released July 10, 2026) still loads rules exactly as it always has. What changed is that three newer homes now exist for things that used to be crammed into a rule: skills, subagents, and plugins. The built-in /migrate-to-skills skill, shipped in Cursor 2.4, moves one specific slice automatically and deliberately refuses to touch the rest. Expect an hour for a mid-sized repo. Nothing breaks if you do nothing, but the token bill and the “why did the agent ignore my rule” complaints do not go away on their own.

The most common thing I see people get wrong here is reading “migrate to skills” as “rules are deprecated.” They are not. Cursor’s rules documentation is still a live page with four rule modes, team/project/user precedence, and nested AGENTS.md support. The migration is a split, and knowing where the seams are is the whole job.

Why bother when rules still work

What actually changes

AreaChangeSeverity
.cursorrules (single legacy file)No longer documented on the rules page. Gets no scoping, no activation modes, loads unconditionally.high
Dynamic rules (alwaysApply: false, no globs)Superseded by skills. /migrate-to-skills converts these.medium
Slash commands (.cursor/commands/)Converted to skills with disable-model-invocation: true. The docs URL for commands now serves skills content.medium
Always-apply rules (alwaysApply: true)Unchanged. Stays a rule. Not migrated.none
Glob-scoped rules (globs: "src/**/*.tsx")Unchanged. Stays a rule. Not migrated.none
User rules (set in Customize)Not migrated, because they are not on the file system.none
Team rulesUnchanged. Still first in precedence: Team, then Project, then User.none

The severity column is the point. Two of the seven rows are “none,” and one of the automatic conversions is genuinely automatic. This is a smaller migration than the phrase “migrate to skills” implies.

Pre-flight checklist

Migration steps

1. Inventory what you have

Before touching anything, get the full list. From the repo root:

# Cursor 3.11, August 2026
ls -la .cursorrules AGENTS.md 2>/dev/null
find .cursor/rules -name '*.mdc' | sort
find .cursor/commands -type f 2>/dev/null | sort
find . -name AGENTS.md -not -path './node_modules/*' | sort

Then read the frontmatter of every rule at once, because the frontmatter is what decides the destination:

# prints "file: <frontmatter block>" for each rule
for f in .cursor/rules/*.mdc; do
  echo "== $f"
  awk '/^---$/{n++; next} n==1' "$f"
done

Verify: you have a list where every .mdc file shows either alwaysApply: true, a globs: line, a description: line, or nothing at all. Files showing nothing are manual @-mention rules.

2. Sort every rule against the frontmatter table

This is the decision that makes the rest mechanical. A rule’s frontmatter already encodes its activation mode, and the activation mode maps cleanly onto a destination:

FrontmatterCursor calls itDestination
alwaysApply: trueAlways ApplyStays a rule, or folds into AGENTS.md
globs: "..." setApply to Specific FilesStays a rule
description: onlyApply IntelligentlyBecomes a skill
No frontmatterApply ManuallyBecomes a skill with disable-model-invocation: true

Two overrides on top of that table, both based on content rather than frontmatter:

Verify: every file in your inventory has exactly one destination written next to it.

3. Retire the legacy .cursorrules file

Do this before running the migration skill, because /migrate-to-skills works on .cursor/rules/ and commands, not on the legacy single file. You have two sensible targets.

If the content is portable repo guidance that other agents should also read, AGENTS.md at the repo root is the better home. Nested AGENTS.md files in subdirectories now apply automatically when the agent works in that directory or below, and more specific files take precedence, so a monorepo can split what used to be one file:

repo/
  AGENTS.md                 # house style, build commands
  apps/web/AGENTS.md        # React conventions
  services/api/AGENTS.md    # API conventions

If the content is genuinely Cursor-specific and you want per-rule activation, split it into .cursor/rules/*.mdc files instead, one concern per file:

<!-- .cursor/rules/typescript-style.mdc -->
---
description: "TypeScript conventions for this repo"
globs: "src/**/*.{ts,tsx}"
alwaysApply: false
---

- Strict mode is on. No `any`, use `unknown` and narrow.
- Prefer `type` over `interface` unless declaration merging is needed.

Then delete .cursorrules.

Verify: open a chat, ask “what rules are active right now”, and confirm the agent lists the new files and not the deleted one. Cross-check against the Customize panel, which groups everything by activation mode.

4. Run /migrate-to-skills and read the diff

In a Cursor chat:

/migrate-to-skills

It converts dynamic rules (alwaysApply: false or undefined, and no globs) into standard skills, and converts both user-level and workspace-level slash commands into skills with disable-model-invocation: true so their explicit-invocation behaviour survives. It leaves always-apply rules, glob-scoped rules, and user rules alone.

The output for each converted rule is a directory:

.cursor/skills/api-error-handling/
  SKILL.md

with frontmatter that looks like this:

---
name: api-error-handling
description: How this service maps domain errors to HTTP responses. Use when adding or changing an API endpoint.
---

# API error handling

Use when adding an endpoint under `services/api/` or changing an error response shape.

- Domain errors derive from `AppError` and carry a `code`.
- The middleware in `src/middleware/errors.ts` maps `code` to status.
- Never return a raw exception message to the client.

Verify: git diff --stat should show new directories under .cursor/skills/ and deletions under .cursor/rules/. Read every generated description line. That single line is the entire basis on which the agent decides whether to load the skill, and a description copied verbatim from an old rule title is usually too vague. “React conventions” is a bad description; “React component conventions for this repo. Use when creating or refactoring a component under apps/web/” is a good one.

5. Hand-convert what the skill deliberately skipped

The migration is conservative by design, so the interesting cases are the ones it refused. Take an always-apply rule that you classified as a procedure in step 2 and convert it manually, including the parts a rule could never hold:

.cursor/skills/release-checklist/
  SKILL.md
  scripts/
    verify-changelog.sh
  references/
    versioning-policy.md
---
name: release-checklist
description: Cut a release for this repo. Use when the user asks to release, tag, or publish a version.
disable-model-invocation: true
---

# Release checklist

- Run `scripts/verify-changelog.sh` first. It fails if the unreleased section is empty.
- Version rules live in `references/versioning-policy.md`. Read it before choosing the bump.
- Tag as `v<major>.<minor>.<patch>` and push the tag, not the branch.

disable-model-invocation: true is the right call for anything destructive: the skill exists, the agent can see it, but it only runs when you type /release-checklist. references/versioning-policy.md is not loaded until the agent reaches the versioning step, which is the progressive-disclosure behaviour you cannot get from a rule.

If instead you want the skill to auto-attach based on files rather than on the agent’s judgement, use paths rather than moving it back to a rule:

---
name: migration-conventions
description: Database migration conventions for this repo.
paths: "db/migrations/**"
---

Verify: type / in chat and confirm each new skill appears by name. Then open a file that matches a paths glob and confirm the scoped skill attaches.

6. Promote role-shaped rules into subagents

Anything you flagged as a role in step 2 becomes a markdown file in .cursor/agents/. The frontmatter is different from a skill’s:

<!-- .cursor/agents/security-reviewer.md -->
---
name: security-reviewer
description: Reviews a diff for injection, authz, and secret-handling problems. Delegate before opening a PR.
model: claude-opus-5[effort=high,context=300k]
readonly: true
is_background: false
---

You review diffs for security problems only. Report findings as a list with
file, line, and severity. Do not fix anything. If the diff is clean, say so
in one line.

model defaults to inherit; you can pin a specific ID such as composer-2, and model IDs accept bracketed parameters. readonly: true is the flag that makes a reviewer safe to delegate to freely, since it cannot write. is_background: true returns immediately and lets the subagent work without blocking the parent, which is what you want for a long verification pass and not what you want for something whose answer you need next.

Invoke with /security-reviewer check the auth changes, or just describe the task and let the agent delegate based on the description. Since Cursor 2.5 a subagent can launch child subagents, so a reviewer can hand off to a test-writer without coming back to the main thread. The tradeoffs against the equivalent Claude Code feature are in Cursor subagents vs Claude Code subagents, and the wider “which abstraction do I even want” question is worked through in skills vs subagents vs MCP servers.

Verify: run /security-reviewer on a deliberately bad diff and confirm it reports rather than edits.

7. Bundle the result as a plugin

Once the split is done you have four kinds of artefact scattered across .cursor/. A plugin makes them one installable unit. Add .cursor-plugin/plugin.json:

{
  "name": "acme-repo-conventions",
  "version": "1.0.0",
  "description": "Rules, skills, and review subagents for the ACME monorepo",
  "rules": "rules/",
  "skills": "skills/",
  "agents": "agents/",
  "commands": "commands/",
  "hooks": "hooks/hooks.json",
  "mcpServers": "mcp.json"
}

Only name is required, and every component path is optional because Cursor auto-discovers the conventional folders. The manifest also accepts author, homepage, repository, license, keywords, logo, and variables (a JSON Schema declaring user-configurable variable names, which is how you avoid hardcoding a connection string into a shared bundle). Note the field is agents, not subagents.

If your plugin also ships MCP servers, the config shape is the same one you already use, and the team-distribution side of that is covered in distributing a team MCP config across cloud agents and the IDE.

Verify: install the plugin from the Customize page on a second machine or a clean clone, and confirm skills, subagents, and rules all appear.

The smoke test after you finish

Rolling back

This migration is fully reversible because it is file moves. Rules, skills, and subagents are all plain files in the repo, so git revert on the migration commit restores the previous behaviour exactly. There is one asymmetry worth knowing: a rule converted to a skill loses its guaranteed inclusion, so if the agent starts missing a convention after the migration, the fix is to move that one file back to .cursor/rules/ with alwaysApply: true rather than to revert the whole thing.

What bites people

Sources

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Back