11 правил, каждое из провала на стенде
Правила, по которым навык заводят и принимают. Первые два решают, будет ли им вообще кто-то пользоваться, — остальное поправимо. Каждое правило с пометкой «прогон» стоило одного провала на стенде (см. METHOD.md).
Термины: автор — тот, чей модуль или библиотеку описывает навык
(acme, shef, ваш вендор); префикс — короткое имя автора в имени
навыка; справочный навык отвечает «как это устроено», операционный —
«сделай по канону».
1. Имя — глагол или тема; каталог и name совпадают. Только [a-z0-9-],
префикс автора первым: acme-new-import, acme-options-traits. Справочные —
<префикс>-<библиотека>-<о чём>; операционные — <префикс>-new-<что>,
<префикс>-use-<что> и другие (<префикс>-feedback). Операционным
считается всё, что не справочное, и у него обязательны evals. Префикс нужен
не для красоты: в одном проекте живут навыки нескольких авторов, и без
префикса они перепишут друг друга при установке.
2. description пишется под задачу, а не под содержание. Это единственное,
по чему навык выбирают: модель видит список имён и описаний и берёт один.
Описание-оглавление («в навыке есть Modules, Events, PrepareFields») проигрывает
описанию-поводу («нужно разобрать цену из строки вида 1 234,50 — брать этот»).
Что обязано быть в описании:
mb_strlen,
не wc -m в C-локали;acme.demo
в пяти случаях из девяти; после правки — 9 из 9.3. Тело — то, чего агент не знает и не выведет сам. Порядок действий,
ловушки, «чего не делать». Пересказывать документацию ядра Битрикса не нужно:
она у модели есть; а вот что prefilters замещает умолчания — нет.
4. Имена классов сверяются с кодом. Любой \Vendor\… в обратных кавычках
проверяет bxshef lint --code против исходников модуля: нет такого класса —
красный CI. Защита от навыка, который уверенно рассказывает про
переименованное.
5. Навык не отсылает к коду, который агент может не увидеть. Сигнатура метода, namespace класса, имя файла — дословно в навыке. «Возьмите из модуля» — с точным путём и только как дополнение. Прогон 2: три угадывания из трёх там, где навык отсылал вместо того, чтобы сказать (глобальный класс без namespace, метод с массивом вместо строки, наследование без подключения модуля).
6. Модуль подключается до объявления класса. Файл, в котором класс делает
extends, implements или use чего-либо из чужого модуля, начинается с
\Bitrix\Main\Loader::includeModule('<модуль>') до слова class. PHP
разбирает наследование при чтении файла; includeModule в теле метода к этому
моменту не выполнялся. Навык, который показывает такой класс, показывает и эту
строку. Прогон 3: Trait "…" not found там, где навык молчал.
7. Ссылки наружу — абсолютные, на GitHub. Навык ставится в чужой проект, относительный путь там ведёт в никуда.
8. Операционному навыку — evals/selection.json. Минимум три фразы: на
себя, на соседа, <none>. Формат — {"input": "<фраза>", "expected": "<навык>" | ["<навык>", …] | "<none>", "notes": "…"}. Массив — когда верны
несколько навыков. Одна и та же фраза в двух навыках не должна ждать разных
ответов (lint это ловит). Реальные фразы с прогонов — с вводной строкой,
как их ставит человек, с пометкой в notes. Фразу пишите как задачу, а не
как механизм: «нужно по ночам пересчитывать остатки» проверяет описание,
«сделай агента» — совпадение по слову.
9. Операционный навык заканчивается разделом «В конце» со ссылкой на
<префикс>-feedback — навык отзыва, который есть в каждом наборе (заготовка
в template/). Шаг, не вписанный в сам навык, до конца задачи не доживает —
за первый прогон отзыв не был написан ни разу; после переноса шага внутрь
навыка — 5 из 5.
10. После правки — bxshef lint и bxshef eval. Правка description
без прогона evals — не правка: описание меняют ради выбора, а проверить выбор
можно только прогоном. CI сделает это на PR, локально быстрее:
npx bxshef lint --dir skills --code <путь к исходникам модуля>
BXSHEF_EVAL_KEY=… npx bxshef eval --dir skills --repeat 3 # ключ — из окружения
11. Навыки автора живут в одном репозитории, не в репозитории модуля.
ИИ-агент ставит и выбирает их как один набор. Связь с кодом держит CI в обе
стороны: репозиторий навыков гоняет lint --code против свежих main
модулей; репозиторий модуля забирает навыки и гоняет lint --code против
себя — переименовали класс, PR модуля красный, пока не поправлен навык.
Репозиторий модуля (#9):
.claude/skills/, ни копий, ни синхронизации:
две копии расходятся с первой же правки.README.md, раздел «Для ИИ-агентов» сразу после «Установки»: «Навыки
для ИИ-агентов (Claude Code, Codex, Cursor) — в
<owner>/<repo>. В проект:
npx skills add <owner>/<repo>».CLAUDE.md / AGENTS.md — для агента, который правит сам модуль:
навыки лежат там-то; правка класса или сигнатуры здесь требует правки
навыка там; порядок — сначала PR в навыки, потом сюда.composer.json → "suggest": {"<vendor>/skills": "Навыки для ИИ-агентов: npx skills add <owner>/<repo>"} — пакета нет, это подсказка при
composer require.main
(без соседей их классы с общим корнем, Vendor\…, будут «не найдены»), и
bx-shef/skills-standard/action@v1 с dir: .check/skills/skills,
code: .check/modules. eval-key не передаётся — eval пропускается: в
модуле проверяется только связь классов с кодом. Готовый файл —
template/module/skills.yml; .check/ — в
.gitignore.На проекте — по npx skills@latest:
npx skills add <owner>/<repo> ставит навыки в .agents/skills/<имя>/, для
Claude Code делает ссылку в .claude/skills/ и пишет skills-lock.json
(источник и хеш). Lock-файл коммитят; восстановить навыки по нему —
npx skills experimental_install, обновить — npx skills update.<префикс>-feedback), и навык правит автор.curl-ом (feedback/README.md).12. Раскладка репозитория навыков — skills/<имя>/SKILL.md. Рядом с
навыком — его evals/, в корне — README.md и CI. Не из прогона — решение
владельца (#10): шаблон
лежал в .agents/skills/, эталон — в skills/, и автор, сверявший одно с
другим, не знал, какому верить. .agents/ — каталог проекта, куда навыки
ставят; в репозитории навыков ставить некуда. Пользователю раскладка не видна:
npx skills add находит навыки в skills/ и кладёт их в проект в
.agents/skills/; bxshef без --dir находит skills/ сам.