AmigaОбучение ИИ
Модуль 3 · Настройка · урок 12 из 13

Хуки: форматирование, запреты, уведомления

12 мин чтения▶ есть видеоверсия

CLAUDE.md — это пожелания. Модель обычно им следует, но не всегда. Когда действие должно происходить всегда, без исключений — форматирование после каждой правки, запрет на опасную команду, уведомление, когда агент ждёт вас, — нужен механизм, который не зависит от решения модели. Это хуки: команды оболочки, привязанные к событиям жизненного цикла Claude Code.

Что такое хук

Хук — это запись в файле настроек: событие, фильтр и команда. Когда событие наступает, Claude Code запускает команду, передаёт ей JSON с подробностями на стандартный ввод и смотрит на код выхода и вывод. В зависимости от них он продолжает, блокирует действие или передаёт модели дополнительный контекст.

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

События

Полный список событий длинный, для начала достаточно нескольких.

  • PreToolUse — перед вызовом инструмента. Единственное из этих событий, которое может заблокировать действие.
  • PostToolUse — после успешного вызова инструмента. Инструмент уже отработал, блокировать нечего, но можно, например, отформатировать файл.
  • Notification — когда Claude Code отправляет уведомление: ждёт разрешения или ввода.
  • Stop — когда Claude закончил ответ. SubagentStop — когда закончил субагент.
  • UserPromptSubmit — когда вы отправили сообщение, до того как его увидела модель.
  • SessionStart, SessionEnd — начало и конец сессии; PreCompact — перед сжатием контекста.

Для событий инструментов фильтр (matcher) — имя инструмента: Bash, Edit|Write, mcp__github__.*. Пустой фильтр или * — на все вызовы. Для Notification фильтр — тип уведомления, для SessionStart — способ старта (startup, resume, clear, compact).

Где хранятся

Хуки живут в hooks внутри файлов настроек, и место определяет область:

  • ~/.claude/settings.json — все ваши проекты.
  • .claude/settings.json — этот проект, в git, для команды.
  • .claude/settings.local.json — этот проект, только вы.

Команда /hooks открывает список настроенных хуков по событиям с указанием источника. Список только для чтения: править нужно JSON. Можно попросить Claude: «напиши хук, который запускает eslint после каждой правки файла» — он умеет.

Структура записи трёхуровневая: событие → список фильтров → список команд:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Пример 1: форматирование после правки

Именно этот хук выше и решает задачу. Событие PostToolUse, фильтр Edit|Write — только после правок файлов. Команда читает JSON со стандартного ввода, достаёт tool_input.file_path через jq и передаёт файл Prettier. Каждый файл, который агент тронул, форматируется сразу, и в diff не попадает шум от отступов.

Для Python-проекта команда будет jq -r '.tool_input.file_path' | xargs uv run ruff format, для Flutter — xargs dart format, для PHP в проекте на Bitrix — xargs vendor/bin/php-cs-fixer fix. Если форматтер должен применяться только к определённым файлам, добавьте условие: поле if с правилом вида "Edit(*.ts)" ограничит хук нужными расширениями.

Хук стоит положить в .claude/settings.json проекта: тогда форматирование одинаково у всей команды.

Пример 2: запрет опасных команд

PreToolUse может остановить действие двумя способами. Первый — код выхода 2: действие блокируется, а текст из стандартного потока ошибок передаётся модели как объяснение. Второй — JSON на стандартном выводе с решением deny, allow или ask. Скрипт из документации, блокирующий rm -rf:

bash
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0
fi

Сохраните его как .claude/hooks/block-rm.sh, сделайте исполняемым (chmod +x) и подключите:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

${CLAUDE_PROJECT_DIR} — корень проекта, откуда запущена сессия; так путь не зависит от текущей папки. Код выхода 0 без JSON означает «решения нет, действует обычная проверка прав».

Тот же приём защищает файлы. Скрипт из документации сравнивает tool_input.file_path со списком (.env, package-lock.json, .git/), пишет причину в поток ошибок и выходит с кодом 2 — правка блокируется, а агент получает объяснение и меняет подход. Для наших проектов в список просятся папки миграций и legacy/, которые нельзя трогать без согласования.

Важно: хук не обходит правила прав. Если в permissions.deny есть запрет, он сработает независимо от хука; хук, вернувший allow, не отменит правило ask. И наоборот: для простых запретов вроде «не читать .env» правило deny короче хука. Хук нужен, когда условие сложнее, чем шаблон команды.

Пример 3: уведомление, когда агент ждёт

Длинная задача идёт минут десять, вы переключились на другое окно, а агент три минуты назад остановился с вопросом. Хук на Notification решает это. Для macOS:

json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

На Linux вместо osascriptnotify-send 'Claude Code' 'Claude Code needs your attention'. На macOS есть тонкость: osascript шлёт уведомления через Script Editor, и если ему не разрешены уведомления, команда молча ничего не покажет. Один раз запустите её в терминале, потом включите уведомления для Script Editor в системных настройках.

Этот хук уместен в ~/.claude/settings.json: он про ваше рабочее место, а не про проект. Аналогично можно повесить уведомление на Stop — «агент закончил».

Что получает и возвращает хук

На стандартный ввод приходит JSON с общими полями — session_id, cwd, hook_event_name, permission_mode — и полями события. Для инструментов это tool_name и tool_input (для Bashcommand, для Editfile_path).

Коды выхода:

  • 0 — успех. Если на выводе JSON, он разбирается; для большинства событий обычный текст уходит только в отладочный лог.
  • 2 — блокирующая ошибка. Действие останавливается на событиях, которые это поддерживают (PreToolUse, UserPromptSubmit, Stop и других), текст из потока ошибок передаётся модели. На PostToolUse блокировать нечего, но текст модель всё равно увидит.
  • Другие — неблокирующая ошибка: действие продолжается, первая строка ошибки показывается в разговоре.

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

Что ещё можно

Хук на Stop может запускать вашу проверку и блокировать завершение хода, пока она не пройдёт: агент вынужден дорабатывать, пока тесты не позеленеют (после восьми блокировок подряд Claude Code всё же завершит ход). Хук на PreToolUse для Bash может переписать команду: документация приводит пример, который добавляет к npm test фильтр, оставляющий в выводе только падения, — экономия контекста в разы. Хук на SessionStart с фильтром compact может заново подложить важный контекст после сжатия. Кроме команд оболочки есть хуки типа prompt, где условие оценивает модель, и HTTP-хуки. Всё это описано в справочнике.

Отключить все хуки на время — "disableAllHooks": true в настройках. Хуки из проектных настроек срабатывают только после того, как вы подтвердили доверие к папке, — репозиторий не может запустить у вас команду без спроса.

Попробуйте сами

10–15 мин на рабочем месте
  1. Настройте хук уведомления для своей системы в ~/.claude/settings.json. Проверьте через /hooks, что он зарегистрирован, затем дайте агенту задачу в ручном режиме и переключитесь в другое окно.
  2. Добавьте в проект хук форматирования под ваш стек. Попросите агента внести правку и убедитесь по git diff, что файл отформатирован.
  3. Напишите скрипт защиты файлов для папок, которые в вашем проекте нельзя трогать без согласования. Попросите агента что-нибудь туда добавить и посмотрите, как он отреагирует на блокировку.

Коротко

  • Хук — команда оболочки на событие цикла; срабатывает всегда, в отличие от инструкций в CLAUDE.md.
  • Основные события: PreToolUse (может блокировать), PostToolUse, Notification, Stop, SessionStart.
  • Формат в settings.json: событие → matcher → список команд; /hooks показывает настроенное.
  • Код выхода 2 или JSON с permissionDecision: "deny" блокирует действие и объясняет модели причину.
  • Три базовых сценария: форматирование после Edit|Write, запрет команд на PreToolUse, уведомление на Notification.
  • Хуки не обходят правила прав; для простых запретов правило deny короче.

Видеоверсия

Сценарий озвучки · 612 слов, ≈ 5 мин

Файл клод-эм-дэ — это пожелания. Модель обычно им следует, но не всегда. Когда действие должно происходить всегда — форматирование после каждой правки, запрет опасной команды, уведомление, когда агент ждёт вас, — нужен механизм, который не зависит от решения модели. Это хуки.

Хук — это запись в файле настроек: событие, фильтр и команда. Когда событие наступает, Claude Code запускает команду, передаёт ей джейсон с подробностями и смотрит на код выхода и вывод. В зависимости от них он продолжает, блокирует действие или передаёт модели дополнительный контекст. Главное свойство — детерминированность. Инструкции в файле консультативны, хук гарантирует.

Событий много, для начала хватит нескольких. «Пре-тул-юз» — перед вызовом инструмента; единственное из базовых, которое может заблокировать действие. «Пост-тул-юз» — после успешного вызова. «Нотификейшн» — когда Claude Code ждёт разрешения или ввода. «Стоп» — когда Claude закончил ответ. И «сешн-старт» — начало сессии. Фильтр для событий инструментов — имя инструмента: «баш», «эдит», «райт».

Хуки хранятся в файлах настроек. В домашней папке — для всех ваших проектов. В папке точка-клод проекта — для команды, через git. В локальном файле — только для вас. Команда «хукс» в сессии показывает всё настроенное с указанием источника. Править нужно джейсон, но можно попросить Claude — он умеет писать хуки.

Первый пример — форматирование после правки. Событие «пост-тул-юз», фильтр — правка или запись файла. Команда читает джейсон со стандартного ввода, достаёт путь к файлу через утилиту jq и передаёт его Prettier. Каждый файл, который агент тронул, форматируется сразу, и в дифф не попадает шум от отступов. Для Python вместо Prettier — ruff, для Flutter — dart format, для PHP — php-cs-fixer. Такой хук стоит положить в настройки проекта, чтобы форматирование было одинаковым у всей команды.

Второй пример — запрет опасных команд. Хук на «пре-тул-юз» может остановить действие двумя способами: кодом выхода два, тогда текст из потока ошибок уходит модели как объяснение, или джейсоном с решением «запретить». Скрипт из документации проверяет команду на «rm -rf» и, если нашёл, возвращает запрет с причиной. Скрипт лежит в папке хуков проекта, путь к нему задаётся через переменную корня проекта. Тот же приём защищает файлы: скрипт сравнивает путь со списком — файл с секретами, lock-файл, папка git — и выходит с кодом два. Агент получает объяснение и меняет подход. Для наших проектов в список просятся миграции и папка legacy.

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

Третий пример — уведомление. Задача идёт десять минут, вы ушли в другое окно, а агент три минуты назад остановился с вопросом. Хук на «нотификейшн» с пустым фильтром запускает системное уведомление: на Mac через osascript, на Linux через notify-send. На Mac есть тонкость: уведомления идут через Script Editor, и если ему не разрешены уведомления, ничего не появится. Один раз запустите команду в терминале и включите уведомления в системных настройках. Этот хук — в личные настройки: он про ваше рабочее место.

Что получает хук? Джейсон с идентификатором сессии, рабочей папкой, именем события и полями события: для инструментов — имя инструмента и его вход. Что возвращает? Код ноль — успех. Код два — блокировка с объяснением. Другие коды — неблокирующая ошибка, действие продолжается. По умолчанию хук ждут десять минут. Все подходящие хуки запускаются параллельно, и из нескольких решений побеждает самое строгое.

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

В последнем уроке подведём итоги курса и наметим, куда двигаться дальше.

Отметка хранится только в вашем браузере