* feat(cursor): add Cursor CLI as target provider Add converter, writer, types, and tests for converting Claude Code plugins to Cursor-compatible format (.mdc rules, commands, skills, mcp.json). Agents become Agent Requested rules (alwaysApply: false), commands are plain markdown, skills copy directly, MCP is 1:1 JSON. * docs: add Cursor spec and update README with cursor target * chore: bump CLI version to 0.5.0 for cursor target Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: note Cursor IDE + CLI compatibility in README --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
12 KiB
title, type, date
| title | type | date |
|---|---|---|
| Add Cursor CLI as a Target Provider | feat | 2026-02-12 |
Add Cursor CLI as a Target Provider
Overview
Add cursor as a fourth target provider in the converter CLI, alongside opencode, codex, and droid. This enables --to cursor for both convert and install commands, converting Claude Code plugins into Cursor-compatible format.
Cursor CLI (cursor-agent) launched in August 2025 and supports rules (.mdc), commands (.md), skills (SKILL.md standard), and MCP servers (.cursor/mcp.json). The mapping from Claude Code is straightforward because Cursor adopted the open SKILL.md standard and has a similar command format.
Component Mapping
| Claude Code | Cursor Equivalent | Notes |
|---|---|---|
agents/*.md |
.cursor/rules/*.mdc |
Agents become "Agent Requested" rules (alwaysApply: false, description set) so the AI activates them on demand rather than flooding context |
commands/*.md |
.cursor/commands/*.md |
Plain markdown files; Cursor commands have no frontmatter support -- description becomes a markdown heading |
skills/*/SKILL.md |
.cursor/skills/*/SKILL.md |
Identical standard -- copy directly |
| MCP servers | .cursor/mcp.json |
Same JSON structure (mcpServers key), compatible format |
hooks/ |
No equivalent | Cursor has no hook system; emit console.warn and skip |
.claude/ paths |
.cursor/ paths |
Content rewriting needed |
Key Design Decisions
1. Agents use alwaysApply: false (Agent Requested mode)
With 29 agents, setting alwaysApply: true would flood every Cursor session's context. Instead, agents become "Agent Requested" rules: alwaysApply: false with a populated description field. Cursor's AI reads the description and activates the rule only when relevant -- matching how Claude Code agents are invoked on demand.
2. Commands are plain markdown (no frontmatter)
Cursor commands (.cursor/commands/*.md) are simple markdown files where the filename becomes the command name. Unlike Claude Code commands, they do not support YAML frontmatter. The converter emits the description as a leading markdown comment, then the command body.
3. Flattened command names with deduplication
Cursor uses flat command names (no namespaces). workflows:plan becomes plan. If two commands flatten to the same name, the uniqueName() pattern from the codex converter appends -2, -3, etc.
Rules (.mdc) Frontmatter Format
---
description: "What this rule does and when it applies"
globs: ""
alwaysApply: false
---
description(string): Used by the AI to decide relevance -- maps from agentdescriptionglobs(string): Comma-separated file patterns for auto-attachment -- leave empty for converted agentsalwaysApply(boolean): Setfalsefor Agent Requested mode
MCP Servers (.cursor/mcp.json)
{
"mcpServers": {
"server-name": {
"command": "npx",
"args": ["-y", "package-name"],
"env": { "KEY": "value" }
}
}
}
Supports both local (command-based) and remote (url-based) servers. Pass through headers for remote servers.
Acceptance Criteria
bun run src/index.ts convert --to cursor ./plugins/compound-engineeringproduces valid Cursor config- Agents convert to
.cursor/rules/*.mdcwithalwaysApply: falseand populateddescription - Commands convert to
.cursor/commands/*.mdas plain markdown (no frontmatter) - Flattened command names that collide are deduplicated (
plan,plan-2, etc.) - Skills copied to
.cursor/skills/(identical format) - MCP servers written to
.cursor/mcp.jsonwith backup of existing file - Content transformation rewrites
.claude/and~/.claude/paths to.cursor/and~/.cursor/ /workflows:plantransformed to/plan(flat command names)Task agent-name(args)transformed to natural-language skill reference- Plugins with hooks emit
console.warnabout unsupported hooks - Writer does not double-nest
.cursor/.cursor/(follows droid writer pattern) modelandallowedToolsfields silently dropped (no Cursor equivalent)- Converter and writer tests pass
- Existing tests still pass (
bun test)
Implementation
Phase 1: Types
Create src/types/cursor.ts
export type CursorRule = {
name: string
content: string // Full .mdc file with YAML frontmatter
}
export type CursorCommand = {
name: string
content: string // Plain markdown (no frontmatter)
}
export type CursorSkillDir = {
name: string
sourceDir: string
}
export type CursorBundle = {
rules: CursorRule[]
commands: CursorCommand[]
skillDirs: CursorSkillDir[]
mcpServers?: Record<string, {
command?: string
args?: string[]
env?: Record<string, string>
url?: string
headers?: Record<string, string>
}>
}
Phase 2: Converter
Create src/converters/claude-to-cursor.ts
Core functions:
-
convertClaudeToCursor(plugin, options)-- main entry point- Convert each agent to a
.mdcrule viaconvertAgentToRule() - Convert each command (including
disable-model-invocationones) viaconvertCommand() - Pass skills through as directory references
- Convert MCP servers to JSON-compatible object
- Emit
console.warnifplugin.hookshas entries
- Convert each agent to a
-
convertAgentToRule(agent, usedNames)-- agent ->.mdcrule- Frontmatter fields:
description(from agent description),globs: "",alwaysApply: false - Body: agent body with content transformations applied
- Prepend capabilities section if present
- Deduplicate names via
uniqueName() - Silently drop
modelfield (no Cursor equivalent)
- Frontmatter fields:
-
convertCommand(command, usedNames)-- command -> plain.md- Flatten namespace:
workflows:plan->plan - Deduplicate flattened names via
uniqueName() - Emit as plain markdown: description as
<!-- description -->comment, then body - Include
argument-hintas a## Argumentssection if present - Body: apply
transformContentForCursor()transformations - Silently drop
allowedTools(no Cursor equivalent)
- Flatten namespace:
-
transformContentForCursor(body)-- content rewriting.claude/->.cursor/and~/.claude/->~/.cursor/Task agent-name(args)->Use the agent-name skill to: args(same as codex)/workflows:command->/command(flatten slash commands)@agent-namereferences ->the agent-name rule(use codex's suffix-matching pattern)- Skip file paths (containing
/) and common non-command patterns
-
convertMcpServers(servers)-- MCP config- Map each
ClaudeMcpServerentry to Cursor-compatible JSON - Pass through:
command,args,env,url,headers - Drop
typefield (Cursor infers transport fromcommandvsurl)
- Map each
Phase 3: Writer
Create src/targets/cursor.ts
Output structure:
.cursor/
├── rules/
│ ├── agent-name-1.mdc
│ └── agent-name-2.mdc
├── commands/
│ ├── command-1.md
│ └── command-2.md
├── skills/
│ └── skill-name/
│ └── SKILL.md
└── mcp.json
Core function: writeCursorBundle(outputRoot, bundle)
resolveCursorPaths(outputRoot)-- detect if path already ends in.cursorto avoid double-nesting (follow droid writer pattern atsrc/targets/droid.ts:31-50)- Write rules to
rules/as.mdcfiles - Write commands to
commands/as.mdfiles - Copy skill directories to
skills/viacopyDir() - Write
mcp.jsonviawriteJson()withbackupFile()for existing files
Phase 4: Wire into CLI
Modify src/targets/index.ts
import { convertClaudeToCursor } from "../converters/claude-to-cursor"
import { writeCursorBundle } from "./cursor"
import type { CursorBundle } from "../types/cursor"
// Add to targets:
cursor: {
name: "cursor",
implemented: true,
convert: convertClaudeToCursor as TargetHandler<CursorBundle>["convert"],
write: writeCursorBundle as TargetHandler<CursorBundle>["write"],
},
Modify src/commands/convert.ts
- Update
--todescription:"Target format (opencode | codex | droid | cursor)" - Add to
resolveTargetOutputRoot:if (targetName === "cursor") return path.join(outputRoot, ".cursor")
Modify src/commands/install.ts
- Same two changes as convert.ts
Phase 5: Tests
Create tests/cursor-converter.test.ts
Test cases (use inline ClaudePlugin fixtures, following codex converter test pattern):
- Agent converts to rule with
.mdcfrontmatter (alwaysApply: false,descriptionpopulated) - Agent with empty description gets default description text
- Agent with capabilities prepended to body
- Agent
modelfield silently dropped - Agent with empty body gets default body text
- Command converts with flattened name (
workflows:plan->plan) - Command name collision after flattening is deduplicated (
plan,plan-2) - Command with
disable-model-invocationis still included - Command
allowedToolssilently dropped - Command with
argument-hintgets Arguments section - Skills pass through as directory references
- MCP servers convert to JSON config (local and remote)
- MCP
headerspass through for remote servers - Content transformation:
.claude/paths ->.cursor/ - Content transformation:
~/.claude/paths ->~/.cursor/ - Content transformation:
Task agent(args)-> natural language - Content transformation: slash commands flattened
- Hooks present ->
console.warnemitted - Plugin with zero agents produces empty rules array
- Plugin with only skills works correctly
Create tests/cursor-writer.test.ts
Test cases (use temp directories, following droid writer test pattern):
- Full bundle writes rules, commands, skills, mcp.json
- Rules written as
.mdcfiles inrules/directory - Commands written as
.mdfiles incommands/directory - Skills copied to
skills/directory - MCP config written as valid JSON
mcp.json - Existing
mcp.jsonis backed up before overwrite - Output root already ending in
.cursordoes NOT double-nest - Empty bundle (no rules, commands, skills, or MCP) produces no output
Phase 6: Documentation
Create docs/specs/cursor.md
Document the Cursor CLI spec as a reference, following docs/specs/codex.md pattern:
- Rules format (
.mdcwithdescription,globs,alwaysApplyfrontmatter) - Commands format (plain markdown, no frontmatter)
- Skills format (identical SKILL.md standard)
- MCP server configuration (
.cursor/mcp.json) - CLI permissions (
.cursor/cli.json-- for reference, not converted) - Config file locations (project-level vs global)
Update README.md
Add cursor to the supported targets in the CLI usage section.
What We're NOT Doing
- Not converting hooks (Cursor has no hook system -- warn and skip)
- Not generating
.cursor/cli.jsonpermissions (user-specific, not plugin-scoped) - Not creating
AGENTS.md(Cursor reads it natively, but not part of plugin conversion) - Not using
globsfield intelligently (would require analyzing agent content to guess file patterns) - Not adding sync support (follow-up task)
- Not transforming content inside copied SKILL.md files (known limitation -- skills may reference
.claude/paths internally) - Not clearing old output before writing (matches existing target behavior -- re-runs accumulate)
Complexity Assessment
This is a medium change. The converter architecture is well-established with three existing targets, so this is mostly pattern-following. The key novelties are:
- The
.mdcfrontmatter format (different from all other targets) - Agents map to "rules" rather than a direct equivalent
- Commands are plain markdown (no frontmatter) unlike other targets
- Name deduplication needed for flattened command namespaces
Skills being identical across platforms simplifies things significantly. MCP config is nearly 1:1.
References
- Cursor Rules:
.cursor/rules/*.mdcwithdescription,globs,alwaysApplyfrontmatter - Cursor Commands:
.cursor/commands/*.md(plain markdown, no frontmatter) - Cursor Skills:
.cursor/skills/*/SKILL.md(open standard, identical to Claude Code) - Cursor MCP:
.cursor/mcp.jsonwithmcpServerskey - Cursor CLI:
cursor-agentcommand (launched August 2025) - Existing codex converter:
src/converters/claude-to-codex.ts(hasuniqueName()deduplication pattern) - Existing droid writer:
src/targets/droid.ts(has double-nesting guard pattern) - Existing codex plan:
docs/plans/2026-02-08-feat-convert-local-md-settings-for-opencode-codex-plan.md - Target provider checklist:
AGENTS.mdsection "Adding a New Target Provider"