Скиллы для AI-агентов
Сегодня добавил в Testo набор AI-скиллов — небольших инструкций, которые подгружает агент (Claude Code, Codex и компания), когда видит подходящую задачу. Лежат в папке skills/.
Что внутри
Девять скиллов, по одному на сценарий:
testo-write-tests— написать обычный #[Test]#[Test()]Явно помечает метод, функцию или класс как тест.-класс сAssert/Expect/ lifecycle-хуками.testo-data-driven— параметризовать тест: #[DataSet]#[DataSet(array $arguments, ?string $name = null)]Объявляет набор аргументов для параметризованного теста. Можно использовать многократно — каждый атрибут создаёт отдельный запуск., #[DataProvider]#[DataProvider(callable|string $provider)]Предоставляет данные для параметризованного теста из метода или вызываемого объекта., #[DataUnion]#[DataUnion(DataProviderAttribute ...$providers)]Объединяет данные из нескольких провайдеров в один последовательный набор., #[DataZip]#[DataZip(DataProviderAttribute ...$providers)]Объединяет провайдеры попарно по индексу., #[DataCross]#[DataCross(DataProviderAttribute ...$providers)]Создаёт все возможные комбинации из провайдеров (декартово произведение)..testo-flaky-tests— #[Retry]#[Retry(int $maxAttempts = 3, bool $markFlaky = true)]Объявляет политику повторного запуска теста при падении. vs #[Repeat]#[Repeat(int $times = 2, int $maxFailures = 0, bool $markFlaky = true)]Запускает тест фиксированное число раз и решает итог по порогу падений.: представляете, между ними есть разница.testo-inline-tests— #[TestInline]#[TestInline(array $arguments, mixed $result = null)]Объявляет встроенный тест на методе или функции. прямо на методах вsrc.testo-benchmarks— #[Bench]#[Bench(array $callables, array $arguments = [], int $warmup = 1, int $calls = 1_000, int $iterations = 10)]Объявляет бенчмарк для сравнения производительности метода с альтернативными реализациями. и как читать Mean / Median / RStDev.testo-coverage— настройкаCodecovPlugin, #[Covers]#[Covers(string $classOrFunction, ?string $method = null)]Ограничивает, какой исходный код засчитывается в покрытие для данного теста., отчёты Clover / Cobertura / PHPUnit XML.testo-migrate-from-phpunit— миграция тестов с PHPUnit — хит.testo-plugin-author— написать собственный плагин Testo.testo-configure— собрать или поправитьtesto.php.
А зачем вообще скиллы, если есть llms.txt?
llms.txt — это что в API есть. Скиллы — это когда что применять и где грабли. Они короткие, активируются по триггерам (фразам пользователя), и каждый отправляет агента читать llms.txt за уточнениями. Так документация не дублируется, а скиллы не протухают вместе с API.
Но копировать их в каждый проект — лень
Сейчас, чтобы агент увидел эти скиллы, их надо положить в .claude/skills/ (или куда там настроен ваш агент). А значит — либо копи-пастить из vendor/testo/testo/skills/, либо ставить симлинки, либо… забить и не использовать.
Поэтому запилил отдельный пакет — llm/skills.
llm/skills — Composer-плагин для скиллов
Идея простая: Composer-пакет объявляет в composer.json, что он "донор" скиллов:
{
"extra": {
"skills": {
"source": "skills"
}
}
}А проект-потребитель ставит llm/skills, и при composer install скиллы из доверенных пакетов автоматически едут в .agents/skills/ (или куда настроите).
Никаких ручных копирований, никаких симлинков, никакого "ой, я забыл обновить SKILL.md после composer update".
Присоединяйтесь к тестированию
Только что выкатил llm/skills v1.0.0. Я не знаю, насколько он окажется востребованным, поэтому фичей особо не накидывал — собрал минимальный жизнеспособный механизм:
- Две команды:
composer skills:updateсинкает,composer skills:show— read-only инспектор, который показывает, что синкается, что пропущено и почему. Уupdateесть--dry-runдля превью без записи. - Декларирование папки со скиллами через
extra.skills.sourceвcomposer.jsonзависимостей. - Auto-discovery: поиск скиллов в пакетах без
extra.skillsпо папкеskillsв корне. - Whitelist доверенных вендоров:
extra.skills.trusted+--trust=PATTERN, поддержка wildcards (acme/*,*) и встроенный список уже доверенных пакетов. - Шорткат «назвал — значит доверяю»:
composer skills:update acme/fooобходит trust-список на время команды и попутно включает auto-discovery для этого пакета. - Транзакционность: если два донора объявили скилл с одинаковым именем — sync падает до того, как тронет файлы. Никаких полусобранных состояний.
- Non-destructive merge: локальные правки в
target/<skill>/переживают синк, перезаписывается только то, что реально несёт донор. Можно дописатьlocal.mdк чужому скиллу — он сохранится.
Если поставите и наткнётесь на грабли — кидайте issue в репозиторий. Особенно интересно услышать про сценарии, до которых я сам не додумался: чужие агенты, нестандартные раскладки, политики безопасности у больших команд.
Пакет навайбкожен на 95%, но переживать об этом не стоит: всё покрыто тестами с высоким MSI.
Быстрый старт
Прописать настройки в
composer.json:json{ "extra": { "skills": { "target": ".agents/skills", "aliases": [".claude/skills", ".cursor/skills"], "trusted": ["my-vendor/*"], "discovery": true, "auto-sync": true } } }target— реальная папка со скиллами (дефолт.agents/skills).aliases— дополнительные пути-зеркала (junction на Windows, symlink на POSIX), указывающие наtarget. Удобно, когда в проекте живут несколько агентов: Claude Code, Cursor и компания читают один и тот же набор через свои привычные пути, без дублирования файлов.trusted— доверенные вендоры в форматеvendor/*илиvendor/package. Testo и часть других пакетов уже сидят во встроенном whitelist'е, так что сюда обычно добавляют что-то своё.discovery— подбирать скиллы из пакетов, у которых нетextra.skills, но есть папкаskills/в корне. По умолчаниюfalse.auto-sync— гонятьskills:updateавтоматически послеcomposer install/update.
Поставить плагин — он сразу же подхватит и разложит скиллы по указанным путям:
bashcomposer require --dev llm/skillsComposer спросит разрешение запустить плагин (
allow-plugins) — соглашайтесь.Посмотреть, что ещё можно синкнуть.
composer skills:show— read-only инспектор: показывает, что уже синкается, что пропущено и почему. Полезные флаги:bashcomposer skills:show # текущая раскладка composer skills:show --discovery # + пакеты с папкой skills/ без extra.skills composer skills:show --trust='acme/*' # + что окажется доступным, если расширить трастыЗаметили
[skip] not trustedнапротив интересного пакета — добавьте его вtrusted, и при следующем синке скиллы приедут. Для однократного синка можно указать пакет прямо в командеskills:update acme/foo
Плагин можно поставить глобально — тогда composer skills:show / skills:update будут доступны в любом проекте, а настройки (target, aliases, trusted) всё равно читаются из локального composer.json:
composer global require llm/skills