Cross-Tool Interoperability
Write skills, rules, and commands that work across Claude Code, OpenCode, Cursor, and Cline without duplication.
Quick Start
All four tools canonically search .claude/skills/ per the spec. However, Cursor has several bugs that require a workaround — see Cursor Bugs below.
In practice, always write new skills to .cursor/skills/ and symlink .claude/skills/ to it:
# One-time setup per project
ln -sfn ../.cursor/skills .claude/skills
Skills use combined frontmatter that each tool reads selectively:
---
name: my-skill
description: "Does X. Use when user asks to Y or mentions Z."
user-invocable: true
disable-model-invocation: false
---
Always set both invocation fields explicitly for visible defaults across all tools.
Core Strategy
Skills: Fully Shareable
The spec says .claude/skills/ is the canonical location. In practice, Cursor bugs (see below) require the physical files to live in .cursor/skills/, with .claude/skills/ as a symlink.
Always write new skills to .cursor/skills/. The symlink makes Claude Code and other tools pick them up automatically.
Create one SKILL.md with combined frontmatter. Each tool reads what it understands, ignores the rest.
Notes:
.cursor/skills/holds the physical files — always write here.claude/skills/is a symlink to.cursor/skills/— never write directly to it- Cline skills require enabling in Settings → Features → Enable Skills (experimental)
- Skills can be invoked via
/<skill-name>slash commands in Claude Code, Cursor IDE, and Cursor CLI
Cursor Bugs
Cursor has three known issues that drive the .cursor/skills/ workaround:
-
Doesn't search
.claude/skills/— Despite the spec, Cursor IDE does not reliably read skills from.claude/skills/. It only reliably finds skills in.cursor/skills/. -
Doesn't traverse above the open directory — Cursor only searches for skills within the directory the IDE is opened to. It won't walk up to the git root. In a monorepo where the IDE is opened to a sub-project, skills defined at the repo root are invisible. Workaround: symlink the
.cursor/directory into each sub-project that needs access. -
Skills don't survive two symlinks — If
.claude/skills/is a symlink and the skills inside are themselves symlinks (e.g., pointing up to a parent directory), Cursor fails to read them. Skills must be physically present at the resolved path, with at most one level of symlink indirection.
Consequence: Skills must be physically in .cursor/skills/. The symlink goes in the other direction: .claude/skills/ → .cursor/skills/.
Rules: Symlink Between Names
Claude Code uses CLAUDE.md. OpenCode, Cursor, and Cline use AGENTS.md. Content is identical—only the filename differs.
# Project level: pick one as source, symlink the other
ln -s AGENTS.md CLAUDE.md # or vice versa
# Global level
ln -s ~/dotfiles/AGENTS.md ~/.claude/CLAUDE.md
ln -s ~/dotfiles/AGENTS.md ~/.config/opencode/AGENTS.md
Cline also supports a .clinerules/ folder with multiple rule files. For portability, use AGENTS.md.
Cursor Rules Bug: .mdc Extension Required
Cursor currently only detects .mdc files in .cursor/rules/, not .md files, despite documentation stating both formats are supported. This breaks the symlink strategy for modular rules.
Temporary workaround (until bug is fixed):
- Maintain separate
.cursor/rules/*.mdcfiles alongside.claude/rules/*.md - Keep content synchronized between the two (manually or via script)
- Use identical frontmatter (Claude reads
paths:, Cursor readsglobs:, both ignore unknown keys) - When Cursor fixes the bug, delete
.cursor/rules/*.mdcand replace with symlink:.cursor/rules/→.claude/rules/
Note: This only affects modular rules in .cursor/rules/. Root-level AGENTS.md works fine in Cursor without needing .mdc extension.
Claude Code Rules Extensions
Claude Code supports several rules features beyond basic CLAUDE.md. These are Claude Code-only but degrade gracefully (other tools simply ignore them).
CLAUDE.local.md — Personal project-specific preferences. Auto-added to .gitignore. Place next to CLAUDE.md for private overrides (sandbox URLs, test data, personal workflow notes).
CLAUDE.md imports — Use @path/to/file syntax to import other files into CLAUDE.md:
See @README for project overview and @docs/git-workflow.md for git conventions.
Relative paths resolve from the importing file. Recursive imports supported (max depth 5). Other tools see @path as literal text—harmless but not functional.
Modular rules with .claude/rules/ — Split rules into focused files:
.claude/rules/
├── code-style.md # Unconditional: always loaded
├── testing.md
└── api-design.md # Can use paths: frontmatter for file-scoped rules
All .md files load automatically. Rules can be scoped to specific files via paths frontmatter with glob patterns. Cursor (.cursor/rules/ with globs) and Cline (.clinerules/ with paths) have similar systems but use their own directories — these are not portable across tools.
Cursor bug: Cursor currently requires .mdc extension for rules in .cursor/rules/, not .md. See Cursor Rules Bug section.
Commands: Use Skills Instead
Claude Code has merged commands into skills — .claude/commands/ files still work but skills take precedence if names collide. For maximum portability, write skills instead of commands. They work as:
- Slash commands in Claude Code, Cursor IDE, and Cursor CLI (
/<skill-name>) - Auto-discovered capabilities in OpenCode and Cline
Migration: Cursor 2.4+ includes /migrate-to-skills to convert existing commands to skills with disable-model-invocation: true.
Cline has a separate "workflows" system (.clinerules/workflows/), but skills are more portable.
OpenCode: Command Wrappers for Slash Invocation
OpenCode skills are NOT slash-invocable — they are loaded on-demand by the agent via the skill tool. To get /skill-name slash invocation in OpenCode, create a thin command wrapper that tells the agent to load the skill.
Place command wrappers in .opencode/commands/ (project) or ~/.config/opencode/commands/ (global):
<!-- .opencode/commands/my-skill.md -->
---
description: Short description of what the skill does
---
Load the `my-skill` skill and follow its instructions. $ARGUMENTS
The filename becomes the command name (e.g., my-skill.md → /my-skill). The agent receives the prompt, calls the skill tool to load the SKILL.md content, and follows it.
When creating a new skill, ALWAYS create a matching OpenCode command wrapper. If commands are symlinked between ~/.claude/commands/ and ~/.config/opencode/commands/, one file serves both Claude Code (as a command) and OpenCode (as a slash command).
Key differences from skills:
- Commands support
$ARGUMENTSand positional args ($1,$2) - Commands support
!command`` for shell output injection - Commands support
@filenamefor file content inclusion - Commands can specify
agent:andmodel:overrides in frontmatter
Writing Portable Skills
Required Fields (All Tools)
| Field | Requirements |
|-------|-------------|
| name | 1-64 chars, lowercase, hyphens only, must match folder name, pattern: ^[a-z0-9]+(-[a-z0-9]+)*$ |
| description | Max 1024 chars, third person, include trigger phrases |
| user-invocable | Always set to true (Claude Code: show in slash menu) |
| disable-model-invocation | Always set to false (Cursor: allow auto-discovery) |
Invocation Control Fields (Required)
Always set both fields explicitly for visible defaults. All skills should be user-invokable and auto-discoverable:
user-invocable: true # Claude Code: show in slash menu
disable-model-invocation: false # Cursor: allow auto-discovery
| Field | Tool | Value | Effect |
|-------|------|-------|--------|
| user-invocable: true | Claude Code | Required | Show in slash menu, allow /<skill-name> |
| disable-model-invocation: false | Cursor | Required | Allow auto-discovery based on description |
Note: These fields have opposite semantics but setting both explicitly ensures consistent behavior:
- Claude Code reads
user-invocable, ignoresdisable-model-invocation - Cursor reads
disable-model-invocation, ignoresuser-invocable - OpenCode and Cline ignore both (auto-discover via
skilltool; for slash commands in OpenCode, use command wrappers)
Optional Fields (Include Only When Specified)
Do not add these fields by default. Only include when the user explicitly requests them:
allowed-tools- Restricts tool access (Claude Code, experimental in Cursor)license- Software license (informational, recognized by Cursor + OpenCode)metadata- Author, version info (informational, recognized by Cursor + OpenCode)compatibility- Environment requirements (recognized by Cursor + OpenCode)
These fields degrade gracefully—tools ignore what they don't recognize.
Example with all possible fields (only add what you need):
---
name: review-code
description: "Reviews code for quality and bugs. Use when asked to review changes."
# === REQUIRED: Invocation control (always set explicitly) ===
user-invocable: true # Claude Code: show in slash menu
disable-model-invocation: false # Cursor: allow auto-discovery
# === OPTIONAL: Add only when specified ===
# Shared (Cursor + OpenCode + Agent Skills spec)
license: MIT
compatibility: "requires git"
metadata:
author: your-name
version: 1.0.0
# Claude Code + Cursor (experimental)
allowed-tools: Read, Grep, Glob
# Claude Code only
model: claude-sonnet-4-20250514
context: fork
agent: Explore
argument-hint: "[file-or-directory]"
---
File Structure
Use the same structure across all tools:
skill-name/
├── SKILL.md # Required: instructions + combined frontmatter
├── scripts/ # Optional: executable code agents can run
├── references/ # Optional: detailed docs (loaded on-demand)
├── examples/ # Optional: usage examples
└── assets/ # Optional: templates, images, data files (Cursor)
Setting Up Global Interoperability
Physical skills live in .cursor/skills/. All other tool locations symlink to it.
# Skills: physical files in .cursor/skills
mkdir -p ~/.cursor/skills
# Claude Code: symlink .claude/skills → .cursor/skills
ln -sfn ~/.cursor/skills ~/.claude/skills
# OpenCode: symlink to .cursor/skills
ln -sfn ~/.cursor/skills ~/.config/opencode/skills
# Cline: symlink to .cursor/skills
mkdir -p ~/.cline
ln -sfn ~/.cursor/skills ~/.cline/skills
# Rules: single source of truth
ln -sf ~/dotfiles/AGENTS.md ~/.claude/CLAUDE.md
ln -sf ~/dotfiles/AGENTS.md ~/.config/opencode/AGENTS.md
For project-level skills in a monorepo, symlink .cursor/ into each sub-project so Cursor can find it (it won't traverse above the open directory):
# In each sub-project that needs access to skills
ln -sfn ../.cursor .cursor # if sub-project is one level deep
ln -sfn ../.cursor/skills .claude/skills
See scripts/setup-interop.sh for full automation.
Hard Limitations
| Limitation | Impact | Workaround |
|------------|--------|------------|
| hooks, context, agent | Claude Code only | Degrade gracefully |
| allowed-tools | Claude Code + Cursor (experimental) | Degrade gracefully in OpenCode/Cline |
| !`command` dynamic injection | Claude Code skills + OpenCode commands | Appears as literal text in Cursor/Cline |
| $ARGUMENTS expansion | Claude Code + OpenCode expand | Cursor/Cline show unexpanded |
| Positional arg indexing | Claude Code: 0-based ($0, $1); OpenCode: 1-based ($1, $2) | Avoid positional args in portable skills |
| ${CLAUDE_SESSION_ID} | Claude Code only | Other tools show unexpanded |
| /<skill-name> invocation | Claude Code + Cursor (IDE & CLI) | OpenCode: create command wrapper (see above). Cline: auto-discovery only |
| @path imports in CLAUDE.md | Claude Code only | Other tools see literal @path text |
| CLAUDE.local.md | Claude Code only | Other tools ignore it; no equivalent |
| File-scoped rules | Claude Code (paths), Cursor (globs), Cline (paths) — each in own rule dir | Not portable across tools; use tool-specific rule dirs |
| Cursor .md rules bug | Cursor only detects .mdc in .cursor/rules/, not .md | Duplicate rules as .mdc until bug fixed |
| Cline skills experimental | Must enable in settings | Toggle in Settings → Features |
| Cline .clinerules/ folder | Cline-specific rules system | Use AGENTS.md for portability |
| Cline global skill precedence | Global overrides project skills | Use unique names across locations |
Validation Checklist
Before finalizing a portable skill:
- [ ]
nameis lowercase with hyphens only, matches folder name - [ ]
descriptionis under 1024 chars, third person - [ ]
descriptionincludes trigger phrases - [ ]
user-invocable: trueis set explicitly - [ ]
disable-model-invocation: falseis set explicitly - [ ] SKILL.md body uses imperative form
- [ ] No tool-specific features in core logic
- [ ] Tool-specific frontmatter clearly commented
- [ ] OpenCode command wrapper created (if slash invocation needed)
- [ ] Tested in target tools
References
Tool-Specific Documentation
- references/claude-code.md - Claude Code configuration and capabilities
- references/opencode.md - OpenCode configuration and capabilities
- references/cursor.md - Cursor configuration and capabilities
- references/cline.md - Cline configuration and capabilities
Examples and Scripts
- examples/universal-skill.md - Complete portable skill template
- scripts/setup-interop.sh - Automated setup script
微信扫一扫