К содержимому
ErgonLab
← Блог
Гайд · Claude Code
· 6 мин

Hooks — когда автоматизировать поведение AI

Если ты пишешь в CLAUDE.md «каждый раз когда X, делай Y» — это не работает. Для автоматизации есть hooks. Разбираю когда нужны, какие события, реальные примеры.

Главный антипаттерн: правило в CLAUDE.md

Самая частая ошибка, которую я вижу у людей, начинающих с Claude Code, — они открывают CLAUDE.md и пишут туда что-то вроде «всегда после редактирования .tsx файла запускай prettier» или «каждый раз перед git push проверяй .env на секреты».

Это не работает. Точнее, работает через раз. Claude — это языковая модель, она читает инструкции и может их выполнить, но может и забыть. Особенно в длинных сессиях, где контекста много, а правило одно из тридцати. Через час работы я ловлю себя на том, что prettier не запустился, потому что Claude был занят основной задачей и не вспомнил.

CLAUDE.md — это знания и стиль: как со мной разговаривать, что за проект, какой стек. Если ты хочешь, чтобы что-то выполнялось железно, каждый раз — это работа не для языковой модели, а для harness'а. Для этого есть hooks.

В чём принципиальная разница

Hook — это shell-команда, которую Claude Code запускает автоматически на определённое событие. Не «Claude решает запустить», а harness запускает сам, без участия модели.

Это меняет надёжность на порядок:

  • Правило в CLAUDE.md: вероятность выполнения ~80-95% в зависимости от контекста
  • Hook: 100%, пока не сломается shell-команда

Hooks живут в ~/.claude/settings.json (или в .claude/settings.json проекта) в разделе hooks. Каждый hook привязан к событию и опционально к матчеру (например, только когда инструмент = Bash, или только когда файл = .py).

Четыре события

В Claude Code есть несколько событий, на которые можно повесить hook. Я использую четыре основных.

PreToolUse — перед инструментом

Срабатывает до того, как Claude вызвал инструмент. Полезно для предупреждений и валидации.

Пример из моего setup — security warning перед опасной bash-командой:

{
  "matcher": "Bash",
  "hooks": [{
    "type": "command",
    "command": "... case \"$CMD\" in *'rm -rf'*|*'git reset --hard'*|*'git push --force'*) echo '⚠️ ВНИМАНИЕ: Деструктивная операция!' ;; esac"
  }]
}

Когда модель собирается выполнить rm -rf, git reset --hard или git push --force — в её контекст падает предупреждение до выполнения. Дальше модель видит warning и сама решает: продолжать или переспросить.

Похожий hook у меня стоит на Write и Edit: если файл .env, CLAUDE.md, schema.prisma или docker-compose.yml — выводится напоминание «это конфиг, проверь изменения».

PostToolUse — после инструмента

Срабатывает сразу после успешного выполнения. Это место для форматирования и побочных эффектов.

Мой основной hook сюда — автоформатирование:

{
  "matcher": "Edit",
  "hooks": [{
    "type": "command",
    "command": "... case \"$FILE\" in *.py) black --quiet \"$FILE\" ;; *.js|*.ts|*.jsx|*.tsx) npx prettier --write \"$FILE\" ;; esac"
  }]
}

После любого Edit файл прогоняется через black (если Python) или prettier (если JS/TS). Я никогда не думаю про форматирование — оно происходит само, и diff в git всегда чистый.

Второй пример — конвертация .excalidraw файлов:

{
  "matcher": "Write",
  "hooks": [{
    "type": "command",
    "command": "if echo \"$FILE\" | grep -qE '\\.excalidraw$'; then bash $HOME/.claude/scripts/excalidraw-to-clipboard.sh \"$FILE\"; fi"
  }]
}

Как только Claude записал .excalidraw файл, скрипт автоматически рендерит его в PNG и кладёт в буфер обмена. Я просто переключаюсь в Telegram или документ и вставляю — без ручных шагов.

Stop — когда Claude закончил отвечать

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

Сюда можно повесить, например:

  • Отправку Telegram-уведомления «Claude закончил, можно смотреть»
  • Запуск дашборда с метриками проекта
  • Сохранение состояния сессии в MEMORY.md

В моём setup на Stop пока ничего тяжёлого нет — Stop у меня используется системой Buddy Evolution (плагин), но идея та же: автоматизировать «что делать каждый раз, когда AI закончил работу».

UserPromptSubmit — когда я отправил сообщение

Срабатывает до того, как мой промт уйдёт в модель. Это место для проверок и логирования:

  • Залогировать все мои запросы в файл (для последующего анализа, какие задачи я повторяю)
  • Сканировать промт на чувствительные данные (если случайно вставил пароль или токен — предупредить)
  • Подгрузить дополнительный контекст автоматически (например, свежий git status)

Я этим почти не пользуюсь, но видел кейсы, где люди вешают сюда фильтр на чувствительные слова — чтобы AI не получал токены из буфера обмена случайно.

Три моих рабочих hook'а

Соберу в одно место то, что у меня реально стоит и спасает каждый день.

1. Форматирование на PostToolUse: Edit

Любой .pyblack. Любой .js/.ts/.jsx/.tsxprettier. Происходит автоматом после каждого редактирования. Я не помню, когда последний раз думал про форматирование вручную — но и не вижу «дрейфа стиля» в коммитах.

2. Security warning на PreToolUse: Bash

Деструктивные команды (rm -rf, git reset --hard, git push --force, DROP TABLE, docker rm) дают warning в контекст. Это не блокировка — это напоминание. Полезно, потому что в длинной сессии модель может «забыть» правило из CLAUDE.md, но warning в её контексте прямо перед выполнением — это не пропустить.

Аналогично — на запись в .env, CLAUDE.md, schema.prisma, docker-compose.yml. Это файлы, которые трогаются редко, но если трогаются — точно стоит затормозить и проверить.

3. Excalidraw → PNG → буфер обмена

Когда я генерирую диаграмму через excalidraw-visualizer, она ложится как .excalidraw (JSON). Я не хочу руками открывать excalidraw.com, импортировать, экспортировать в PNG, копировать. Поэтому hook делает это сам: записал файл → скрипт сконвертил в PNG → положил в буфер обмена → я вставляю куда нужно.

Это избавляет от 5 кликов на каждой диаграмме. За месяц экономит десятки минут и нервы.

Когда hook хуже, чем skill или agent

Hook — это автоматизация. Но не любая задача в неё хорошо ложится. Три красных флага:

1. Если задача требует решения

Hook — это shell-скрипт. Он не «думает». Если нужно решить «когда стоит запустить prettier, а когда не стоит», это уже не работа для hook — это работа для AI. Hook должен делать одно простое действие без условий уровня «а если бизнес-логика говорит ещё что-то».

Если шаги начинаются с «иногда нужно X, а иногда Y, в зависимости от...» — это skill, не hook.

2. Если задача медленная

Hook вешается синхронно (в большинстве событий). Если он занимает 30 секунд — все 30 секунд Claude Code висит. Я ставлю hook'и на действия в пределах 1-3 секунд: форматирование, проверка regex, копирование файла. Что-то тяжёлое (рендер видео, обращение к LLM) — это уже background job или skill.

В settings.json можно указать timeout, но если ты хочешь, чтобы hook не блокировал — это сигнал, что задача не для hook.

3. Если задача требует контекста разговора

Hook видит только то, что ему передал harness: имя инструмента, параметры, иногда содержимое файла. Он не знает что ты обсуждал с Claude минуту назад. Если для действия нужен контекст разговора («запомнить что мы решили на прошлом шаге») — это subagent или skill, не hook.

Простое правило: hook — это «всегда X на событие Y». Если в формулировке появляются «если», «когда», «в зависимости от» — это не hook.

Как добавить hook

Два пути.

Через skill /update-config — у меня есть skill, который умеет добавлять hooks в settings.json через диалог. Я говорю: «повесь hook на PostToolUse Edit, форматирование .py через black» — skill сам формирует JSON и добавляет в правильное место. Это удобно, когда не хочется лезть в файл руками.

Вручную в settings.json — открыть ~/.claude/settings.json (или .claude/settings.json проекта), найти раздел hooks, добавить новый объект:

"hooks": {
  "PostToolUse": [
    {
      "matcher": "Edit",
      "hooks": [
        {
          "type": "command",
          "command": "echo 'edit done' >> /tmp/edits.log"
        }
      ]
    }
  ]
}

После сохранения hook подхватывается в следующей сессии. Старые сессии не перезагружают конфиг.

Что сделать прямо сейчас

  1. Открой свой ~/.claude/settings.json. Если там нет раздела hooks — это твой первый шаг.
  2. Поймай одну рутину, которую делаешь руками после Claude — форматирование, копирование, уведомление. Заверни в hook.
  3. Не клади в hook ничего, что требует решения. Только «всегда X».

Через неделю работы с hooks начинаешь замечать, как много мелких ручных действий исчезает из жизни. И в CLAUDE.md больше не пишутся бесполезные «каждый раз делай Y» — все автоматизации живут там, где им положено.

Соседние посты

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

← Предыдущий
/loop dynamic — событийная автоматизация в одном окне
Следующий →
Контекст переполняется — что делать
Все материалы
Принимаю заказы

Понравилось?
Обсудим задачу?

Напишите коротко — ответим в течение дня.

Написать в Telegram
@ergonlab_bot