Skill

skill-migration

Migrate legacy .claude/commands/ to .claude/skills/ directory structure. Actions: discover, analyze, migrate, audit, validate, fix.

From claude-ecosystem
Install
1
Run in your terminal
$
npx claudepluginhub melodic-software/claude-code-plugins --plugin claude-ecosystem
Tool Access

This skill is limited to using the following tools:

ReadWriteEditBashGlobGrepTaskAskUserQuestion
Supporting Assets
View in Repository
references/migration-patterns.md
Skill Content

Commands-to-Skills Migration

Migrate legacy .claude/commands/*.md flat files to the modern .claude/skills/*/SKILL.md directory structure introduced in Claude Code v2.1.3+. This skill is designed to work in any repository -- whether it has this plugin installed or is a standalone project with legacy commands.

Background

Claude Code unified commands and skills in v2.1.3. The key differences:

ConceptOld (Commands)New (Skills)
Directory.claude/commands/.claude/skills/ or plugins/*/skills/
Filecommand-name.md (flat file)skill-name/SKILL.md (directory with optional references/)
FrontmatterMinimal or noneYAML: name, description, argument-hint, allowed-tools
Invocation/command-name/skill-name or /plugin:skill-name [args]
ArgumentsNot supported$ARGUMENTS parsed by skill, argument-hint in frontmatter
DiscoveryUser-invocable onlyuser-invocable field controls / menu visibility
ContextAlways main threadcontext: fork for isolated execution

Both formats still work at runtime (backwards compatible), but skills are the recommended pattern going forward. This skill helps migrate any codebase from commands to skills.

Portability

This skill works in any repo:

  • Plugin repos with plugins/*/commands/ directories
  • Project repos with .claude/commands/ only
  • Mixed repos with both project and plugin commands
  • Already-migrated repos (audit/validate modes confirm health)

Argument Routing

ActionDescription
discoverFind all commands/ directories and legacy command files in the current repo
analyzeMap commands to skill consolidation groups and suggest naming
migrateExecute the migration: create skill directories, convert files, update references
auditComprehensive post-migration health check using parallel agents
validateQuick validation that no commands were lost in migration
fixAuto-remediate findings from audit (stale refs, naming, missing frontmatter)

Parse $ARGUMENTS to determine the action. Default to discover if no action specified.

Action Workflow

The typical migration workflow is:

discover -> analyze -> migrate -> audit -> fix (if needed) -> validate

For repos that have already been partially migrated:

audit -> fix -> validate

Action: discover

Scan the current repository for legacy command files and directories.

Step 1: Find commands/ directories

Search for any commands/ directories in the repo:

Glob patterns to check:
  .claude/commands/**/*.md
  plugins/*/commands/**/*.md
  **/commands/**/*.md (broader sweep)

Step 2: Identify command files

For each commands/ directory found, catalog every .md file:

  • File name (without extension) = command name
  • Check for YAML frontmatter (some commands had minimal frontmatter)
  • Check for $ARGUMENTS usage (indicates argument-aware command)
  • File size (helps estimate migration effort)
  • Check if a corresponding skill already exists

Step 3: Check for existing skills

For each command found, check if a skill with the same or similar name already exists:

Check paths:
  .claude/skills/<command-name>/SKILL.md
  plugins/*/skills/<command-name>/SKILL.md

Step 4: Report discovery results

Present a table summarizing findings:

## Discovery Report

**Commands found:** N files across M directories

| # | Command | Location | Size | Has Frontmatter | Has $ARGUMENTS | Skill Exists |
|---|---------|----------|------|-----------------|----------------|--------------|
| 1 | deploy  | .claude/commands/deploy.md | 2.4 KB | Yes | No | No |
| 2 | test    | .claude/commands/test.md   | 1.1 KB | No  | No | No |

**Directories to migrate:**
- .claude/commands/ (N files)
- plugins/my-plugin/commands/ (M files)

**Already migrated:** K commands have corresponding skills
**Remaining:** J commands need migration

Action: analyze

Analyze discovered commands and recommend consolidation groups and naming.

Step 1: Run discover (if not already done)

If no discovery data exists in the conversation, run the discover action first.

Step 2: Read each command file

For every command file found, read the content and extract:

  • Purpose: What does this command do?
  • Domain: What area does it belong to? (e.g., testing, deployment, docs)
  • Verb or noun: Is the name a verb (imperative) or noun-phrase?
  • Related commands: Are there other commands that operate on the same resource?
  • Complexity: Simple (< 50 lines) vs complex (> 50 lines, multiple sections)

Step 3: Identify consolidation opportunities

Group commands that share a domain and operate on the same resources:

Consolidation candidates (merge into one skill with argument routing):

Example groups:
  deploy, deploy-staging, deploy-prod  ->  deployment <env>
  test-unit, test-e2e, test-lint       ->  testing <type>
  db-migrate, db-seed, db-reset        ->  database <action>

Rename candidates (verb-form -> noun-phrase):

  run-tests      ->  test-runner
  deploy-app     ->  deployment
  check-types    ->  type-checking
  lint-code      ->  code-linting
  build-docs     ->  documentation-build

Keep as-is (already noun-phrase or standalone):

  code-review    ->  code-review (already noun-phrase)
  onboarding     ->  onboarding (already noun-phrase)

Step 4: Present analysis

## Migration Analysis

### Consolidation Groups

| Group | Commands to Merge | Suggested Skill Name | Actions |
|-------|-------------------|---------------------|---------|
| Testing | test-unit, test-e2e, test-lint | `testing` | unit, e2e, lint |
| Deployment | deploy, deploy-staging | `deployment` | staging, prod |

### Renames (Verb -> Noun-Phrase)

| Current Name | Suggested Name | Reason |
|-------------|----------------|--------|
| run-tests | test-runner | Noun-phrase convention |
| build-docs | documentation-build | Noun-phrase convention |

### Keep As-Is

| Command | Reason |
|---------|--------|
| code-review | Already noun-phrase |
| onboarding | Already noun-phrase |

### Migration Plan Summary
- **Total commands:** N
- **Will consolidate:** M commands into K skills
- **Will rename:** J commands
- **Keep as-is:** L commands
- **Final skill count:** X skills (from N commands)

Ask the user to review and approve the plan before proceeding to migrate.


Action: migrate

Execute the migration based on the analysis. This action creates skill directories, converts command files, and updates references.

Prerequisites

  • Run analyze first to have an approved migration plan
  • Ensure working tree is clean (git status shows no uncommitted changes)
  • If working tree is dirty, ask user to commit or stash first

Step 1: Create skill directories

For each command being migrated:

# For standalone commands
mkdir -p .claude/skills/<skill-name>

# For plugin commands
mkdir -p plugins/<plugin>/skills/<skill-name>

Step 2: Convert command files to SKILL.md

For each command file, create a proper SKILL.md:

2a: Generate YAML frontmatter

---
name: <skill-name>
description: "<one-line description of what this skill does>"
argument-hint: <hint based on $ARGUMENTS usage or consolidation>
allowed-tools: <infer from command content - Bash, Read, Write, etc.>
---

Frontmatter inference rules:

  • name: Use the skill directory name (kebab-case, noun-phrase)
  • description: Extract from first paragraph or heading of command file
  • argument-hint: If command used $ARGUMENTS, preserve the pattern. If consolidating, add <action> routing
  • allowed-tools: Scan command body for tool references (Bash, Read, Write, Glob, Grep, etc.)
  • user-invocable: Default true unless command was clearly internal-only

2b: Transform content

  1. Copy command content as the skill body
  2. If consolidating multiple commands, create an argument routing table
  3. If command had references to other commands, update to skill references
  4. Preserve any references/ content

2c: Handle consolidation

For consolidated skills (multiple commands -> one skill):

## Argument Routing

| Action | Description |
|--------|-------------|
| `unit` | Run unit tests |
| `e2e` | Run end-to-end tests |
| `lint` | Run linter |

Parse `$ARGUMENTS` to determine the action. Default to `<most-common-action>` if no action specified.

---

## Action: unit

<content from old test-unit command>

---

## Action: e2e

<content from old test-e2e command>

Step 3: Update references across the codebase

Search for and update stale references:

Patterns to find and replace:

FindReplace With
.claude/commands/<name>.md.claude/skills/<name>/SKILL.md
/old-command-name (invocations)/new-skill-name or /new-skill-name <action>
commands/ directory referencesskills/ directory references

Files to search:

  • CLAUDE.md and any .claude/ config files
  • README.md and documentation files
  • Memory files (.claude/memory/)
  • Other skill files that cross-reference

Exclusions (do NOT change):

  • Third-party documentation in canonical/ directories
  • References to external tools that genuinely use "commands" (Gemini CLI, Cursor, etc.)
  • Historical audit logs
  • This skill file itself

Step 4: Verify migration

After all files are created and references updated:

  1. Count new skills vs old commands (should match or consolidation explains difference)
  2. Run validate action to confirm nothing was lost
  3. Present migration summary to user

Step 5: Clean up (with user approval)

Ask user before deleting old command files:

Migration complete. N skills created from M commands.

The old commands/ directories still exist:
  .claude/commands/ (M files)

Delete the old commands/ directories? (The content has been migrated to skills/)

Only delete after explicit user approval.


Action: audit

Comprehensive post-migration health check. Auto-discovers the repo structure (no hardcoded paths) and validates everything using parallel agents.

How It Works

The audit dynamically discovers what exists in the repo:

  1. Auto-discover all commands/ and skills/ directories
  2. Build old-to-new mapping from git history (deleted commands -> current skills)
  3. Run 7 parallel checks via agent team
  4. Compile structured report with PASS/FAIL per check

Checks Performed

Check 1: No commands/ directories remain

# Search for any commands/ directories in the repo
find . -type d -name "commands" -not -path "*/node_modules/*" \
  -not -path "*/.git/*" -not -path "*/canonical/*" 2>/dev/null

Expected: No output. If any remain, the migration is incomplete.

Exclusions (do NOT flag):

  • canonical/ directories (scraped third-party docs)
  • node_modules/ (dependencies)
  • External tool references (Gemini CLI ~/.gemini/commands/, Cursor, etc.)

Check 2: No stale deleted skill directories

If the repo has consolidation history, verify no orphaned skill directories remain from deleted/merged skills.

# Check git log for deleted skill directories
git log --all --diff-filter=D --name-only -- "*/skills/*/SKILL.md" 2>/dev/null | \
  grep "SKILL.md$" | sort -u

For each deleted skill, verify the directory no longer exists on disk.

Check 3: All skills have proper YAML frontmatter

For every SKILL.md found in the repo, validate:

  • name field exists and matches directory name
  • description field is non-empty
  • allowed-tools field is non-empty
  • argument-hint field exists (if skill body references $ARGUMENTS)
  • No duplicate name values across skills (within same plugin)

Use Glob to find all SKILL.md files:

.claude/skills/*/SKILL.md
plugins/*/skills/*/SKILL.md

Check 4: No stale invocation references

Search for old /command-name invocations that should now be /skill-name:

Discovery approach:

  1. Build the list of deleted command names from git history
  2. Search all .md files for /deleted-command-name\b patterns
  3. Exclude: canonical/, .prompts/, agent names (e.g., user-config-auditor), external CLI flags, Python variables, audit logs

Files to search:

  • .claude/ (memory, config)
  • plugins/ (skills, agents, hooks)
  • CLAUDE.md, README.md

Check 5: No stale path references

Search for commands/ path references that should be skills/:

Grep pattern: \.claude/commands/
Scope: all .md files
Exclusions: canonical/, external tools (Gemini, Cursor, Codex), TAC plugin

Also check for plugins/*/commands/ path references.

Check 6: Naming convention compliance

Auto-detect naming convention issues:

  • Scan all skill directory names
  • Flag imperative verb names (e.g., deploy-app should be deployment)
  • Check name frontmatter matches directory name
  • Common verb prefixes to flag: run-, create-, build-, deploy-, setup-, check-, manage-, audit-, prep-, get-, set-, make-
  • Report as WARNING (not FAIL) since some imperative names are intentional exceptions

Check 7: Completeness -- no commands lost

Build old-to-new mapping from git history:

# Find all historically deleted command files
git log --all --diff-filter=D --name-only -- \
  ".claude/commands/*.md" "plugins/*/commands/*.md" 2>/dev/null | \
  grep "\.md$" | sort -u

# Also find commands that still exist (not yet migrated)
find . -path "*/commands/*.md" -not -path "*/canonical/*" \
  -not -path "*/node_modules/*" 2>/dev/null

For each old command, verify a corresponding skill exists:

  • Exact name match: deploy -> skills/deploy/SKILL.md
  • Noun-form match: deploy-app -> skills/deployment/SKILL.md
  • Consolidated match: test-unit -> skills/testing/SKILL.md (check argument routing)

Audit Execution: Agent Team

Create a team named migration-audit-<timestamp> with parallel agents:

AgentChecksAgent TypeDescription
fs-checker1, 2BashFilesystem integrity: no stale dirs
frontmatter-auditor3general-purposeYAML frontmatter validation
reference-scanner4, 5general-purposeStale invocation and path refs
naming-auditor6general-purposeNaming convention compliance
completeness-auditor7general-purposeNo commands lost

Dependencies: All 5 agents run in parallel. No dependencies between them.

After all agents complete, compile the report in the main thread.

Audit Report Template

# Migration Audit Report

**Date:** <current date>
**Repository:** <repo name>
**Commit:** <current HEAD>

## Summary

| Check | Status | Details |
|-------|--------|---------|
| 1. No commands/ dirs | PASS/FAIL | N found |
| 2. No stale skill dirs | PASS/FAIL | N stale |
| 3. Frontmatter valid | PASS/FAIL | N/M valid |
| 4. No stale invocations | PASS/FAIL | N stale refs |
| 5. No stale path refs | PASS/FAIL | N stale refs |
| 6. Naming conventions | PASS/WARN/FAIL | N/M compliant |
| 7. Completeness | PASS/FAIL | N/M mapped |

## Findings

(Detail any failures with file:line and suggested fix)

## Recommendation

[ ] All checks pass - migration is complete
[ ] Fixes needed - run `/skill-migration fix` to auto-remediate
[ ] Manual intervention needed - see findings above

Action: fix

Auto-remediate findings from audit. Run this after audit identifies issues.

Prerequisites

  • Run audit first to identify what needs fixing
  • Audit findings should be in the conversation context
  • Working tree should be clean (commit or stash first)

Fix Categories

Fix 1: Stale commands/ directories

If commands/ directories still exist with content:

  1. For each command file in the directory, check if a corresponding skill exists
  2. If skill exists: the command is redundant -- delete the command file
  3. If skill does NOT exist: run a mini-migration for that single command
  4. After all files handled, delete the empty commands/ directory
# Verify directory is empty before deleting
rmdir <commands-dir>  # Fails safely if not empty

Fix 2: Missing YAML frontmatter

For skills missing required frontmatter fields:

  1. Read the SKILL.md content
  2. Infer missing fields:
    • name: from directory name
    • description: from first heading/paragraph
    • allowed-tools: from content patterns (see inference table below)
    • argument-hint: from $ARGUMENTS usage
  3. Add or update the YAML frontmatter block

IMPORTANT: Always show the user what frontmatter will be added before writing.

Fix 3: Stale invocation references

For each stale /old-command-name reference:

  1. Read the file containing the stale reference
  2. Determine the correct replacement:
    • Simple rename: /old-name -> /new-name
    • Consolidation: /test-unit -> /testing unit
    • Plugin-scoped: /scrape-docs -> /docs-ops scrape
  3. Use Edit tool to replace the reference
  4. Verify the replacement is correct by re-reading the file

Fix 4: Stale path references

For each commands/ path reference:

  1. Replace .claude/commands/<name>.md with .claude/skills/<name>/SKILL.md
  2. Replace plugins/*/commands/ with plugins/*/skills/
  3. Preserve any path that references external tools (Gemini, Cursor, etc.)

Fix 5: Naming convention violations

For imperative verb skill names:

  1. Present suggested noun-phrase rename to user
  2. If user approves:
    • Rename the skill directory
    • Update the name frontmatter field
    • Update all references to the old name
  3. If user declines: document as intentional exception

Fix Execution

Apply fixes sequentially (not parallel) since fixes may affect each other:

1. Fix stale directories (Check 1 findings)
2. Fix missing frontmatter (Check 3 findings)
3. Fix stale invocations (Check 4 findings)
4. Fix stale paths (Check 5 findings)
5. Fix naming (Check 6 findings) -- ask user per rename
6. Re-run validate to confirm nothing was lost

Fix Report

After all fixes applied:

## Fix Report

**Fixes applied:** N
**Manual intervention needed:** M

| # | Category | File | Action Taken |
|---|----------|------|-------------|
| 1 | Stale dir | .claude/commands/ | Deleted (all content migrated) |
| 2 | Frontmatter | .claude/skills/deploy/SKILL.md | Added name, description, allowed-tools |
| 3 | Stale ref | CLAUDE.md:42 | /deploy-staging -> /deployment staging |

**Re-validation result:** PASS/FAIL

Action: validate

Quick validation that no commands were lost. Lighter than audit -- focuses only on completeness.

Step 1: Inventory old commands

Find all command files that exist or have been tracked by git:

# Current commands (if any remain)
find .claude/commands -name "*.md" 2>/dev/null
find plugins/*/commands -name "*.md" 2>/dev/null

# Historical commands (from git history)
git log --all --diff-filter=D --name-only -- \
  ".claude/commands/*.md" "plugins/*/commands/*.md" 2>/dev/null | \
  grep "\.md$" | sort -u

Step 2: Inventory current skills

# Project skills
find .claude/skills -maxdepth 2 -name "SKILL.md" 2>/dev/null

# Plugin skills
find plugins/*/skills -maxdepth 2 -name "SKILL.md" 2>/dev/null

Step 3: Map old to new

For each old command, check if a corresponding skill exists:

  • Exact name match: deploy -> .claude/skills/deploy/SKILL.md
  • Consolidated match: test-unit -> .claude/skills/testing/SKILL.md (with unit action)
  • Renamed match: run-tests -> .claude/skills/test-runner/SKILL.md
  • Plugin match: plugins/foo/commands/bar.md -> plugins/foo/skills/bar/SKILL.md

Step 4: Report

## Validation Report

**Old commands found:** N (current) + M (deleted from git history)
**Current skills found:** K (project) + J (plugin)

| Old Command | Location | Current Skill | Status |
|------------|----------|---------------|--------|
| deploy | .claude/commands/ | deployment | FOUND |
| test-unit | .claude/commands/ | testing (unit action) | FOUND |
| run-tests | .claude/commands/ | test-runner | FOUND |
| unknown-cmd | .claude/commands/ | ??? | MISSING |

**Result:** N/M commands accounted for

Naming Convention Reference

When migrating, follow these naming patterns:

Verb -> Noun-Phrase Conversions

Verb Form (Old)Noun-Phrase (New)Pattern
run-teststest-runnerverb-noun -> noun-agent
deploy-appdeploymentverb-noun -> nominalization
build-docsdocumentation-buildverb-noun -> noun-noun
check-typestype-checkingverb-noun -> gerund
lint-codecode-lintingverb-noun -> gerund
create-useruser-creationverb-noun -> nominalization
manage-configconfig-managementverb-noun -> management
audit-securitysecurity-auditingverb-noun -> gerund
setup-envenvironment-setupverb-noun -> noun-noun
prep-interviewinterview-skillsverb-noun -> noun-skills

Consolidation Patterns

PatternExampleWhen to Use
<resource> <action>/user-config auditMultiple actions on same resource
<domain> <mode>/testing unitRelated but distinct operations
<tool> <subcommand>/docs-ops scrapeLifecycle management of a resource

Exceptions (Imperative Names OK)

Some skill names are intentionally imperative when the action IS the skill:

  • track-win - The action of tracking is the skill
  • plan-career-goals - Planning is the core activity
  • push - Direct git operation

Frontmatter Template

Every migrated skill needs this YAML frontmatter:

---
name: <skill-directory-name>
description: "<concise description of what this skill does>"
argument-hint: <action> [options]  # Only if skill accepts arguments
allowed-tools: Bash, Read, Write  # Tools this skill needs
user-invocable: true  # Set false for model-only skills
---

allowed-tools Inference

Scan the command body to determine which tools the skill needs:

Content PatternInferred Tool
bash, shell, run, execute, backtick code blocksBash
read, file, cat referencesRead
write, create file, save referencesWrite
edit, modify, replace referencesEdit
find, glob, search filesGlob
grep, search contentGrep
agent, task, parallelTask
ask, prompt, questionAskUserQuestion
skill, invoke, /skill-nameSkill
web, fetch, urlWebFetch
Stats
Parent Repo Stars40
Parent Repo Forks6
Last CommitFeb 16, 2026