Четыре промпта, которые вы вбиваете снова, станут четырьмя файлами
Расширения Claude Code - это файлы, которые может хранить репозиторий, чтобы вам не приходилось снова вбивать один и тот же промпт: скилл, хук, сабагент и MCP-сервер. Мы взяли четыре повторяющиеся задачи в одном реальном репозитории, небольшом API для счетов с JSON-файлом вместо базы данных. Каждой задаче мы сначала подбирали неверное расширение, и неверный выбор каждый раз проваливался на экране. Затем каждая задача превращалась в закоммиченный файл. На этой странице разобраны четыре жеста и карта выбора, которая решает, какой тип использовать.
Два результата задают рамку всему дальнейшему. Хук часто описывают как гарантию, а правило в файле памяти как просьбу, но наш первый хук сработал, вывел предупреждение, и запись всё равно прошла. Что превращает хук, который просто запускается, в хук, который блокирует, рассказано в разделе про хуки. Первый подключённый нами сервер документации ничего не изменил, а ответил только собственный сервер документации инструмента. Рецепт хука, который копируют чаще всего, требует отдельного файла со скриптом, но наш целиком помещается в файл настроек.
Прежде чем писать любой из четырёх файлов, нужно понять, что ваша сессия уже загружает.
Что это за четыре вещи и что ваша сессия уже грузит
Расширение Claude Code - это файл, который вы добавляете в репозиторий, чтобы больше не вбивать одни и те же инструкции в поле промпта. Существует четыре типа, и если вы их уже знаете, можете перейти к следующему разделу.
- Скилл - это регламент, положенный в ваш репозиторий. Claude загружает его шаги только тогда, когда возникает эта задача, а запустить его можно, набрав его имя.
- Хук похож на коммит-хук в git, только для собственных действий Claude. Это скрипт, который запускается по событию, например по правке, независимо от того, что задумал Claude.
- Сабагент - это подрядчик с пустым столом. Он получает ваше задание, сам читает всё нужное и присылает отчёт вместо груды файлов.
- MCP-сервер - это API-клиент, который Claude может вызывать, для данных, которых нет на вашем диске, например для базы данных или документации инструмента.
Чтобы что-то измерять, нужна точка отсчёта. Откройте свежую сессию и выполните команду context. Она выведет таблицу всего, что загружено до вашего первого слова. На нашем стенде итог составил 26.2k токенов из окна в 1m, без строки memory files и вообще без строки MCP. Каждая задача, которую вы дадите Claude с этого момента, либо добавит строку в эту таблицу, либо оставит её нетронутой.
Первая задача: чеклист релиза становится скиллом
Скилл - это чеклист, положенный в отдельную папку, который Claude читает только тогда, когда вы его вызываете. Первая задача - чеклист релиза, который вы постоянно вставляете в поле промпта: проверка типов, тесты, повышение версии, строка в changelog, коммит и тег.
Очевидное место для него - файл памяти, и это неверный выбор, потому что всё содержимое этого файла загружается на каждом шаге каждой сессии. Мы положили шаги туда и снова прочитали таблицу контекста. В ней появилась строка Memory files на 210 токенов, которая грузится независимо от того, делаете ли вы релиз. Документация говорит то же самое: когда раздел этого файла разросся до процедуры, ему место в скилле.
Верный выбор - папка в каталоге скиллов с шагами в .claude/skills/release/SKILL.md, коротким описанием сверху и одной дополнительной строкой во frontmatter, disable-model-invocation: true. Эта строка убирает скилл из собственного списка Claude, так что запустить его можете только вы, а это именно то, что нужно для задачи, которая делает коммит и ставит тег. Дальше вы набираете слеш и его имя, и чеклист выполняется.
| Показание контекста | Итого | Строка Memory files |
|---|---|---|
| Базовый уровень, свежая сессия | 26.2k / 1m | нет |
| Чеклист в файле памяти | 26.4k / 1m | 210 токенов |
| Чеклист как скилл | 26.2k / 1m | нет, скилл не в списке |
Когда мы его запустили, Claude выполнил все шесть шагов меньше чем за минуту: проверка типов, 34 теста, повышение версии с 0.4.0 до 0.4.1, строка в changelog, коммит и тег. Он также нашёл изъян в нашем собственном чеклисте. Команда коммита в том виде, как она записана, пропускает неотслеживаемые файлы, поэтому Claude сначала добавил в индекс новый changelog и сказал об этом. Теперь релиз - это команда, которую вы набираете, она живёт в репозитории для всей команды и ничего не стоит в контексте, пока вы её не наберёте.
Вторая задача: правило, которое должно держаться, становится хуком
Хук - это скрипт, который Claude Code запускает перед срабатыванием инструмента, здесь перед любой правкой или записью, и то, что этот скрипт отвечает, решает, что произойдёт дальше. Вторая задача - правило, которое нельзя нарушать: в этом репозитории файл с хранимыми данными никогда не правится вручную, а любое изменение проходит через миграцию.
Написать это правило заглавными буквами в файле памяти не значит его обеспечить. Документация прямо говорит: этот файл - контекст, а не принудительная конфигурация, и инструкция вроде "never edit this file" - это просьба, а не гарантия, тогда как хук, блокирующий правку, - это принуждение.
Очевидный первый хук - тот, что написали мы. В файле настроек он следит за записью в файл данных, выводит предупреждение и завершается с кодом ноль, как скрипт логирования. Затем мы попросили Claude изменить сумму счёта прямо в файле, без миграции. Хук сработал, предупреждение вывелось, но запись прошла: цена за единицу в третьем счёте выросла с 8000 до 12000. В ответе Claude хук ни разу не упоминался, потому что хук, который завершается чисто и ничего не печатает, не принял никакого решения, а его предупреждения до Claude не доходят.
Гарантия - не в том, что хук запускается, а в ответе, который он возвращает. Исправление оставляет тот же хук и заставляет его возвращать решение deny: короткую JSON-строку с причиной, которую вы хотите, чтобы Claude прочитал. Завершение с кодом два блокирует так же. Весь хук - одна встроенная команда в .claude/settings.json, без файла со скриптом:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -c 'if (.tool_input.file_path // \"\") | test(\"data/.*\\\\.json$\") then {hookSpecificOutput: {hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: \"Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions).\"}} else empty end'"
}
]
}
]
}
}
С тем же запросом правка была отклонена, а файл данных остался нетронутым. Claude увидел такую ошибку: PreToolUse:Edit hook error: Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions). Он сообщил, что ничего не менял, и спросил, обойти ли ограждение или написать миграцию.
Ограничение, которое Claude назвал сам: этот хук следит только за инструментами правки и записи, и в одном из следующих прогонов Claude предложил внести изменение через шелл. Хук должен закрывать и шелл, либо шелл не должен входить в ваш список разрешённого, потому что защита, закрывающая только одну дверь, остаётся просьбой. Хук ничего не стоит в контексте, потому что работает вне вашего разговора.
Третья задача: аудит, который заливает, становится сабагентом
Сабагент - это отдельная сессия Claude с собственным пустым контекстом. Он получает ваше задание, читает всё нужное, и обратно приходит только краткий итог. Третья задача - аудит, который заливает ваш тред: проверить каждый файл в папке с исходниками на соответствие конвенциям из README и перечислить все нарушения с файлом и строкой.
Если спросить напрямую, Claude читает каждый файл в ваш разговор, и всё это содержимое остаётся там до конца сессии. В нашем прогоне Claude прочитал все 19 файлов исходников, строка Messages в таблице контекста раздулась, а результаты потонули в куче.
Верный выбор - файл в .claude/agents/, здесь .claude/agents/conventions-auditor.md, с именем, описанием того, когда его использовать, списком разрешённых инструментов и закреплённой моделью. Закреплять модель важно, потому что иначе сабагент наследует ту, на которой работает ваша сессия, а большинству задач на чтение большая модель не нужна.
Мы задали точно тот же вопрос ещё раз, и Claude снова читал файлы сам. В обеих попытках он ни разу не вызвал агента, хотя тот был в списке: 0 делегирований из 2. Сработало упоминание его по имени в начале промпта: "use the conventions auditor subagent". Ушёл один вызов, и вернулся короткий итог.
| Прогон | Весь контекст | Строка Messages |
|---|---|---|
| Аудит напрямую | 46.1k | 19.3k |
| Сабагент по имени | 34.1k | 7.2k |
Находки были не хуже: сабагент поймал ошибку в пути, которую пропустил прямой прогон, и пропустил одну из тех, что нашёл прямой прогон. Итак, файл создаёт сабагента, а ваш промпт заставляет Claude им пользоваться; пока Claude не выбирает его сам, называйте его по имени. Одно ограничение: счёт не изменился. Оба прогона стоили одинаково, потому что чтение всё равно происходит, просто не в вашем окне.
Четвёртая задача: данные вне досягаемости Claude становятся MCP-сервером
MCP-сервер - это API-клиент, который Claude может вызывать, а задача, ради которой он существует, - данные новее модели или данные, которые лежат вне вашего диска. Рефлекторный выбор здесь неверен, поэтому мы сделали его первым.
Мы подключили публичный сервер документации для нашей тестовой библиотеки и попросили Claude написать тест с фейковыми таймерами. Ничего не изменилось: Claude и так знал эту библиотеку и обращался к серверу, только когда мы называли его. Документация, которую модель уже знает, - это не данные, до которых она не может дотянуться.
Настоящий тест - вопрос о самом Claude Code, о настройке хука новее модели: какая настройка заставляет упавший хук блокировать, в какой версии она появилась и каков точный JSON. Без сервера Claude прочитал схему настроек собственной установки, ничего не нашёл и ответил, что не может найти эту настройку и выдумывать её не будет. Ответ честный и бесполезный, потому что настройка существует; она просто появилась после того, как модель узнала то, что знает.
Решение - добавить в репозиторий собственный сервер документации Claude Code с областью project, чтобы конфигурация легла в файл в корне репозитория, который получит вся ваша команда:
claude mcp add --transport http claude-docs --scope project https://code.claude.com/docs/mcp
Эта команда записывает .mcp.json. Claude Code показывает сервер как ожидающий, пока вы не одобрите его в следующей сессии, и после этого его инструменты доступны Claude. Мы задали тот же вопрос обычными словами, не называя сервер. Claude поискал в документации, нашёл страницу и ответил настройкой, версией и JSON, который нужно добавить, со ссылкой.
Эта настройка - "onFailure": "block", она заставляет упавший хук блокировать действие, а не пропускать его. Она появилась в v2.1.295, а наша установка тогда была ещё 2.1.294. В этом и ограничение: Claude ни разу не сказал, что наша установка не может ей пользоваться, потому что не проверял версию, так что версию проверяете вы сами. В таблице контекста сервер отображается только именами своих инструментов, загружаемых по требованию, пока Claude не обратится к одному из них.
Четыре файла, одна карта выбора
После четырёх задач git status показывает ровно четыре файла, потому что хуку не нужен собственный скрипт:
.claude/skills/release/SKILL.md.claude/settings.json.claude/agents/conventions-auditor.md.mcp.json
Закоммитьте их, и следующая сессия в этом репозитории получит все четыре, как и любой, кто его склонирует.
Причина, по которой каждый выбор был верным, - таблица, которую поставляет документация и которую можно держать как карту выбора:
| Ситуация | Расширение |
|---|---|
| Процедура, которую вы опять вставляете | Скилл |
| То, что должно происходить каждый раз, без вопросов | Хук |
| Побочная задача, заливающая разговор выводом, который вы больше не прочитаете | Сабагент |
| Данные, которые вы постоянно копируете оттуда, куда Claude не видит | MCP-сервер |
Типы можно сочетать. Документация объединяет скилл с MCP-сервером: сервер даёт соединение, а скилл учит Claude им хорошо пользоваться. Скилл также может выполняться внутри форкнутого сабагента, чтобы его чтение оставалось вне вашего окна. Когда другому репозиторию нужна такая же настройка, плагин - это коробка, которая поставляет всё вместе.
Карта определяет тип, а не качество. Хук, который только логирует, сабагент, которого вы никогда не называете, и сервер для документации, которую модель уже знает, каждый попадали в правильный тип и всё равно не работали. Выбирайте тип по карте, а потом добивайтесь, чтобы файл отвечал, а не просто существовал.
Вердикт: когда что, когда ничего, одно правило
Все четыре задачи, которые вы раньше вбивали заново, сначала решались неверным способом, проваливались на экране, а теперь это файлы в репозитории, которые работают. Релиз - это команда, которую вы набираете. Правило для данных - это хук, который отклоняет правку и заставляет Claude спросить. Аудит приходит в виде краткого итога. На вопрос о документации приходят настройка и версия, которые Claude не смог найти сам.
Хук, с которого мы начали, тот, что сработал и пропустил запись, преподал правило, лежащее под всем остальным: хук решает только тем, что возвращает его команда, а значит хук, который только логирует, - это просьба в костюме.
Есть случаи, когда ничего из этого не нужно:
- Конвенция, которую Claude постоянно нарушает, по-прежнему живёт в файле памяти, одной строкой, а не процедурой.
- Задача, которую нужно направлять туда-сюда, имея перед глазами свою историю, остаётся в основном треде, а не в сабагенте.
- Сервер документации для библиотеки, которую модель уже знает, расходует контекст и ничего не меняет. Подключайте то, чего у модели быть не может, а не то, что у неё уже есть.
Правило, которое стоит запомнить: дайте каждой задаче, которую вы вбиваете заново, свой файл, выбирайте тип по карте и помните, что из всех них только хук никогда не спрашивает.
AIDive