Custom commands слились со Skills. Когда мигрировать на `.claude/skills/` и что ты получаешь взамен.
Первые три статьи целиком разобрали слеш-команды: от Markdown-файла и инъекции через !команду до оркестровки через subagent и MCP. Ничего из этого не устарело — но за кулисами произошло официальное слияние: custom commands теперь входят в Skills.
Твой .claude/commands/review.md по-прежнему работает, /review всё так же доступен, менять ничего не надо. Но если хочется, чтобы команда обросла новыми возможностями (несколько файлов, выполнение в fork, автоматическая активация по path), новый путь — .claude/skills/review/SKILL.md. В этой статье разбираемся в двух вещах: как мигрировать и стоит ли.
Обещание совместимости от разработчиков однозначно: .claude/commands/deploy.md и .claude/skills/deploy/SKILL.md оба регистрируются как /deploy и ведут себя одинаково. При коллизии имён побеждают skills.
Минимальные шаги:
cd .claude/commands
mkdir -p ../skills/review
mv review.md ../skills/review/SKILL.md
В содержимом файла не нужно менять ни строчки. YAML frontmatter, !`команда`, @file, $ARGUMENTS — всё остаётся совместимым.
Но если ограничиться только этим, читатель резонно спросит: а зачем тогда вообще мигрировать? Ответ в том, что в директорию skill можно положить сколько угодно файлов, а command — только один.
SKILL.md — это точка входа, а в том же каталоге можно хранить любые сопутствующие файлы. Представь такую структуру:
.claude/skills/review/
├── SKILL.md # entry point, краткие инструкции + ссылки
├── checklist.md # длинный чек-лист code review
├── examples/
│ └── good-diff.md # положительный пример
└── scripts/
└── lint.sh # скрипт, который может вызвать SKILL.md
Внутри SKILL.md на них ссылаются по относительному path:
Проверь текущий diff по критериям из [checklist.md](checklist.md).
Смотри [examples/good-diff.md](examples/good-diff.md) для ожидаемого формата вывода.
Эти вспомогательные файлы не попадают в context автоматически — Claude читает их по необходимости. Официальная рекомендация: держать SKILL.md в пределах 500 строк, а всё остальное выносить во вспомогательные файлы.
disable-model-invocation: какие skills ты запускаешь только вручнуюПо умолчанию Claude может сам решить вызвать подходящий skill, если видит удобный момент. Для команд с побочными эффектами вроде /deploy, /commit, /send-email — это опасно: ты не хочешь, чтобы он «задеплоил, потому что код выглядит прилично».
---
name: deploy
description: Деплой в продакшн
disable-model-invocation: true
allowed-tools: Bash(kamal deploy:*), Bash(git push:*)
---
С этой строкой /deploy запускается только вручную — Claude по своей инициативе его в диалоге не дёрнет.
Обратная ситуация: user-invocable: false означает «запускать может только сам Claude, но в меню / этого skill нет» — подходит для skills-фоновых знаний (например, legacy-system-context, который Claude загружает автоматически в нужных ситуациях и который бессмысленно вызывать руками).
context: fork: запускать skill в subagentЭто самое крупное обновление. В прошлой статье мы видели, что command через allowed-tools: Task может попросить модель поднять subagent — но описание «подними Explore subagent, чтобы…» надо было писать руками в самом prompt.
В SKILL достаточно одной строки frontmatter:
---
name: deep-research
description: Глубокое исследование символа
context: fork
agent: Explore
---
Изучи все использования $ARGUMENTS:
- все точки вызова
- бизнес-сценарии
- альтернативные реализации
Верни сводку ≤300 слов.
Когда ты запускаешь /deep-research SomeClass, весь SKILL.md становится prompt'ом отдельного subagent, исполняется агентом типа Explore и по завершении возвращает только итог. Context основной беседы остаётся полностью чистым.
Это превращает «поднять subagent» из текстового описания в prompt в декларативный атрибут skill.
paths: автоматическая активация по типу файла---
name: rails-conventions
description: Кодовые конвенции этого Rails-проекта
paths: ["app/**/*.rb", "config/**/*.rb"]
---
Соблюдай Rails-конвенции проекта:
- используй service object вместо fat controller
- все ActiveRecord scope должны быть именованными
...
Когда ты редактируешь app/models/user.rb, этот skill автоматически попадает в context; при редактировании package.json — нет. Это точнее, чем глобальный фон CLAUDE.md — по сути «CLAUDE.md, послойный по path».
/plan до skillФинальный пример /plan из прошлой статьи был однофайловой command:
.claude/commands/plan.md
Версия как skill:
.claude/skills/plan/
├── SKILL.md
├── research-prompt.md # инструкции для subagent 1
└── risk-prompt.md # инструкции для subagent 2
SKILL.md:
---
name: plan
description: Сформировать план внедрения по Linear-тикету
disable-model-invocation: true
allowed-tools: mcp__linear__*, Task, Read, Grep, Bash(git log:*)
---
## Context
@.claude/context/architecture.md
## Состояние
!`git log --oneline -10`
## Задача
Получи описание и комментарии Linear-тикета $ARGUMENTS.
Через Task подними два subagent параллельно:
1. Исследование кода: prompt в [research-prompt.md](research-prompt.md)
2. Оценка рисков: prompt в [risk-prompt.md](risk-prompt.md)
Сведи оба результата в: шаги внедрения + список рисков + рекомендации по гранулярности commit.
Prompts двух subagent лежат в отдельных файлах — их можно править, не трогая SKILL.md. Сам skill остаётся компактным, в пределах 30 строк, а вспомогательные файлы при необходимости могут быть и в несколько сотен строк каждый.
Если не хочется самому оркестрировать subagents — можно пойти радикальнее и бросить весь skill в fork, чтобы агент Explore выполнил всё одним заходом:
---
name: plan
context: fork
agent: Explore
allowed-tools: mcp__linear__*
---
/plan ENG-4213 → агент Explore в изолированном context берёт всё содержимое SKILL.md как задачу → возвращает только итоговый план. Основная беседа остаётся чистой.
Не каждая command заслуживает апгрейда. Оставаться в .claude/commands/ правильнее, если:
Простые /commit, /pr-desc и им подобные — frontmatter плюс несколько строк prompt — в .claude/commands/ выглядят даже нагляднее. Официально обе формы сосуществуют, и ни одна не депрекейтится.
На сегодня самая аккуратная организация .claude/:
.claude/
├── commands/ # лёгкие: один файл + простой prompt
│ ├── commit.md
│ └── pr-desc.md
├── skills/ # тяжёлые: много файлов / fork / контроль доступа
│ ├── plan/
│ │ ├── SKILL.md
│ │ ├── research-prompt.md
│ │ └── risk-prompt.md
│ ├── deploy/
│ │ └── SKILL.md # disable-model-invocation
│ └── rails-conventions/
│ └── SKILL.md # автоактивация через paths glob
└── context/
└── coding-standards.md
Не надо мигрировать всё скопом. Сигнал к миграции: «эта command начинает распухать» — больше 200 строк, хочется вынести документацию, хочется запускать в subagent. Тогда и поменять path — дело пары минут.
| Желаемая возможность | commands/ |
skills/ |
|---|---|---|
| Шорткат-prompt в одном файле | ✅ | ✅ |
!`команда` / @file / $ARGUMENTS |
✅ | ✅ |
Контроль доступа через allowed-tools |
✅ | ✅ |
| Вспомогательные файлы (ссылки, scripts) | ❌ | ✅ |
disable-model-invocation против ложных срабатываний |
❌ | ✅ |
Изолированный запуск через context: fork + agent: |
❌ | ✅ |
Автоактивация по paths: glob |
❌ | ✅ |
На первых трёх строках commands и skills полностью эквивалентны; последние четыре — только у skills. Берёшь то, что нужно, остальное не трогаешь.
Официальный вектор склоняется в сторону skills, но обещание совместимости commands/ однозначно — это не «мигрируй сейчас или вымрешь», а расширение «вот тебе новые возможности, без принуждения». Разберись в отличиях, мигрируй по потребности и не гонись за косметической стройностью.