Правильно настроить AI skills - это три вещи: корректно устроенный SKILL.md, входной файл, который его регистрирует, и регулярная проверка, отлавливающая расхождения. Сам скилл - это markdown-файл с именем, описанием и инструкциями, и модель откроет его только тогда, когда что-то из уже загруженного - CLAUDE.md, AGENTS.md или .github/copilot-instructions.md - укажет на файл и объяснит, когда им пользоваться. Описание при этом работает маршрутизатором: в нём должны стоять фразы, которые люди реально печатают, а не пересказ продукта. Дальше - по одному делу на скилл, тонкий постоянно загружаемый слой и аудит по расписанию, потому что сломанный AI-конфиг никогда не бросает ошибок.
TL;DR
- Скилл - это папка в kebab-case с файлом SKILL.md, где имя во фронтматтере совпадает с именем папки, а описание собрано из фраз, которые люди реально печатают: оно работает маршрутизатором.
- Репозиторий за вас никто не сканирует: каждому скиллу нужна запись в CLAUDE.md, AGENTS.md и copilot-instructions.md - триггер плюс путь. Без неё скилла не существует.
- Одна работа на скилл, тонкий постоянный слой (около 70% хорошего конфига грузится по требованию) и десятиминутный аудит по расписанию - сломанный конфиг ошибок не бросает.
Как выглядит правильно устроенный скилл?
Опенсорсных скиллов в сети сейчас тысячи. Любой скачивается за десять секунд и так же быстро оказывается в репозитории - только работать от этого не начинает. Мне однажды попался проект, где скиллов лежало больше двухсот: все скачанные, все под копирку, и ни одного настроенного входного файла. Для ИИ их просто не существовало. Дальше - ровно про ту часть, которую хозяин того репозитория пропустил.
Снимем сначала весь хайп. Скилл - это markdown-файл: имя, описание, инструкции. Больше ничего. И сила не в самом файле - она в том, найдёт ли его ИИ и догадается ли, в какой момент открыть. За это отвечают четыре правила, и торговаться тут не о чем.
Файл называется SKILL.md - и точка. Никаких skill.md, seo-writer.md или README.md. Инструменты ищут ровно это имя, и на регистрозависимой файловой системе строчный вариант для них попросту не существует.
Один скилл - одна папка, а имя папки и есть идентификатор:
.github/skills/
next-intl-add-language/SKILL.md
next-intl-writer/SKILL.md
seo-writer/SKILL.md
web-design-reviewer/SKILL.mdKebab-case: строчные, через дефис, без пробелов, подчёркиваний и номеров версий. Кладбище из 200 скиллов начинается как раз с какого-нибудь seo-writer-v2-final.
Имя во фронтматтере обязано совпадать с именем папки. Каждый SKILL.md открывается фронтматтером:
---
name: web-design-reviewer
description: 'This skill enables visual inspection of websites running
locally or remotely to identify and fix design issues. Triggers on
requests like "review website design", "check the UI", "fix the
layout", "find design problems". Detects issues with responsive
design, accessibility, visual consistency, and layout breakage,
then performs fixes at the source code level.'
---Стоит папке называться web-design-reviewer, а фронтматтеру - Web Design Reviewer, и у скилла уже две личности: один инструмент смотрит на папку, другой на фронтматтер, а реестр целится куда-то в третье. Имя папки, имя во фронтматтере и запись в реестре - одна и та же строка. Проверяется по всему репозиторию за пять секунд:
for f in .github/skills/*/SKILL.md; do
folder=$(basename "$(dirname "$f")")
name=$(grep -m1 '^name:' "$f" | sed 's/name: *//; s/["'"'"']//g')
[ "$folder" = "$name" ] || echo "MISMATCH: $folder != $name"
doneСами скиллы кладите в .github/skills/. Не потому, что так велит спецификация, а потому что .github/ - единственная папка, которую и AI-инструменты, и вендоры, и коллеги по умолчанию считают общей. Claude, Copilot и живой ревьюер ищут её в одном и том же месте.
Пишите описание как маршрутизатор, а не как аннотацию
Вот где спотыкаются почти все. Тело скилла модель прочитает только после того, как решит его открыть, а решение это принимается по описанию. Значит, описание - не аннотация к продукту, а маршрутизатор. Два примера из одного репозитория.
Слабое - потому что рассказывает про продукт:
seo-writer:
"Write high-converting, SEO-optimized website copy,
landing pages, and marketing content."Сильное - потому что состоит из слов, которые люди набирают:
web-design-reviewer:
"...Triggers on requests like 'review website design',
'check the UI', 'fix the layout', 'find design problems'..."Выигрывает второе, и дело не в длине. В нём дословные фразы, которые реальный человек печатает в чат. Пишу "check the UI" - совпадение один в один. Составляйте описания из слов, которыми будут пользоваться ваши люди, а не из тех, что выбрал бы продакт-менеджер.
Хорошее описание ещё и проводит границу. У next-intl-writer оно заканчивается фразой: "After writing translations, always wires them into the components using useTranslations or getTranslations, replacing every hardcoded string." Одно предложение - и скилл уже не бросит работу на полпути, оставив мне JSON без единого тронутого компонента.
И две привычки, которые не дают описаниям врать. Первая: никаких имён вендоров - скилл, в котором написано "as Claude, you should", мгновенно перестаёт быть переносимым. Вторая: примеры должны соответствовать проекту. Скилл с en.json, az.json и ru.json в примерах прекрасно работает и в проекте, где есть только en.json и fr.json, - просто модель на каждом запуске читает про две несуществующие локали и вынуждена сама догадываться, что это иллюстрация. Трение на ровном месте, в каждой задаче.
Как ИИ понимает, какой скилл использовать?
ИИ не сканирует ваш репозиторий - он читает то, на что вы ему указали.
Бросить SKILL.md куда-то в репозиторий - значит не зарегистрировать его нигде. У каждого инструмента есть одна-две своих папки, которые он просматривает; всё остальное для него не существует, да и из своих папок на старте сессии он берёт только имя и описание. Пока в загруженном контексте модели нет ни слова про файл, она его не откроет, не узнает о нём и ничем не выдаст его существования. Скилл без указателя - это текстовый файл. Так что входные файлы - не бюрократия. Это проводка.
Точек входа три, и каждую инструменты читают сами:
Файл | Кто читает | Как грузится |
|---|---|---|
| Claude Code | Автоматически, каждую сессию |
| Вендор-нейтральный, читает большинство современных агентов | Автоматически |
| GitHub Copilot | Автоматически, на весь репозиторий |
Всё остальное попадает в контекст только потому, что один из этих трёх файлов велел это загрузить.
Внутри входного файла регистрация - это таблица маршрутизации. В CLAUDE.md и AGENTS.md подойдёт обычная markdown-таблица:
## Skills
When the user's request matches an available skill, read its
`SKILL.md` and follow its instructions.
| Trigger | Skill |
|---|---|
| Generating translation keys and wiring i18n strings into components, replacing hardcoded strings | `next-intl-writer` - `.github/skills/next-intl-writer/SKILL.md` |
| Adding a new language / locale | `next-intl-add-language` - `.github/skills/next-intl-add-language/SKILL.md` |
| Writing SEO/marketing copy, landing-page text, or high-converting content | `seo-writer` - `.github/skills/seo-writer/SKILL.md` |
| Reviewing UI, checking the design, or fixing layout/responsive/visual issues | `web-design-reviewer` - `.github/skills/web-design-reviewer/SKILL.md` |Две колонки, три единицы информации: когда браться, как называется, где лежит. Колонку триггеров пишите языком задач, а не языком скиллов: модель сверяет её с запросом пользователя, а не с именем файла.
Для Copilot тот же реестр оформлен блоком внутри copilot-instructions.md:
<skills>
<skill>
<name>web-design-reviewer</name>
<description>This skill enables visual inspection of websites...</description>
<file>.github/skills/web-design-reviewer/SKILL.md</file>
</skill>
</skills>Синтаксис другой, работа та же: имя, триггер, путь. Все реестры мира состоят из этих трёх полей. Без пути модель не откроет файл. Без триггера - не поймёт, что пора. Без имени вы не сможете это отладить.
И регистрируйте каждый скилл в каждом входном файле, а не только в любимом. Добавили скилл, обновили два реестра из трёх - и третий инструмент уже живёт по другому каталогу, чем остальные. Сообщать об этом он не станет.
Когда два скилла - это на один больше, чем нужно?
Два скилла в одной области - пожалуйста. Два скилла на одну работу - нельзя. Приоритет в файле скилла выразить нечем, поэтому, если под запрос подходят оба, модель прочитает оба, усреднит и соберёт вам кодовую базу из двух наполовину соблюдённых стандартов.
Тест здесь точнее, чем "не заводите похожие скиллы". Посмотрите на пару i18n-скиллов выше: next-intl-writer и next-intl-add-language сидят в одной области и трогают одну и ту же папку - и не сталкиваются, потому что задачи у них разные. next-intl-writer - это "я собрал компонент, сгенерируй ключи и замени все захардкоженные строки". next-intl-add-language - это "хочу всё приложение на новой локали". Другое намерение, другой триггер, другой результат.
Вопрос не в том, похоже ли звучат два скилла. Вопрос в том, может ли один запрос пользователя подойти обоим. Может - значит, один удаляем, либо переписываем триггеры, пока совпадать не будет способен только один.
Этой же логикой меряется размер коллекции. Двести скиллов не нужны никому: четыре, каждый из которых отрабатывает своё место, побьют две сотни, делящие между собой три триггера. Не можете вслух назвать запрос, который запускает конкретный скилл, - значит, ему в репозитории делать нечего.
Могут ли Claude и Copilot делить один набор скиллов?
Могут, причём без вторых экземпляров чего бы то ни было. Держится всё на одной схеме: контент живёт в .github/, во входных файлах - только указатели.
.github/
skills/*/SKILL.md <- the actual skills
instructions/*.md <- deep domain standards (a11y, Next.js)
prompts/*.prompt.md <- reusable task recipes
agents/*.agent.md <- specialised personas
copilot-instructions.md <- Copilot's entry point
CLAUDE.md <- Claude's entry point
AGENTS.md <- vendor-neutral rulebook
.claude/commands/*.md <- thin Claude wrappers, no contentВ .github/skills/, .github/instructions/ и .github/prompts/ нет ни слова про вендоров. Вообще. Эти файлы не говорят ни "Claude", ни "Copilot" - они описывают работу. Поэтому их и можно делить. Остальное закрывают три трюка.
Тонкая обёртка. Слэш-команды Copilot берёт из .github/prompts/*.prompt.md, Claude Code - из .claude/commands/*.md. Напрашивается скопировать файл в обе папки - не делайте этого. Вот весь мой .claude/commands/implement-feature.md целиком:
---
description: Implement or update a feature, strictly matching
existing architecture, conventions, and patterns
argument-hint: <feature description, or paste/reference a .md,
code, or design>
---
Follow the feature-implementation guidelines below as the rules
for this task.
**My request:** $ARGUMENTS
@.github/prompts/implement-feature.prompt.mdДесять строк, и последняя - ссылка @ на файл, которая на лету подтягивает настоящий промпт. Стандарт реализации фич на 374 слова существует ровно в одном экземпляре: Copilot видит его как /implement-feature, Claude видит его как /implement-feature, правится он один раз. Обёртка несёт синтаксис вендора, цель - контент.
Пусть каждый вендор грузит общее по-своему. Во фронтматтере файлов инструкций лежат глобы applyTo:
---
description: 'Next.js + Tailwind development standards'
applyTo: '**/*.tsx, **/*.ts, **/*.jsx, **/*.js, **/*.css'
---Copilot по глобу сам подцепляет файл, стоит открыть подходящий на редактирование. Claude applyTo не читает - поэтому CLAUDE.md ссылается на те же файлы явно:
## Related Instruction Files
For deeper, tool-agnostic guidance, also follow:
- `.github/instructions/nextjs.instructions.md`
- `.github/instructions/nextjs-tailwind.instructions.md`
- `.github/instructions/a11y.instructions.md`
- `AGENTS.md` - full project rulebook shared across AI agents.Одни и те же три файла, два разных механизма загрузки, ноль дублей. Поменялось правило WCAG - одна правка, и её видят оба инструмента.
Схлопните и верхний свод правил. Скажу честно: у меня CLAUDE.md, AGENTS.md и copilot-instructions.md до сих пор повторяют один и тот же набор базовых правил (компоненты до 300 строк, только next/image, isLoading в каждом сторе, Loader2 с подписью, никакого next/dynamic с ssr: false) - а три копии одного правила обязательно разъедутся. Конечная точка - один свод и два указателя:
<!-- CLAUDE.md -->
# Project rules
@AGENTS.md
<!-- .github/copilot-instructions.md -->
# Project rules
See AGENTS.md for the full project rulebook. Follow it exactly.Начинаете с нуля - делайте AGENTS.md единственным источником правды, а два других держите тонкими с первого дня.
Держите слой, который грузится всегда, маленьким
Контекст - это бюджет. Скиллы - способ тратить его только тогда, когда он нужен. Вот как это делится в живой конфигурации, в словах:
Слой | Слов | Когда грузится |
|---|---|---|
Входные файлы ( | 4 498 | каждую сессию |
Скиллы, инструкции, промпты, агенты | 11 744 | только когда нужны |
Примерно 70% конфигурации вообще не попадает в контекст, пока в ней нет нужды. Один стандарт доступности - это 3 987 слов критериев WCAG 2.2: когда собираешь форму - бесценно, когда чинишь payload вебхука - чистый шум. Поэтому он живёт в .github/instructions/a11y.instructions.md и подтягивается глобом, а не вклеен в CLAUDE.md.
Здесь же и главный аргумент против репозитория с двумя сотнями скиллов. Даже зарегистрируй он все 200 - каждая сессия открывалась бы стеной противоречащих друг другу инструкций, и модели оставалось бы угадывать, что из этого важно. То, что лежит перед моделью каждую сессию, обязано быть маленьким, иначе его перестают читать. Тот же урок, что и с рабочим логом на 407 строк из Claude Code + Obsidian без плоского журнала (flat log).
Отсюда два вывода для вашей конфигурации. Во входные файлы - только правила, применимые к любой задаче; всё предметное - за скилл, файл инструкций или глоб. И удаляйте скилл, как только он перестал быть правдой: устаревший скилл хуже отсутствующего, потому что модель следует ему с полной уверенностью, а каждый упомянутый в нём путь - обещание, что этот путь существует.
Что делать с правилами, которые действуют только какое-то время?
Бывают фазы, когда обычные правила проекта не работают: миграция, редизайн, спринт по укреплению. Стандартный ход - переписать правила на месте и надеяться, что не забудешь вернуть. Ход получше - временный блок в самом верху CLAUDE.md, в котором есть две вещи, каких нет больше нигде в файле: явно объявленный приоритет и условие истечения.
## ACTIVE: UI Redesign Implementation Contract (temporary)
This section governs the `redesign/ui` branch. Where it conflicts
with any rule below, this section wins. Delete this section once
the redesign ships.
**Scope: visual layer only.** A valid edit changes what the user
sees, never what the code fetches, stores, decides, or where it
navigates.
### Frozen (never touch)
...Работают тут два предложения. "Where it conflicts with any rule below, this section wins" закрывает проблему приоритета - сами скиллы её выразить не умеют. "Delete this section once the redesign ships" означает, что блок носит инструкцию по своему удалению с собой и втихую постоянным не станет. Выстроить правила по порядку мало. Объявите приоритет - и объявите срок.
Проверьте свою конфигурацию за десять минут
Сломанный AI-конфиг падает молча. Исключений он не бросает: модель просто тихо делает чуть-чуть не то, а виноватой назначают модель. Поэтому конфигурации нужна проверка по расписанию - и проверка механическая. Четыре штуки закрывают большую часть гнили; перед публикацией этого текста каждая из четырёх что-то нашла у меня самого.
Проверка 1: каждый ли скилл с диска зарегистрирован везде?
# every skill folder on disk
ls -1 .github/skills/
# every skill mentioned in your entry files
grep -o 'skills/[a-z-]*' CLAUDE.md AGENTS.md \
.github/copilot-instructions.md | sort -uДва списка обязаны сходиться по каждому входному файлу. Что ловит: скилл, который есть в двух реестрах и пропал из третьего, - то есть один инструмент, живущий по другому каталогу, чем остальные.
Проверка 2: живы ли пути, которые упоминают скиллы? Устаревают скиллы одним-единственным способом: они называют файлы, а файлы переезжают.
# real paths
grep -ohE 'src/[a-zA-Z0-9/._-]+' .github/skills/*/SKILL.md \
.github/instructions/*.md CLAUDE.md AGENTS.md \
| sort -u | while read -r p; do
[ -e "$p" ] || echo "MISSING: $p"
done
# alias paths - do not skip this half, it is where the rot hides
grep -ohE '@/[a-zA-Z0-9/._-]+' .github/skills/*/SKILL.md \
.github/instructions/*.md .github/agents/*.md \
CLAUDE.md AGENTS.md .github/copilot-instructions.md \
| sed 's/[.,`)]*$//' | sort -u | while read -r p; do
real="src/${p#@/}"
[ -e "$real" ] || [ -e "$real.ts" ] || [ -e "$real.tsx" ] \
|| echo "MISSING: $p"
doneГоняйте обе половины. Первый grep видит только настоящие пути src/; если правила у вас написаны через алиас @/, вся гниль прячется во второй. И будьте готовы к ложным срабатываниям: иллюстративные пути из примеров кода тоже попадут в список. Список читают, а не считают. Что ловит: скилл, посылающий модель в src/components/language-toggle.tsx, когда файл уже который месяц живёт в src/components/animated/language-toggle.tsx, и правило "always use the utilities from @/lib/utils/scroll" - про модуль, которого никогда не существовало.
Во втором случае есть подвох. Размытая версия того же правила, вовсе без пути - "use a shared scroll utility" - звучит безопаснее. На деле она хуже. Неверный путь падает громко, едва модель попробует его открыть. Размытый указатель просто заставит модель что-нибудь выдумать и пойти дальше - и до код-ревью этого никто не увидит.
Проверка 3: описывает ли скилл всё ещё этот проект? Пройдитесь по примерам в каждом скилле и сверьте с текущей кодовой базой: файлы локалей, имена папок, имена компонентов. Когда они расходятся, ничего не ломается - потому и расходятся.
Проверка 4: не записан ли один факт дважды? Строка, живущая в двух файлах, рано или поздно станет в них разной. Классический виновник - описания скиллов: во фронтматтере SKILL.md и ещё раз в блоке <skills> для Copilot. Реестрам описание нужно для маршрутизации, так что совсем от дубля не уйти - но пусть он числится среди вещей, которые вы сознательно держите в синхроне, а не среди тех, о которых забыли.
Аудит одним списком:
- Каждый
SKILL.mdназывается ровноSKILL.md - Имя папки, имя во фронтматтере и запись в реестре - идентичные строки
- Каждый скилл с диска зарегистрирован в каждом входном файле, а не только в любимом
- Каждая запись реестра указывает на существующий путь
- Каждый путь, упомянутый внутри скилла, всё ещё существует
- Никакие два скилла не подходят под один запрос пользователя
- Описания состоят из фраз, которые пользователи реально печатают
- Ни один скилл не упоминает имя вендора
- Постоянно загружаемый слой маленький, тяжёлые файлы грузятся по требованию
Ваш ИИ не ленив и не глуп. Он читает ровно то, что вы ему дали, - и ничего сверх. Откройте репозиторий прямо сейчас, выведите папки скиллов, прогрепайте входные файлы. Всё, в чём эти два списка не сойдутся, - и есть то место, которое ваш ИИ молча обходил.
Скилл, на который никто не указывает, - просто текстовый файл.
Октай Искендеров
Дизайнер и фулстек-разработчик, веду klauzzdcode - студию одного человека в Баку. На фрилансе с 2023 года: выкатываю продукты от Figma до деплоя и записываю то, что переживает контакт с продакшеном.