AIDive

Пакет к видео

Скилл, хук, сабагент или MCP: четыре файла и карта выбора

7 мин чтения

Кратко и вердикт

Четыре задачи, которые вы постоянно вбиваете в Claude Code, относятся к разным файлам. Чеклист релиза - это скилл. Правило, которое нельзя нарушать, - это хук PreToolUse. Аудит, читающий много файлов, - это сабагент. Данные, которых Claude не видит, - это MCP-сервер.

Мы прогнали все четыре на одном реальном репозитории, API для счетов с 34 тестами, и каждый раз сначала делали неверный выбор. Неверные выборы проваливались на экране:

  • Чеклист в CLAUDE.md добавил в /context строку Memory files: 210 tokens, на каждом шаге.
  • Первый хук сработал, вывел предупреждение, и запись прошла (unitPrice с 8000 до 12000).
  • Аудит напрямую заполнил тред: Messages на 19.3k токенов против 7.2k с сабагентом по имени.
  • Без сервера документации Claude ответил "I couldn't find that setting... I won't guess one."

Вердикт: выбирайте тип по таблице из документации, а потом добивайтесь, чтобы файл отвечал, а не просто существовал. Хук, который только логирует, носит рекомендательный характер, как строка в CLAUDE.md, которую он заменил. Из всех четырёх только хук никогда не спрашивает.

Скопируйте файлы ниже, закоммитьте их и начните с того, который вы вбиваете чаще всего.

Синтез по источникам

Что ваша сессия загружает до первого слова. Наш базовый /context показал 26.2k/1m токенов, без строки памяти и без строки MCP. Каждая задача, которую вы добавляете, либо ставит строку в эту таблицу, либо нет. Сначала прочитайте её, а потом сравнивайте с ней.

Правило в CLAUDE.md - это просьба, хук - это принуждение. Документация говорит, что инструкция вроде "never edit .env" в CLAUDE.md или в скилле - это просьба, а хук PreToolUse, блокирующий правку, - это принуждение s1. Страница о памяти называет CLAUDE.md контекстом, а не принуждением s8. Тред на Hacker News сообщил то же самое со стороны хука: хук, который завершается с кодом 0, ничего не блокирует s10.

Таблица триггеров подсказывает, какой файл нужен. Третья вставка процедуры значит скилл. Вывод, заливающий чат, значит сабагент. Вкладка, которой Claude не видит, значит MCP-сервер. То, что должно происходить каждый раз, значит хук s1.

Задача 1, чеклист релиза, - это скилл.

  • Процедура, выросшая внутри CLAUDE.md, должна жить в скилле, а его тело загружается только при использовании s3.
  • В нашем прогоне чеклист, вставленный в CLAUDE.md, добавил строку Memory files: 210 tokens. Как скилл с disable-model-invocation: true /context вернулся к базовому уровню.
  • /release выполнил шесть шагов (34 теста, 0.4.0 до 0.4.1, тег v0.4.1). Он также заметил, что git commit -am пропускает неотслеживаемые файлы, и сначала добавил в индекс новый changelog.

Задача 2, правило, которое должно держаться, - это хук, и гарантия - в ответе, который он возвращает.

  • Код 0 без вывода - это отсутствие решения. Код 2 блокирует. JSON с deny тоже блокирует s2.
  • Наш первый хук логировал, завершался с кодом 0, и запись прошла (unitPrice с 8000 до 12000). В ответе Claude хук не упоминался.
  • Тот же хук, возвращающий JSON с deny, отклонил правку с сообщением PreToolUse:Edit hook error: Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions). Затем Claude спросил: обойти или написать миграцию.
  • Документация публикует рецепт PreToolUse для защищённых файлов s23.
  • Ограничение: этот хук следит за Edit и Write. Закройте и шелл, либо не включайте шелл в список разрешённого.
  • onFailure: "block" заставляет упавший хук блокировать, а не пропускать. Он появился в v2.1.295 s2 s15.

Задача 3, аудит, который заливает, - это сабагент.

  • Сабагент стартует вслепую и возвращает итог s4.
  • Напрямую Messages показал 19.3k. С сабагентом по имени 7.2k.
  • Один файл агента делегировал 0 из 2 прогонов. Упоминание по имени в промпте делегировало. Файл создаёт агента, ваш промпт заставляет Claude им пользоваться.
  • Закрепите model: в файле. Счёт не изменился: чтение всё равно происходит, просто не в вашем окне.

Задача 4, данные, до которых Claude не дотянуться, - это MCP-сервер.

  • claude mcp add --scope project записывает .mcp.json, а поиск инструментов загружает только имена s5.
  • Для документации самого Claude Code существует бесплатный HTTP-сервер без входа, с search_claude_code_docs и query_docs_filesystem_claude_code_docs s24.
  • Без него Claude ответил "I couldn't find that setting... I won't guess one." С ним Claude ответил "onFailure": "block", добавлено в v2.1.295, тогда как наша установка была 2.1.294.
  • Сервер документации для библиотеки, которую модель уже знает, у нас ничего не изменил. Подключайте то, чего у модели быть не может.

Отчёты с мест. В одном треде на Reddit сообщали, что Claude игнорирует строку "MUST use agent" в CLAUDE.md s9. Мы цитируем это как сообщение, а не как измерение. Инженерные посты Anthropic описывают то же разделение s6 s7.

Код

Четыре файла из прогона плюс два вспомогательных. Пути показывают, где каждый лежит в вашем репозитории.

1. Скилл релиза, в .claude/skills/release/SKILL.md. Наберите /release. disable-model-invocation: true оставляет запуск за вами, а это именно то, что нужно для задачи, которая делает коммит и ставит тег.

SKILL.md

---
name: release
description: Cut a patch release of ledgerly (typecheck, tests, version bump, changelog line, commit, tag).
disable-model-invocation: true
---

Run these steps in order and stop at the first failure:

1. `pnpm typecheck` must exit 0.
2. `pnpm test` must pass.
3. Bump the patch version in `package.json` (e.g. 0.4.0 -> 0.4.1).
4. Add one line at the top of `CHANGELOG.md` (create it if missing): `## vX.Y.Z - YYYY-MM-DD` followed by a one-line summary of the commits since the last tag.
5. `git commit -am "release vX.Y.Z"`
6. `git tag vX.Y.Z`. Never push.

SKILL.md on GitHub

2. Хук protect-data, в .claude/settings.json. Он возвращает решение deny с причиной, которую читает Claude. Ему нужен jq в вашем PATH, и он помещается во встроенную команду, так что файл со скриптом хуку не нужен. Замените шаблон data/.*\.json$ и причину на ваше правило.

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'"
          }
        ]
      }
    ]
  }
}

settings.json on GitHub

3. Аудитор конвенций, в .claude/agents/conventions-auditor.md. Инструменты ограничены теми, что только читают, а модель закреплена. Отредактируйте список конвенций под ваш README.

conventions-auditor.md

---
name: conventions-auditor
description: Use when asked to audit the code against the README conventions
model: sonnet
tools: Read, Grep, Glob
---

You audit ledgerly's source against README § Conventions:

- Money is always an integer number of cents. Never floats.
- Domain functions return new objects; they never mutate their input.
- Validation failures throw `ValidationError` (400), missing records `NotFoundError` (404), illegal state transitions `ConflictError` (409).
- A change of stored shape bumps `schemaVersion` and adds a migration in `src/store/migrations.ts`.

Read every file under `src/`. Return only the findings: one line per violation as `file:line - convention - what is wrong`, then borderline cases the same way, then one line with the count of files read. No file contents, no summary of clean files.

conventions-auditor.md on GitHub

4. Конфигурация MCP, сохранённая как .mcp.json в корне репозитория. Это то, что записывает claude mcp add --transport http claude-docs --scope project https://code.claude.com/docs/mcp. Claude Code показывает серверы проекта как ожидающие, пока вы не одобрите их в следующей сессии.

mcp.json

{
  "mcpServers": {
    "claude-docs": {
      "type": "http",
      "url": "https://code.claude.com/docs/mcp"
    }
  }
}

mcp.json on GitHub

Вспомогательные. ctx.sh запускает /context в безголовой сессии и печатает строки с токенами, чтобы сравнивать до и после. mcp-probe.sh отправляет initialize и tools/list на HTTP-адрес MCP, чтобы проверить, что сервер отвечает, прежде чем его добавлять.

ctx.sh

#!/usr/bin/env bash
# Usage: ctx.sh <step>  -> runs "/context" through run-session.sh, writes context-<step>.txt (honours STRICT_MCP / EXTRA_ALLOW)
C="$(cd "$(dirname "$0")" && pwd)"
"$C/run-session.sh" "$C/out/context-$1.json" "/context" >/dev/null
python3 -c "import json,sys;d=json.load(open(sys.argv[1]));print(d['result'])" "$C/out/context-$1.json" > "$C/context-$1.txt"
grep -E '^\*\*Tokens|^\| (System prompt|System tools|MCP tools|Memory files|Skills|Custom agents|Messages) ' "$C/context-$1.txt"

ctx.sh on GitHub

mcp-probe.sh

#!/usr/bin/env bash
# Usage: mcp-probe.sh <url> [tools/call json params]  -> initialize + notifications/initialized + tools/list (+ optional tools/call) over streamable HTTP
U="$1"; H=$(mktemp)
curl -s -D "$H" -m 30 -X POST "$U" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
echo; SID=$(grep -i '^mcp-session-id:' "$H" | tr -d '\r' | cut -d' ' -f2); echo "session-id: ${SID:-none}"
HS=(-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream'); [ -n "$SID" ] && HS+=(-H "Mcp-Session-Id: $SID")
curl -s -m 30 -X POST "$U" "${HS[@]}" -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' >/dev/null
curl -s -m 30 -X POST "$U" "${HS[@]}" -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'; echo
[ -n "${2:-}" ] && { curl -s -m 90 -X POST "$U" "${HS[@]}" -d "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":$2}"; echo; }
rm -f "$H"

mcp-probe.sh on GitHub

Сделайте это в понедельник

  • Откройте свежую сессию и выполните /context. Запишите итог. Это ваш базовый уровень.
  • Выберите чеклист, который вы вставляете чаще всего. Перенесите его в .claude/skills/<name>/SKILL.md, добавьте disable-model-invocation: true, если он делает коммит или ставит тег, и запускайте через /<name>.
  • Возьмите одно правило, которое нельзя нарушать, и поместите его в хук PreToolUse, возвращающий permissionDecision: "deny" или завершающийся с кодом 2. Попросите Claude нарушить правило и проверьте, что правка отклонена.
  • Если ваш хук только логирует, измените его. Хук, завершающийся с кодом 0, ничего не блокирует.
  • Перенесите одну задачу, читающую много файлов, в .claude/agents/<name>.md с закреплённой model:. Называйте его в промпте, пока Claude не начнёт выбирать его сам.
  • Проверьте один факт, который Claude недавно угадал за вас. Если он новее модели, добавьте сервер документации командой claude mcp add --transport http <name> --scope project <url> и закоммитьте .mcp.json.
  • Проверьте версию Claude Code, прежде чем полагаться на настройку. onFailure: "block" требует 2.1.295.
  • Выполните git add -A && git status и закоммитьте новые файлы. В нашем прогоне их было ровно четыре.

Дальше

Карта выбора. Одна страница. Это строки из документации s1 с нашими четырьмя задачами. Главное в этом наборе - четыре файла выше плюс эта карта.

Тип Что делает Когда использовать Наша задача Наш файл
CLAUDE.md Постоянный контекст, загружаемый в каждом разговоре Конвенция, которую Claude нарушает второй раз Одна строка, а не процедура нет
Скилл Инструкции и рабочие процессы, которыми может пользоваться Claude Вы вставляете один и тот же регламент в третий раз Чеклист релиза .claude/skills/release/SKILL.md
Хук Скрипт, запускаемый событиями Нужно, чтобы это происходило каждый раз, без вопросов Никогда не править хранимые данные вручную .claude/settings.json
Сабагент Изолированный контекст, возвращающий краткие результаты Побочная задача заливает разговор Аудит конвенций .claude/agents/conventions-auditor.md
MCP Подключение к внешним сервисам Вы постоянно копируете данные из вкладки, которой Claude не видит Документация Claude Code .mcp.json
Плагин Объединяет четыре в один устанавливаемый пакет Второму репозиторию нужна такая же настройка Не использовался нет

Карта определяет тип, а не качество. Хук, который только логирует, сабагент, которого вы не называете, и сервер для документации, которую модель знает, каждый попадали в правильный тип и не работали. Выберите тип, а потом добейтесь, чтобы файл отвечал.

Когда к этому не прибегать.

  • Конвенция, которую Claude постоянно нарушает, остаётся в CLAUDE.md одной строкой, а не процедурой.
  • Задача, которую вы направляете туда-сюда, имея перед глазами свою историю, остаётся в основном треде, а не в сабагенте.
  • Сервер документации для библиотеки, которую модель уже знает, расходует контекст и ничего не меняет.
  • pnpm test - это CLI, который у вас уже есть. Это не задача для MCP.

Плагины. Плагин - это коробка, которая поставляет все четыре вместе. Берите его, когда другому репозиторию нужна такая же настройка.

Скилл плюс MCP. Документация объединяет их: сервер даёт соединение, а скилл учит Claude им хорошо пользоваться.

Скилл с context: fork. Скилл может выполняться в форкнутом сабагенте, чтобы его чтение оставалось вне вашего окна. Проверьте это на одной задаче, прежде чем полагаться.

Типы хуков помимо command. Мы использовали command. Справочник по хукам перечисляет другие типы обработчиков, включая http- и prompt-хуки. Правило то же: решает то, что возвращает хук, а молчащий хук ничего не решает.

MCP против CLI. Идёт открытый спор о том, что MCP-серверы съедают контекст: в одном посте аргументируют, что сначала надо брать CLI s11, в другом говорится о цене MCP-серверов в контексте s12. Наше правило: если CLI уже делает работу, используйте его. MCP берите для того, чего в CLI нет. Поиск инструментов теперь загружает только имена, поэтому измерьте /context на своей настройке, прежде чем решать s5.

Держите скиллы короткими и проверенными. Один короткий скилл на задачу, сверенный с реальным прогоном. См. статью SkillsBench s13 и взгляд практика на то, почему скиллы мало стоят, пока не используются s14.

Вопросы и ответы

Почему мой хук сработал, а правка всё равно прошла? Хук решает только тем, что возвращает его команда. Код 0 без вывода - это отсутствие решения s2. Верните JSON с deny или завершитесь с кодом 2.

Почему Claude сам читает файлы, если у меня есть сабагент? В нашем прогоне один файл агента делегировал 0 из 2 прогонов. Назовите его в промпте, например "use the conventions auditor subagent".

Закрывает ли хук всё, что может делать Claude? Нет. Наш срабатывает на Edit и Write. В одном из следующих прогонов Claude предложил внести изменение через шелл, так что закройте шелл или не включайте его в список разрешённого.

Нужно ли добавлять MCP-сервер для каждой библиотеки, которой я пользуюсь? Нет. Если модель уже знает библиотеку, сервер ничего не изменит и потратит контекст. Добавляйте его для данных новее модели или вне вашего диска и сверяйте установленную версию с той, что добавила нужную возможность.

Можно ли закоммитить всё это? Да. Скилл, файл настроек, файл агента и .mcp.json живут в репозитории, так что любой, кто его склонирует, получит их. Одобрите проектный MCP-сервер один раз в следующей сессии.

Файлы