Васин Константин
Васин Константин · инструменты

Хуки в Claude Code: что это и зачем они тебе

Главное, ради чего они нужны: хуки позволяют снимать записи прямо посреди сессии, не завершая её. Решения, заметки, цифры, контекст сохраняются сами — даже если ты не досидел диалог до конца и не давал никаких указаний. Если ты ведёшь второй мозг для своего ИИ, это та деталь, без которой он наполняется вручную и поэтому не наполняется никогда.

Тезис в одну строку. Без хуков контекст сохраняется только тогда, когда ты об этом попросил и довёл сессию до конца. С хуками — сам, по ходу дела. Всё, что выяснилось в разговоре, оседает в файлах, даже если разговор оборвался на середине.
Что настроим
Три хука, которые закрывают 90% пользы. Скрипты готовые, копируются целиком.

Что такое хук и зачем он

Обычно агент делает только то, что ты попросил в переписке. Хук — это способ сказать ему заранее: «в такой-то момент выполни вот этот мой скрипт». Агент запускает его сам, без напоминаний.

Ключевое отличие от промпта: хук выполняется всегда. Промпт можно забыть написать, хук — нет. Поэтому в хуки уходит всё, что должно происходить каждый раз и не должно зависеть от твоей памяти.

Почему это главное — про запись в моменте

Обычный сценарий без хуков выглядит так. Ты полчаса что-то обсуждал с агентом, выяснил три важные вещи, принял решение — и закрыл вкладку. Или сессия упёрлась в лимит. Или ты просто переключился на другое. Всё, что выяснилось, осталось в переписке, которую ты больше не откроешь.

Причина в том, что запись обычно привязана к двум условиям: ты должен о ней попросить и ты должен довести сессию до конца. Оба условия ненадёжны — именно в интересных сессиях о них забывают.

Хуки снимают оба. Событие Stop срабатывает после каждого ответа агента, а не в конце сессии — то есть точек сохранения за один разговор десятки. Плюс PostToolUse после каждого действия и PreCompact перед тем, как сжатие контекста что-то потеряет. Записи копятся по ходу, сами.

Для второго мозга это решающая деталь. База знаний, которую надо наполнять вручную, не наполняется — проверено на себе. Наполняется та, которая пишется сама, пока ты занят разговором.

Три типовые задачи

Главное про SessionStart, если читать только одну строчку. Всё, что скрипт напечатает в консоль, уезжает в контекст сессии — агент это видит как часть входных данных. Это и есть механика «памяти на старте». И ровно поэтому хук — самый частый источник лишних десятков тысяч токенов: он молча растёт, а платишь за него каждым ходом.

Какие события бывают

Событий много, но в реальной работе нужны единицы. Вот те, ради которых стоит открывать конфиг:

СобытиеКогда срабатываетЧто может
SessionStartСессия открывается или продолжаетсяВывод уходит в контекст — так дают память на старте
UserPromptSubmitТы отправил сообщение, до обработкиВывод тоже уходит в контекст — подмешать данные под конкретный запрос
PreToolUseПеред вызовом инструментаЗаблокировать вызов — стоп-кран на опасные команды
PostToolUseПосле успешного вызоваИнструмент уже отработал; можно показать агенту предупреждение
StopПосле каждого ответа агента, а не в конце сессииГлавная точка авто-сохранения: десятки срабатываний за разговор. Умеет и не дать агенту закончить
SubagentStopСубагент закончилТо же, но для подзадач
PreCompactПеред сжатием контекстаУспеть сохранить то, что сжатие потеряет
SessionEndСессия завершаетсяПрибраться за собой

Есть ещё пара десятков событий — на смену рабочей папки, на изменение файла на диске, на уведомления, на запрос прав. Они нужны редко; начинать стоит с SessionStart и Stop.

Как настроить

Хуки живут в settings.json. Глобально для всех проектов — ~/.claude/settings.json; для одного проекта — .claude/settings.json в его корне.

Минимальная структура~/.claude/settings.json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/context.sh",
            "timeout": 20
          }
        ]
      }
    ]
  }
}

Что здесь важно. matcher — фильтр, когда именно срабатывать. Для SessionStart значения такие: startup (свежий запуск), resume (продолжение), clear (после очистки), compact (после сжатия контекста), fork (после форка сессии).

Не пропусти matcher. Без него хук срабатывает на все события сразу, включая compact. Получается петля: контекст переполнился → произошло сжатие → хук снова влил свой объём → ты быстрее приехал к следующему сжатию. Для памяти на старте почти всегда нужен именно "startup".

Для хуков на инструменты (PreToolUse, PostToolUse) matcher фильтрует по имени инструмента: "Bash", "Edit|Write", "mcp__.*" — поддерживается и список через |, и регулярка, и "*" на всё.

Коды возврата

КодЧто значит
0Успех. Вывод уходит в контекст (для событий, которые это умеют)
2Блокирующая ошибка — действие отменяется. Так работает стоп-кран
любой другойОшибка без блокировки: действие продолжится, первая строка stderr покажется в транскрипте

Хук получает на вход JSON: session_id, cwd, hook_event_name, transcript_path, а для событий на инструменты ещё tool_name и tool_input. Это и есть материал для проверок.

Пример 1. Память по проектам и сайтам на старте

Мой основной хук. Задача: агент открывает сессию в любой папке и уже знает контур — какие проекты живы, какие сайты крутятся, где что лежит.

Главный принцип, к которому я пришёл не сразу: в хук идут карты, а не данные. Первая версия грузила канбан проектов целиком — 97 КБ, это 32 500 токенов в каждой сессии. Заменил на номенклатурную карту в 2 КБ: имя проекта → строка сути → где лежат детали. Агент по-прежнему знает все проекты по именам и сам открывает нужную карточку грепом, когда до неё дошло дело.

SessionStart: контекст на старте~/.claude/hooks/context.sh
#!/usr/bin/env bash
# ⚠️ БЮДЖЕТ: суммарно ≤ 25 КБ (~8k токенов).
#    Вывод уезжает в контекст и пересылается КАЖДЫЙ ход каждой сессии.
#    Один лишний файл здесь дороже десяти чтений по запросу.
#
# Сюда идут КАРТЫ, а не данные:
#   ✓ где я остановился (2-3 КБ)  ✓ номенклатура проектов  ✓ карта хранилища
#   ✗ канбан целиком  ✗ справочники  ✗ логи  ✗ личное (хук работает в ЛЮБОЙ папке)
set -euo pipefail

BASE="$HOME/brain"
[ -d "$BASE" ] || exit 0          # нет хранилища — молча выходим, не роняем сессию

files=(
  "_BRAIN/hot.md"           # где я остановился в прошлый раз
  "_BRAIN/PROJECTS_map.md"  # имя проекта → суть → где детали
  "_BRAIN/INDEX.md"         # карта хранилища
)

echo "# Рабочий контекст (автозагрузка)"
echo
echo "Это КАРТЫ, а не данные: детали открывать по запросу, грепом по \`$BASE\`."
echo "В конце содержательной сессии обнови _BRAIN/hot.md — 3-6 строк, не больше."
echo

for f in "${files[@]}"; do
  [ -f "$BASE/$f" ] || continue
  echo "---"; echo "## $f"; echo
  cat "$BASE/$f"; echo
done

Про сайты отдельно. У меня их 15, и держать их состояние в голове агента бессмысленно — оно меняется каждый день. Поэтому в карту идёт только строка на сайт (домен, что это, где репозиторий, как деплоится), а живые цифры — трафик, что опубликовано, что сломалось — агент достаёт по запросу из отдельных файлов. В хуке этого нет вообще.

Пример 2. Запись контекста по ходу сессии

Тот самый главный сценарий. Проблема: сессия оборвалась — и всё, что в ней выяснилось, растворилось. Следующая начинается с чистого листа, ты пересказываешь заново.

Важно понимать механику: Stop срабатывает после каждого ответа агента. То есть это не «сохранение на выходе», а регулярная точка записи в течение всего разговора — сколько ответов, столько и шансов зафиксировать. Даже если ты закроешь вкладку на середине, последние срабатывания уже отработали.

Решение — resume-кэш: маленький файл, в который агент пишет в конце «над чем работали, следующий шаг, какие файлы тронуты». На старте этот же файл подхватывается хуком из примера 1. Круг замыкается.

Ключевая деталь, на которой я обжёгся: у такого файла обязан быть лимит. Мой разросся с обещанных в его же шапке «3–6 строк» до 22 КБ и девяти накопленных сессий — то есть тихо превратился в ещё один справочник в автозагрузке. Лимит пишется и в сам файл, и в правила.

Stop: напомнить записать, где остановились~/.claude/hooks/save-context.sh
#!/usr/bin/env bash
# Срабатывает, когда агент закончил отвечать.
# Не пишет файл сам — напоминает агенту это сделать, если сессия была содержательной.
set -euo pipefail

HOT="$HOME/brain/_BRAIN/hot.md"
[ -f "$HOT" ] || exit 0

# Если файл не трогали больше двух часов, а работа шла — пора обновить
LAST=$(( ($(date +%s) - $(stat -f %m "$HOT" 2>/dev/null || echo 0)) / 60 ))
[ "$LAST" -lt 120 ] && exit 0

cat <<'TXT'
Прежде чем закончить: если в этой сессии была содержательная работа —
обнови _BRAIN/hot.md (3-6 строк: над чем работали, следующий шаг, тронутые файлы).
Лимит файла — 2 последние сессии, старое выкидывай. Если работы не было — не трогай.
TXT
Стоит понимать границу: хук Stop может не дать агенту закончить и заставить продолжить работу. Это мощно и легко превращается в бесконечный цикл. Поэтому в примере выше стоит проверка по времени — она не даёт хуку срабатывать на каждый чих.

Пример 3. Как у меня собираются решения

Самая недооценённая вещь. В переписке постоянно принимаются решения: выбрали стек, отказались от канала, поменяли цену, решили что-то не делать. Через месяц ты не помнишь ни решения, ни аргументов — и обсуждаешь заново.

Я веду журнал решений: один файл на решение, формат жёсткий — контекст, само решение, почему так, последствия. Пишет их агент, сам, без напоминания — потому что это записано в правилах как триггер:

Правило в CLAUDE.mdагент пишет решения без напоминания
## Триггеры записи (без спроса)

- В диалоге появилось решение, на которое сошлются позже (выбор стека, отказ
  от канала, смена позиционирования, принятие/отказ оффера) → завести файл
  решения: контекст / решение / почему так / последствия. 5-10 строк, не больше.
- Факт, цифра, метод, рабочий конфиг, результат ресёрча, который не хочется
  искать заново → короткая заметка с тегами в первой строке.
- Не сохранять: общие обсуждения, легко восстановимые ответы, дубли.
  Тема уже есть — дополни существующее, не плоди второй файл.

Почему это правило, а не хук: решение — вещь смысловая, скрипт его не распознает. Хук здесь работает с другой стороны — на старте подаёт агенту карту хранилища, чтобы он знал, куда это класть и что там уже есть. Без карты он либо не запишет, либо создаст пятый файл на ту же тему.

Это общий принцип: хук отвечает за то, чтобы нужное было под рукой; правило — за то, чтобы им воспользовались. Порознь оба работают плохо.

Пример 4. Стоп-кран на опасные команды

Единственный хук, который не про контекст, а про безопасность. Проверяет команду до выполнения и блокирует, если она из чёрного списка. Код возврата 2 — это и есть «заблокировать».

PreToolUse: не дать выполнить опасное~/.claude/hooks/guard.sh
#!/usr/bin/env bash
# Блокирует опасные команды до выполнения. Выход 2 = заблокировать.
set -euo pipefail

CMD=$(cat | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))")

deny() { echo "Заблокировано хуком: $1" >&2; exit 2; }

case "$CMD" in
  *"rm -rf /"*)                deny "удаление от корня" ;;
  *"git push --force"*)        deny "force-push (используй --force-with-lease)" ;;
  *"git reset --hard origin"*) deny "сброс локальных правок" ;;
  *"DROP TABLE"*|*"TRUNCATE"*) deny "разрушающий SQL" ;;
  *".env"*"rm "*)              deny "удаление .env" ;;
esac
exit 0
Подключение стоп-крана~/.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/guard.sh" }]
      }
    ]
  }
}

Бюджет хука — правило, которое сэкономит тебе больше всего

Всё, что печатает SessionStart, пересылается заново каждый ход. Не один раз за сессию — каждый. Поэтому у хука должен быть явный потолок, записанный прямо в шапке скрипта, чтобы будущий ты его видел.

Мой ориентир: не больше 25 КБ вывода, то есть примерно 8 тысяч токенов. Как проверить свой:

Замер вывода всех SessionStart-хуков
python3 -c "
import json,os,subprocess
s=json.load(open(os.path.expanduser('~/.claude/settings.json')))
for grp in s.get('hooks',{}).get('SessionStart',[]):
    for h in grp.get('hooks',[]):
        out=subprocess.run(h['command'],shell=True,capture_output=True).stdout
        print(len(out),'байт  ~',len(out)//3,'токенов  <-',h['command'])
"

Полный разбор, куда ещё утекает контекст и как срезать его в разы, — в гайде про экономию токенов.

Грабли

  1. Забыл matcher — хук бьёт по всем событиям Включая compact. Контекст сжался, хук снова влил свой объём, ты быстрее приехал к следующему сжатию. Для памяти на старте ставь "startup".
  2. Хук молча растёт Добавил файл, ещё файл, ещё — и через полгода стартовый вес сессии в три раза больше. Лечится потолком в шапке скрипта и периодическим замером.
  3. Личное в глобальном хуке Глобальный хук работает в любой папке — и на демонстрациях, и на записи экрана. Финансы, семья, здоровье в него попадать не должны: такие файлы читаются по запросу.
  4. Хук роняет сессию Скрипт упал — старт сломался. Всегда предусматривай тихий выход: нет файла или папки — exit 0, а не ошибка. И ставь timeout.
  5. Stop-хук зацикливается Stop умеет не дать агенту закончить. Без условия выхода получается вечный цикл. Ставь проверку — по времени, по флагу, по счётчику.
  6. Данные вместо карт Самая дорогая ошибка. Справочник в автозагрузке едет с тобой каждый ход; тот же справочник, открытый грепом по запросу, — один раз и по делу.

Промпты — копировать и вставлять

Промпт 1: собрать хук памяти под себя
Собери мне SessionStart-хук, который на старте сессии даёт тебе рабочий контекст.

Условия:
- Вывод — только КАРТЫ и указатели, не данные. Потолок 25 КБ, запиши его в шапку скрипта.
- Если файла или папки нет — тихий выход (exit 0), сессия не должна падать.
- Личное (финансы, семья, здоровье) в вывод не включать: хук работает в любой папке.
- Подключи через matcher "startup", чтобы не срабатывал после сжатия контекста.

Сначала посмотри, что у меня вообще есть по проектам и заметкам, предложи состав
вывода списком и посчитай примерный вес. Скрипт пиши после того, как я подтвержу состав.
Промпт 2: аудит существующих хуков
Проверь мои хуки в ~/.claude/settings.json и в .claude/settings.json проекта:

1. Для каждого — что запускает, сколько байт выводит, сколько это токенов.
2. У каких SessionStart-хуков не задан matcher (значит, бьют и по compact)?
3. Что из выводимого — данные, а не карты? Для каждого предложи замену указателем.
4. Есть ли риск уронить сессию: нет тихого выхода, нет timeout?
5. Не утекает ли личное в глобальный хук?

Дай таблицу находок и план правок. Ничего не меняй, пока не подтвержу.
Промпт 3: стоп-кран под мой контур
Собери PreToolUse-хук на Bash, который блокирует опасные команды (выход 2).

Посмотри, с чем я реально работаю — какие тут репозитории, серверы, базы —
и предложи чёрный список под МОЙ контур, а не общий шаблон. Для каждого пункта
объясни, какой сценарий он предотвращает.

Отдельно скажи, что блокировать НЕ надо, потому что это сломает нормальную работу.

Дальше — про то, куда утекает контекст

Хук — только один из источников. Полный разбор с цифрами: стартовый вес сессии, субагенты, выбор модели, готовые правила для CLAUDE.md и AGENTS.md.

Читать дальше

Все гайды рабочие: каждый — то, что реально крутится у меня, а не пересказ документации.

Claude Code и агенты

Экономия токеновСтартовый вес сессии пересылается каждый ход. Как срезать его в разы: 48 400 → 15 200 токенов, готовые правила. Панель анализа расходаГотовый промпт: собирает локальную панель — сколько токенов и денег ушло по дням, проектам и моделям. Разбор инструментаСкилл: кидаешь ссылку на Reels, пост или репозиторий — получаешь вердикт, брать или мимо, и первый тест.

SEO и сайты

SEO-машинаКарта из десяти систем, из которых собран сайт на ИИ-агентах: что делает каждая и как собрать такую же. GEO-оптимизация сайтаКак попадать в ответы ChatGPT, AI Overviews и Яндекс Нейро: механика, чеклист по четырём уровням и промпт для аудита и правок. Разведка нишиСкилл: go/no-go по нише до сборки сайта — спрос через Wordstat, деньги-страницы, контент-план. Сайт с нуля с ИИ-агентомЧто решить до первого промпта, зачем два раздельных ТЗ и как сразу заложить блог с категориями. Самоаудит сайта20 проверок за 30 секунд и сравнение с медианой по 83 сайтам малого бизнеса. Скрипт бесплатный. Wordstat APIПодключение через Yandex Cloud за 15 минут: API-ключ, Folder ID, роль, проверка, лимиты и деньги. API поисковиковТокен Яндекс.Вебмастера и доступ к Google Search Console. Включая ловушку с семью днями.

Видео и контент

6 проектов в Claude CodeКонтент-пайплайн целиком: карусели, Reels, озвучка клон-голосом, авто-монтаж, второй мозг. Монтаж «голова + экран»Обучающий ролик с лицом поверх записи экрана: синхронизация, резка тишины, поиск фальстартов, вставки. Индекс видеотекиГотовый промпт: ИИ режет все видео на диске на смысловые куски с таймкодами — что происходит, что говорится, подо что годится.