Что такое хуки

Хуки - заданные пользователем команды оболочки, HTTP-эндпоинты, вызовы MCP-инструментов, LLM-промпты или субагенты, которые выполняются автоматически в конкретных точках жизненного цикла Claude Code. Они позволяют автоматизировать действия, применять политики и связывать Claude Code с внешними системами - без того, чтобы каждый раз напоминать об этом словами в разговоре.

Три частоты событий

События хуков делятся на три группы по тому, как часто они срабатывают.

Раз за сессию: SessionStart - сессия начинается или возобновляется, SessionEnd - сессия завершается.

Раз за реплику: UserPromptSubmit - промпт отправлен, ещё до того, как Claude начнёт его обрабатывать; Stop - Claude закончил отвечать; StopFailure - ответ завершился с ошибкой.

На каждый вызов инструмента: PreToolUse - перед выполнением вызова, может его заблокировать; PostToolUse - после успешного выполнения; PostToolUseFailure - после неудачного вызова (кроме вызовов EndConversation).

Как настраивается

Хуки описываются в JSON-файле настроек с тремя уровнями вложенности: название события, затем matcher (какие инструменты или условия подходят), и дальше список самих хуков с типом и командой.

структура настройки
{
  "hooks": {
    "НАЗВАНИЕ_СОБЫТИЯ": [
      {
        "matcher": "ИМЯ_ИНСТРУМЕНТА",
        "hooks": [
          {
            "type": "command",
            "command": "/путь/к/скрипту.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

Хуки можно прописать на трёх уровнях: ~/.claude/settings.json - для всех своих проектов на этой машине, .claude/settings.json - для одного проекта, с возможностью поделиться настройкой через контроль версий, и .claude/settings.local.json - тоже для одного проекта, но без попадания в git.

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

Хук получает контекст события в формате JSON через стандартный ввод - с общими полями (идентификатор сессии, путь к рабочей папке, режим разрешений) и полями, специфичными для конкретного события. Для PreToolUse, например, это ещё и название вызываемого инструмента вместе с его входными параметрами:

пример входных данных (stdin)
{
  "session_id": "abc123",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build"
  }
}

Управлять поведением можно двумя способами: кодом возврата и структурированным JSON на выходе. Код 0 означает успех - вывод обрабатывается, если это валидный JSON; код 2 - блокирующая ошибка, которая останавливает действие; любой другой код - не блокирует, действие продолжается, если хук не вернул явное переопределение через JSON.

Практический пример: блокировка опасных команд

Ниже - хук, который проверяет каждую команду Bash перед выполнением и блокирует конкретно rm -rf.

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "args": []
          }
        ]
      }
    ]
  }
}
.claude/hooks/block-rm.sh
#!/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

Дальше файл нужно сделать исполняемым: chmod +x .claude/hooks/block-rm.sh. Логика по шагам: событие PreToolUse срабатывает перед выполнением Bash-команды, matcher Bash отбирает нужный тип вызова, условие if дополнительно сужает круг до команд с rm, скрипт получает JSON, разбирает саму команду и, если в ней найдено rm -rf, возвращает решение deny с причиной - вызов блокируется ещё до выполнения.

Чек-лист

Настройка хука
  1. Выбрано подходящее событие (SessionStart, PreToolUse, Stop и т.д.)
  2. Matcher сужен до нужного инструмента, а не оставлен универсальным без причины
  3. Скрипт хука сделан исполняемым (chmod +x)
  4. Хук проверен на реальном срабатывании события, а не только по коду
  5. Настройка сохранена на нужном уровне: личном, проектном публичном или проектном локальном
Чем хук отличается от навыка (skill)?
Скилл подгружает инструкции для Claude, когда задача подходит под его описание, - это добавка к тому, как модель рассуждает. Хук - это внешний код, который выполняется автоматически на границе события, независимо от того, что думает модель, и может напрямую заблокировать действие.
Может ли хук полностью остановить действие Claude?
Да, хук на событии PreToolUse может заблокировать вызов инструмента до его выполнения - для этого скрипт возвращает код выхода 2 или явное решение permissionDecision: deny в JSON-выводе.
Где лучше хранить хук - в личных настройках или в настройках проекта?
Если хук нужен во всех ваших проектах - в ~/.claude/settings.json. Если это правило конкретного проекта, которым стоит поделиться с командой через git, - в .claude/settings.json. Если это личная настройка только для одного проекта на своей машине - в .claude/settings.local.json, который не попадает в контроль версий.
Может ли хук быть не скриптом, а чем-то ещё?
Да, помимо команд оболочки хуком может быть HTTP-запрос к внешнему сервису, вызов MCP-инструмента, LLM-промпт или запуск отдельного субагента - выбор зависит от того, что нужно сделать в момент события.
Что произойдёт, если скрипт хука завершится с ошибкой?
Зависит от кода возврата: код 2 - блокирующая ошибка, которая останавливает действие. Любой другой ненулевой код не блокирует - действие продолжается, если только хук явно не вернул JSON с переопределением поведения.
Нужно ли что-то устанавливать, чтобы пользоваться хуками?
Нет, сама возможность хуков встроена в Claude Code - устанавливать нужно только зависимости, которые использует конкретный скрипт хука (например, утилиту jq для разбора JSON в примере выше).
← Все статьи блога