Мой скилл про локальную разработку в Symfony-проекте разросся до ста сорока строк. Из них девяносто — таблица команд: запустить контейнеры, войти в контейнер, применить миграции, залить демо-данные, собрать фронт, посмотреть логи, очистить кэш.
Полезная таблица. Нужна она примерно на одной задаче из двадцати, а грузилась на всех двадцати.
Что такое прогрессивное раскрытие на практике
Идея простая: основной файл содержит правила и порядок работы, а длинные детали лежат рядом и открываются по ссылке, если понадобятся.
.ai/skills/local-dev/
├── SKILL.md 41 строка — правила и рабочий процесс
└── references/
└── commands.md 96 строк — полный справочник командБыло сто сорок строк в одном файле, стало сорок одна плюс девяносто шесть на отдельной полке. Суммарно даже больше, потому что при разделении справочник стало не жалко дополнить.
Работает это не потому, что второй файл никогда не читается. Работает, потому что решение о чтении принимается по задаче. На вопросе «почему не поднимается контейнер» справочник открывается. На правке шаблона — нет.
Что остаётся в основном файле
Три вещи, которые нужны всегда, когда скилл вообще сработал.
Правила. То, что нельзя нарушать. В скилле про локальную разработку это одна строка, ради которой он и написан: файл композиции контейнеров лежит в .local/, а корневая заглушка от установщика — не источник правды. Без неё команды запускаются мимо.
Порядок работы. Пронумерованные шаги. Их обычно четыре-шесть, и они помещаются на экран.
Одна-две ключевые команды. Не все, а те, которыми начинается работа. В моём случае это имя контейнера и форма вызова консоли внутри него — из них выводится остальное.
Основной файл я держу в пределах пятидесяти строк. У меня их сейчас в Symfony-проекте одиннадцать штук, и десять укладываются: от двадцати девяти до сорока шести строк. Одиннадцатый — про миграции, девяносто семь строк, и его я не разрезал, о чём ниже.
Что уходит в references
Всё, что имеет форму справочника.
local-dev/references/commands.md 96 строк — команды на все случаи
sql-migrations/references/rules.md 76 строк — правила именования,
обратимость, работа с архивом
module-architecture/references/patterns.md 46 строк — примеры слоёв
frontend-shell/references/layout.md 28 строк — раскладка шаблонаПризнак, по которому отправляю в справочник: содержимое читается выборочно. Никто не читает список команд подряд — в нём ищут одну. То же с правилами именования миграций: нужна одна строка из семидесяти шести.
Второй признак: содержимое нужно не всегда, а в определённом сценарии. Работа с архивом старых миграций случается раз в полгода. Держать её в основном файле — платить каждый раз за случай, который наступает дважды в год.
Третий: примеры длиннее правила, которое иллюстрируют. Абзац «репозиторий объявляется интерфейсом в домене, реализация в инфраструктуре» короче, чем код на сорок строк, который это показывает. Правило — в основной файл, код — в справочник.
Ссылка как приглашение
Формулировка ссылки решает, работает разделение или нет.
Плохо: «перед началом работы прочитай references/commands.md». Это возвращает нас к исходному размеру, только с лишним переходом.
Хорошо: «полный справочник команд — references/commands.md». Или ещё точнее, с условием: «работа с архивом legacy-миграций описана в references/rules.md».
Второй вариант лучше первого тем, что называет ситуацию. Ссылка с условием читается как маршрут: если попал в этот случай — тебе туда. Ссылка без условия читается как обязанность, и её либо игнорируют, либо выполняют всегда.
Замер
Считал по одному скиллу, грубо, но разница нагляднее любых рассуждений.
| Строк в контексте на типовой задаче | |
|---|---|
| Один файл на 140 строк | 140 |
| SKILL.md 41 + справочник по нужде | 41 |
| То же, когда справочник понадобился | 137 |
Экономия появляется не на одной задаче, а на распределении. Из двадцати задач справочник открывается на одной. Средний расход падает примерно втрое.
Число само по себе небольшое: сто строк не решают судьбу контекстного окна. Дело в накоплении. Скиллов подключается несколько, у каждого свой справочник, и разница между «все справочники всегда» и «справочник по нужде» набирается сотнями строк — ровно в тот момент, когда контекст занят самой задачей.
Когда делить не надо
Скилл на сорок строк делить бессмысленно. Получится два файла по двадцать, переход между которыми стоит дороже, чем экономия.
Мой порог — примерно сотня строк. Ниже держу одним файлом, выше смотрю, есть ли внутри справочная часть. Если содержимое на сто двадцать строк однородное и читается подряд — не делю.
Ровно поэтому у меня не разрезан скилл про миграции Doctrine на девяносто семь строк основного файла. Он длинный, но в нём нет справочной части: там порядок действий, разбор частых ошибок и пример. Справочник из него уже вынесен, остальное читается целиком.
Второй случай, когда деление вредит, — скилл, который целиком про редкий сценарий. Разделять «редкое на редкое» бессмысленно: если скилл сработал, его тело нужно всё.
Как я это переделывал
Порядок, в котором я разрезал существующие скиллы.
Сначала помечал строки, которые за эти месяцы читал сам. Это неточная мера, но других у меня нет: инструмент не показывает, какая часть скилла реально использовалась.
Потом переносил в справочник всё непомеченное, что имеет форму списка или таблицы.
Потом перечитывал остаток и проверял: понятно ли по нему, что делать, без открытия справочника. Если непонятно — возвращал строку обратно. У меня вернулось три строки из двух скиллов: имя контейнера, форма вызова консоли и путь к файлу композиции.
Заняло часа полтора на одиннадцать скиллов. С тех пор новые пишу сразу разделёнными, если видно, что справочник будет.
Что осталось
Приём дешёвый и предсказуемый, спорить тут не о чем. Единственная сложность — определить справочную часть, и она решается вопросом «читают ли это подряд».
Чего мне не хватает — обратной связи. Я не знаю, открывались ли мои справочники хоть раз. Возможно, половина из них — это способ спрятать текст, который никому не нужен, и вместо разделения его надо было удалить. Проверить нечем, поэтому пока считаю, что лежать на полке дешевле, чем в основном файле, и на этом успокаиваюсь.