skill-link

SKILL.md

skill-link — Manage Extensions for Claude Code & Codex CLI

Install, list, health-check, and uninstall skills, commands, and agents via symlinks.

Requires environment variables (set in shell profile):

export CLAUDE_HOME="${CLAUDE_HOME:-$HOME/.claude}"
export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"

Subcommands

Subcommand Trigger Description
(default) /skill-link Install extensions from source directory
list /skill-link list List all installed extensions
check /skill-link check Health-check: find broken/stale symlinks
uninstall /skill-link uninstall <name> Remove symlinks for specified extensions

If no subcommand is given, defaults to install.

Extension Types

Type Source pattern Claude Code target Codex CLI target
Skills skills/{name}/ (has SKILL.md) $CLAUDE_HOME/skills/{name} → dir symlink $CODEX_HOME/skills/{name} → dir symlink
Commands commands/{name}.md $CLAUDE_HOME/commands/{name}.md → file symlink N/A (Codex has no commands)
Agents agents/{name}/agent.md $CLAUDE_HOME/agents/{name}.md → file symlink to agent.md N/A (Codex has no agents)

Subcommand: install (default)

Workflow

  1. Get source directory from user (default: current project root)
  2. Scan for:
    • skills/*/SKILL.md → skill directories
    • commands/*.md → command files
    • agents/*/agent.md → agent files
  3. Show discovered extensions with current status, AskUserQuestion to confirm
  4. Create symlinks per the table above
  5. Report results

Linking Logic

src="$(realpath "/path/to/source")"
dst="$CLAUDE_HOME/skills/{name}"

# Check target type before linking
if [ -d "$dst" ] && [ ! -L "$dst" ]; then
    # Target is a real directory, not a symlink — warn user
    echo "WARNING: $dst is a real directory, skipping (use --force to replace)"
else
    ln -sfn "$src" "$dst"
fi

Rules

  • Always resolve source to absolute path with realpath
  • Quote all paths to handle spaces
  • Create target directories if missing
  • If target is a real directory (not a symlink): skip and warn
  • If target is a symlink: ln -sfn safely overwrites it
  • Show status: new / updated / already linked / skipped (real dir)
  • For agents: symlink points to the agent.md file, not the directory

Example

User: /skill-link

Agent: Scanning /Users/me/claude-code-addons/ ...

Skills (3):
  ✓ continuous-learning  → not installed
  - skill-link           → already linked
  ✓ eval-harness         → not installed

Commands (2):
  ✓ peer-review.md       → not installed
  - cr.md                → already linked

Agents (2):
  - code-reviewer.md     → already linked
  ✓ architect.md         → not installed

Install 3 extensions?

User: Yes

Agent:
  Skills:  2 installed to claude + codex
  Commands: 1 installed to claude
  Agents:  0 (all linked)

Done.

Subcommand: list

Scan $CLAUDE_HOME and $CODEX_HOME for all installed symlinks. Shows every extension regardless of whether it came from the current project.

Workflow

  1. Scan these directories for symlinks:
    • $CLAUDE_HOME/skills/ — directory symlinks
    • $CLAUDE_HOME/commands/ — file symlinks
    • $CLAUDE_HOME/agents/ — file symlinks
    • $CODEX_HOME/skills/ — directory symlinks
  2. For each symlink, resolve target with readlink
  3. Display as table with name, type, target path, and health status

Output Format

Installed extensions:

Skills (Claude + Codex):
  roadmap           → /path/to/source/skills/roadmap         ✓ ok
  tech-interview    → /path/to/source/skills/tech-interview   ✓ ok
  cr                → /path/to/source/skills/cr               ✓ ok

Commands (Claude only):
  architect.md      → /path/to/source/commands/architect.md   ✓ ok
  code-invs.md      → /path/to/source/commands/code-invs.md   ✓ ok

Agents (Claude only):
  architect.md      → /path/to/source/agents/architect/agent.md  ✓ ok
  code-reviewer.md  → /path/to/source/agents/code-reviewer/agent.md  ✓ ok

Total: 7 extensions installed

Example

User: /skill-link list
Agent: [displays table above]

Subcommand: check

Health-check all installed symlinks. Detects:

Problem Description How to detect
broken Symlink target no longer exists [ -L "$link" ] && [ ! -e "$link" ]
stale Target exists but is not the expected type (e.g., no SKILL.md in skill dir) Symlink resolves but content is wrong
orphaned Installed in one location but not the other (Claude has it, Codex doesn't) Cross-check Claude vs Codex for skills
duplicate Same name installed from different source directories Compare resolved targets

Workflow

  1. Scan all symlinks in $CLAUDE_HOME/{skills,commands,agents} and $CODEX_HOME/skills
  2. For each symlink, check:
    • Does the target exist? (broken if not)
    • For skills: does target contain SKILL.md? (stale if not)
    • For commands: is target a .md file? (stale if not)
    • For agents: is target a .md file? (stale if not)
    • Is this skill installed in both Claude and Codex? (orphaned if not)
  3. Report problems, suggest fixes
  4. If problems found, AskUserQuestion: "Fix N issues?" with options to auto-fix

Auto-fix Actions

Problem Fix
broken Remove the dead symlink
orphaned Create the missing symlink in the other location
stale Remove + warn user
duplicate Show both, let user choose

Output Format

Health check:

  ✗ exec-plan      BROKEN  → /path/to/skills/exec-plan (target removed)
  ✗ tech-design    BROKEN  → /path/to/skills/tech-design (target removed)
  ! my-skill       ORPHAN  → installed in Claude but not Codex
  ✓ 22 extensions  OK

Found 3 issues. Fix them?

Example

User: /skill-link check

Agent:
  ✗ exec-plan    BROKEN → target directory no longer exists
  ✗ tech-design  BROKEN → target directory no longer exists
  ✓ 29 extensions OK

Remove 2 broken symlinks?

User: Yes

Agent:
  Removed: exec-plan (claude + codex)
  Removed: tech-design (claude + codex)
Done.

Subcommand: uninstall

Remove symlinks for specified extensions. Never deletes source files — only removes symlinks from $CLAUDE_HOME and $CODEX_HOME.

Workflow

  1. Parse extension names from arguments
  2. For each name, find matching symlinks in:
    • $CLAUDE_HOME/skills/{name}
    • $CODEX_HOME/skills/{name}
    • $CLAUDE_HOME/commands/{name}.md
    • $CLAUDE_HOME/agents/{name}.md
  3. Show what will be removed, AskUserQuestion to confirm
  4. Remove symlinks (only if they ARE symlinks, never rm -rf a real directory)
  5. Report results

Safety Rules

  • Only remove symlinks, never real files or directories
  • Check with [ -L "$path" ] before removing
  • If target is a real directory/file, refuse and warn
  • Always confirm with user before removing

Output Format

Will uninstall:
  skill   exec-plan  → claude + codex (2 symlinks)
  skill   tech-design → claude + codex (2 symlinks)

Remove 4 symlinks? Source files are NOT affected.

User: Yes

Agent:
  Removed: exec-plan (claude, codex)
  Removed: tech-design (claude, codex)
Done. Source files in /path/to/skills/ are untouched.

Example

User: /skill-link uninstall exec-plan tech-design

Agent:
  Will remove:
    ~/.claude/skills/exec-plan (symlink)
    ~/.codex/skills/exec-plan (symlink)
    ~/.claude/skills/tech-design (symlink)
    ~/.codex/skills/tech-design (symlink)

  Remove 4 symlinks?

User: Yes

Agent: Removed 4 symlinks. Source files untouched.
Weekly Installs
1
First Seen
12 days ago
Installed on
codex1