Skip to content

Cross-Provider AI Skills: Building for Cursor, Claude Code, Gemini, and Codex

I wanted to create AI skills that work across Cursor, Claude Code, Gemini CLI, and Codex CLI. Then I discovered each tool has completely different format requirements.

The Problem

I started writing a skill file with full metadata—arguments, user-invokable flags, compatibility info. It worked great in Claude Code. Then I tried it in Cursor.

Nothing worked. Cursor ignored my frontmatter completely.

I checked Gemini CLI. Different format entirely—TOML for commands, different placeholder syntax.

Codex CLI? Custom prompts format with $ARGNAME instead of {{arg}}.

Each AI development tool speaks a different dialect. Building for all of them means either:

  1. Limiting to the lowest common denominator ( Cursor has no frontmatter support)
  2. Maintaining separate versions for each provider

Neither option felt right.

Provider Format Differences

After digging through documentation and experimenting, here’s what I found:

Claude Code and OpenCode

Full YAML frontmatter support with complete metadata:

skill.md
---
name: my-skill
description: What this skill does
user-invokable: true
args:
- name: target
description: Focus area
required: false
license: Apache 2.0
---
Skill instructions go here...

Claude Code supports the full Agent Skills specification. You get arguments, user-invokable flags, allowed-tools, license, and compatibility fields.

Cursor

This is where things get painful. Cursor:

  • Does NOT support frontmatter for commands
  • Agent Skills require the nightly channel
  • Strips metadata entirely

If you want Cursor compatibility, you’re stuck with plain markdown. No args. No metadata.

Gemini CLI

Gemini uses TOML for commands and a special @file.md import syntax for skills:

commands.toml
[skills.my-skill]
command = "skill"
file = ".gemini/skills/my-skill/SKILL.md"

The skill file uses minimal frontmatter. And placeholder syntax differs—{{arg}} becomes {{args}}.

Codex CLI

Codex uses a custom prompts format:

prompt.txt
# Arguments
TARGET: {{target}}
Skill instructions here with $TARGET as the placeholder...

The {{arg}} syntax transforms to $ARGNAME in Codex. Completely different approach.

Summary Table

Provider Capabilities
┌──────────────┬─────────────────────────────────────────────────────┐
│ Provider │ Capabilities │
├──────────────┼─────────────────────────────────────────────────────┤
│ Claude Code │ Full metadata, args, user-invokable, license │
│ OpenCode │ Same as Claude Code │
│ Codex CLI │ Args converted to argument-hint format │
│ Gemini CLI │ Minimal frontmatter, {{arg}} → {{args}} │
│ Cursor │ Basic frontmatter only (name, description) │
│ Kiro │ Basic frontmatter │
│ Pi │ Basic frontmatter with license/compatibility │
└──────────────┴─────────────────────────────────────────────────────┘

The Solution: Build-Time Transformation

Instead of limiting all skills to Cursor’s minimal format, I built a transformation system.

Key insight: Author once with full metadata, generate provider-specific versions during build.

Source Format

I write skills in a feature-rich source format:

source/skills/review/SKILL.md
---
name: code-review
description: Perform comprehensive code review
user-invokable: true
args:
- name: target
description: File or directory to review
required: false
- name: style
description: Review style (security, performance, readability)
required: false
license: Apache 2.0
compatibility:
- claude-code
- cursor
- gemini
- codex
---
## Code Review Skill
Perform a thorough review of {{target}} (default: current directory).
### Steps
1. Analyze code structure in {{target}}
2. Check for {{style}} issues if specified
3. Report findings with severity levels
4. Suggest concrete improvements
### Output Format
Provide findings in this structure:
- **CRITICAL**: Security vulnerabilities, data loss risks
- **HIGH**: Bugs, performance issues
- **MEDIUM**: Code smell, maintainability
- **LOW**: Style suggestions, minor improvements

Build System

The build system transforms this source into provider-specific outputs:

build.sh
# Build all providers
bun run build
# Build specific provider
bun run build:cursor
bun run build:claude-code

The output structure:

Output Structure
dist/
├── cursor/
│ └── .cursor/skills/review/SKILL.md
├── claude-code/
│ └── .claude/skills/review/SKILL.md
├── gemini/
│ ├── .gemini/skills/review/SKILL.md
│ └── .gemini/commands.toml
├── codex/
│ └── .codex/skills/review/prompt.txt
└── opencode/
└── .claude/skills/review/SKILL.md

Transformer Logic

Each provider has a transformer in scripts/lib/transformers/:

cursor-transformer.js
export function transformForCursor(skill) {
// Cursor: strip frontmatter, keep content
return {
path: `.cursor/skills/${skill.name}/SKILL.md`,
content: skill.body // Just the markdown body
}
}
claude-code-transformer.js
export function transformForClaudeCode(skill) {
// Claude Code: keep full frontmatter
return {
path: `.claude/skills/${skill.name}/SKILL.md`,
content: `---
name: ${skill.name}
description: ${skill.description}
user-invokable: ${skill.userInvokable}
args:
${skill.args.map(a => ` - name: ${a.name}\n description: ${a.description}`).join('\n')}
---
${skill.body}`
}
}
codex-transformer.js
export function transformForCodex(skill) {
// Codex: convert {{arg}} to $ARGNAME
let content = skill.body
skill.args.forEach(arg => {
content = content.replace(
new RegExp(`{{${arg.name}}}`, 'g'),
`$${arg.name.toUpperCase()}`
)
})
return {
path: `.codex/skills/${skill.name}/prompt.txt`,
content: `# Arguments\n${skill.args.map(a => `${a.name.toUpperCase()}: {{${a.name}}}`).join('\n')}\n\n${content}`
}
}

Why This Approach Works

  1. Single source of truth — One skill file to maintain, not four
  2. Feature richness where supported — Claude Code gets full metadata, Cursor gets what it can handle
  3. Automatic consistency — Build process ensures all versions stay in sync
  4. Extensibility — Adding a new provider means writing one transformer

The alternative—maintaining separate files per provider—is a maintenance nightmare. Every bug fix or improvement requires editing multiple files. With transformation, you edit once, build everywhere.

Practical Usage

My workflow now looks like this:

workflow.sh
# 1. Write skill in source format
vim source/skills/new-feature/SKILL.md
# 2. Build all providers
bun run build
# 3. Copy to target project
cp -r dist/claude-code/.claude ~/projects/my-app/
# or
cp -r dist/cursor/.cursor ~/projects/my-app/

For projects using multiple AI tools, I can generate and deploy the appropriate format instantly.

The Agent Skills specification from Anthropic is becoming a de facto standard. Tools like Claude Code and OpenCode already implement it. If you’re building skills today, authoring against this spec and transforming for other providers is the most future-proof approach.

The key is accepting that providers will always have different capabilities. Rather than fighting it or compromising, embrace transformation as an architectural solution.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments