Claude ↔ Codex Skill Porter
[Created by Opus: 3bad6cf7-0552-419a-92b7-5bc7cbeac4c8]
Generated: 2026-01-05
Claude Code: v2.0.76 (~/swe/claude-code-2.0.76/cli.js)
Codex CLI: v0.77.0 (~/swe/codex.0.77.0/codex-rs/target/release/codex)
TL;DR for Agents
Can I just cp -r?
Yes, if the skill follows the skillname/SKILL.md directory structure.
# User-level cp -r ~/.claude/skills/my-skill ~/.codex/skills/ cp -r ~/.codex/skills/my-skill ~/.claude/skills/ # Project-level cp -r .claude/skills/my-skill .codex/skills/ cp -r .codex/skills/my-skill .claude/skills/
Gotchas Checklist (MUST verify before copying)
| Check | Behavior | Fix |
|---|---|---|
| Skill is a symlink | Codex: Silently ignored | Use real copy, not symlink |
Directory starts with . | Codex: Silently ignored | Remove leading dot from dirname |
name field > 64 chars | Codex: Rejected with error | Truncate name |
description > 1024 chars | Codex: Rejected with error | Truncate description |
Loose .md file (no wrapper dir) | Codex: Not discovered | Wrap in skillname/SKILL.md structure |
File not named SKILL.md exactly | Both: Not discovered | Rename to SKILL.md (case-sensitive) |
| Claude home dir varies | Claude: Wrong path = not found | Check $CLAUDE_CONFIG_DIR; default is ~/.claude |
Quick Validation Command
# Check name/description lengths before copying to Codex
awk '/^---$/,/^---$/' ~/.claude/skills/my-skill/SKILL.md | \
grep -E '^(name|description):' | \
while read line; do
field=$(echo "$line" | cut -d: -f1)
value=$(echo "$line" | cut -d: -f2-)
len=${#value}
if [[ "$field" == "name" && $len -gt 64 ]]; then
echo "WARNING: name is $len chars (max 64)"
elif [[ "$field" == "description" && $len -gt 1024 ]]; then
echo "WARNING: description is $len chars (max 1024)"
fi
done
Skill Paths by Level
Claude Code
| Level | Path | Notes |
|---|---|---|
| User | ~/.claude/skills/ | Default Claude Code config |
| Project | .claude/skills/ | Repo root or nested |
| Plugin | ~/.claude/plugins/marketplaces/.../plugins/{name}/skills/ | Via plugin system |
Codex CLI
| Level | Path | Scope Enum | Priority |
|---|---|---|---|
| Repo | $REPO_ROOT/.codex/skills/ | SkillScope::Repo | 1 (highest) |
| User | ~/.codex/skills/ | SkillScope::User | 2 |
| System | ~/.codex/skills/.system/ | SkillScope::System | 3 |
| Admin | /etc/codex/skills/ (Unix only) | SkillScope::Admin | 4 (lowest) |
Deduplication: By skill name. First match wins (repo > user > system > admin).
Format Comparison
Both use identical core format:
--- name: my-skill description: When to use this skill... --- # Markdown body here
Field Differences
| Field | Claude | Codex |
|---|---|---|
name | Required, no length limit | Required, max 64 chars |
description | Required, no length limit | Required, max 1024 chars |
version | Optional, ignored by Codex | Ignored |
license | Optional, ignored by Codex | Ignored |
metadata.short-description | Ignored | Optional, max 1024 chars |
Conversion Rules
Claude → Codex:
- •Ensure
name≤ 64 characters - •Ensure
description≤ 1024 characters - •Remove
version/license(optional, Codex ignores them anyway) - •Optionally add
metadata.short-description
Codex → Claude:
- •Remove
metadata.short-description(optional, Claude ignores it) - •Optionally add
version,license
Gotcha Details
1. Symlinks Are Silently Ignored (Codex only)
Source code evidence:
- •File:
codex-rs/core/src/skills/loader.rs - •Search:
file_type.is_symlink() - •Lines 207-209:
if file_type.is_symlink() {
continue;
}
Symptom: Skill doesn't appear in Codex skill list, no error message.
Fix: Use cp -r instead of ln -s.
2. Hidden Directories Skipped (Codex only)
Source code evidence:
- •File:
codex-rs/core/src/skills/loader.rs - •Search:
file_name.starts_with('.') - •Lines 199-201:
if file_name.starts_with('.') {
continue;
}
Symptom: Skill in .my-skill/SKILL.md not discovered.
Fix: Rename directory to remove leading dot.
Exception: .system/ is explicitly handled for system skills.
3. Name Length Limit (Codex only)
Source code evidence:
- •File:
codex-rs/core/src/skills/loader.rs - •Search:
MAX_NAME_LEN - •Line 37:
const MAX_NAME_LEN: usize = 64; - •Line 252:
validate_field(&name, MAX_NAME_LEN, "name")?;
Symptom: Error in skill loading: invalid name: exceeds maximum length of 64 characters
Fix: Truncate name to 64 characters.
4. Description Length Limit (Codex only)
Source code evidence:
- •File:
codex-rs/core/src/skills/loader.rs - •Search:
MAX_DESCRIPTION_LEN - •Line 38:
const MAX_DESCRIPTION_LEN: usize = 1024; - •Line 253:
validate_field(&description, MAX_DESCRIPTION_LEN, "description")?;
Symptom: Error: invalid description: exceeds maximum length of 1024 characters
Fix: Truncate or summarize description.
5. Whitespace Normalization (Codex only)
Source code evidence:
- •File:
codex-rs/core/src/skills/loader.rs - •Search:
sanitize_single_line - •Lines 273-275:
fn sanitize_single_line(raw: &str) -> String {
raw.split_whitespace().collect::<Vec<_>>().join(" ")
}
Effect: Newlines and extra spaces in name/description collapsed to single spaces.
Impact: Usually harmless, but be aware multiline YAML descriptions become single-line.
6. Loose .md Files (Claude allows, Codex doesn't)
Claude: Allows ~/.claude/skills/my-notes.md (file directly in skills dir)
Codex: Requires ~/.codex/skills/my-skill/SKILL.md (directory wrapper)
Source code evidence:
- •File:
codex-rs/core/src/skills/loader.rs - •Search:
SKILLS_FILENAME - •Line 33:
const SKILLS_FILENAME: &str = "SKILL.md"; - •Line 216:
if file_type.is_file() && file_name == SKILLS_FILENAME {
Fix: Wrap loose file in directory:
mkdir ~/.codex/skills/my-notes mv my-notes.md ~/.codex/skills/my-notes/SKILL.md
7. Case-Sensitive Filename (Both)
Codex requires: SKILL.md exactly (uppercase)
Fix: Rename skill.md → SKILL.md
8. Claude Home Directory (Claude only)
Default: Claude Code's home directory is ~/.claude/
How to detect: Check the CLAUDE_CONFIG_DIR environment variable:
echo $CLAUDE_CONFIG_DIR # If empty → ~/.claude/
What agents must do:
- •Before copying skills, determine the active Claude home:
bash
CLAUDE_HOME="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" - •Use the correct path for skill operations:
bash
cp -r ~/.codex/skills/my-skill "$CLAUDE_HOME/skills/"
- •When creating new skills for Claude, install to
~/.claude/skills/
FAQ: Top 5 Agent Questions
Q1: "Can I copy the entire skills directory at once?"
Yes, with caveats:
cp -r ~/.claude/skills/* ~/.codex/skills/
But run validation afterward—any skill with oversized name/description will fail silently in Codex.
Q2: "The skill copied but doesn't appear in Codex. Why?"
Check in order:
- •Is it a symlink? → Use real copy
- •Does directory start with
.? → Rename - •Is file named exactly
SKILL.md? → Rename - •Is skill inside a wrapper directory? → Create one
- •Check
name/descriptionlengths
Q3: "Do bundled resources (scripts/, references/, assets/) copy over?"
Yes. Both systems support the same directory structure. cp -r copies everything.
Q4: "What about plugin-based Claude skills?"
Plugin skills live in ~/.claude/plugins/marketplaces/.../plugins/{plugin}/skills/. These use a different discovery mechanism. To port:
- •Copy skill directory to
~/.codex/skills/ - •Verify SKILL.md frontmatter meets Codex requirements
Q5: "How do I verify a skill loaded correctly in Codex?"
# List all discovered skills (requires Codex running) codex --list-skills # Or check for errors during load codex --verbose 2>&1 | grep -i skill
Conversion Script (Optional)
For bulk conversion with validation:
#!/bin/bash
# Usage: ./convert-skill.sh <source> <dest>
# Example: ./convert-skill.sh ~/.claude/skills/my-skill ~/.codex/skills/my-skill
src="$1"
dest="$2"
# Validate source
if [[ ! -f "$src/SKILL.md" ]]; then
echo "ERROR: $src/SKILL.md not found"
exit 1
fi
# Extract and validate fields
name=$(awk '/^---$/,/^---$/' "$src/SKILL.md" | grep '^name:' | cut -d: -f2- | xargs)
desc=$(awk '/^---$/,/^---$/' "$src/SKILL.md" | grep '^description:' | cut -d: -f2- | xargs)
if [[ ${#name} -gt 64 ]]; then
echo "WARNING: name is ${#name} chars (max 64 for Codex)"
fi
if [[ ${#desc} -gt 1024 ]]; then
echo "WARNING: description is ${#desc} chars (max 1024 for Codex)"
fi
# Copy
cp -r "$src" "$dest"
echo "Copied $src → $dest"
Source Code Reference
| Component | File | Key Functions/Constants |
|---|---|---|
| Codex loader | codex-rs/core/src/skills/loader.rs | load_skills, parse_skill_file, discover_skills_under_root |
| Codex constants | codex-rs/core/src/skills/loader.rs:33-39 | SKILLS_FILENAME, MAX_NAME_LEN, MAX_DESCRIPTION_LEN |
| Codex validation | codex-rs/core/src/skills/loader.rs:277-292 | validate_field, sanitize_single_line |
| Codex scope enum | codex-rs/protocol/src/protocol.rs:1717-1725 | SkillScope::{User,Repo,System,Admin} |
| Claude loader | cli.js (compiled) | Search: skillsPath, d62(), m62() |