Заметки

Хуки в Claude Code: автоматизируем рутину

M
Markabus
·11 августа 2026 г.
Макросъёмка рабочего места разработчика: на переднем плане экран с JSON-конфигурацией и подсветкой синтаксиса, позади — расфокусированный терминал с зелёным логом и механическая клавиатура

Просьба «не забывай прогонять форматтер после правок» в инструкциях проекта работает через раз: модель может её выполнить, а может увлечься задачей и пропустить. Хуки (hooks — «зацепки») решают эту проблему иначе. Это обычные команды вашей системы, которые Claude Code запускает сам в строго определённые моменты своей работы — после записи файла, перед вызовом инструмента, когда сессия стартует или завершается. Разница принципиальная: инструкция в тексте — это просьба к модели, хук — гарантия. Он сработает независимо от того, что модель решила в этот раз.

Ниже — как устроен механизм, где его настраивать, как хук «разговаривает» с агентом и какие сценарии окупаются в первую очередь.

Где живут хуки

Хуки описываются в файлах настроек в формате JSON. Место файла определяет область действия:

  • ~/.claude/settings.json — все ваши проекты, только на этой машине;
  • .claude/settings.json в корне репозитория — конкретный проект, файл можно закоммитить и раздать команде;
  • .claude/settings.local.json — тот же проект, но локально, без коммита;
  • управляемые политики организации — задаются администратором на всю компанию;
  • hooks/hooks.json внутри плагина — включаются вместе с плагином;
  • фронтматтер скилла или сабагента — действуют, пока активен соответствующий компонент.

Команда /hooks внутри Claude Code показывает все зарегистрированные хуки, сгруппированные по событиям, — удобно, чтобы понять, откуда прилетел неожиданный запуск. Меню только для чтения: добавлять и править хуки нужно в JSON (или попросить об этом самого Claude). Аварийный рубильник — "disableAllHooks": true в настройках.

Структура конфигурации

Каждое событие — ключ внутри общего объекта hooks. Внутри — массив групп, у каждой группы есть matcher (фильтр) и список обработчиков:

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

Это готовый автоформаттер: после каждой правки файла Claude Code передаёт хуку JSON события на стандартный ввод, jq вытаскивает оттуда путь к файлу, а Prettier его переписывает. Если ключ hooks в файле уже есть, новое событие добавляют соседним ключом, а не заменяют весь объект целиком — типичная ошибка при ручной правке.

Что означает matcher, зависит от события. Для инструментальных событий это регулярное выражение по имени инструмента (Bash, Edit, Write, а также MCP-инструменты вида mcp__memory__.*). Для SessionStart — источник запуска (startup, resume, clear, compact, fork). Для Notification — тип уведомления. У части событий фильтра нет вовсе.

События жизненного цикла

Событий в справочнике несколько десятков, но на практике почти всё закрывается небольшим набором:

  • SessionStart — сессия началась или возобновилась. Удобно подсовывать агенту актуальный контекст: статус git, номер текущей задачи, содержимое рабочего журнала.
  • UserPromptSubmit — вы отправили запрос, но модель его ещё не увидела. Можно дополнить запрос контекстом или заблокировать.
  • PreToolUse — перед вызовом инструмента. Единственная точка, где вызов ещё можно остановить.
  • PostToolUse и PostToolUseFailure — после успешного вызова и после упавшего. Отменить действие уже нельзя, зато можно отформатировать, залинтить, записать в журнал.
  • PermissionRequest — Claude Code собирается спросить у вас разрешение. Хук может ответить за вас.
  • Stop и SubagentStop — модель (или сабагент) закончила отвечать. Можно не дать остановиться и вернуть её к работе.
  • PreCompact и PostCompact — до и после сжатия контекста; момент, когда часть истории теряется и её полезно переинжектить.
  • Notification — Claude Code показывает уведомление: ждёт подтверждения или простаивает.
  • SessionEnd — сессия завершается. Учтите жёсткий бюджет: все хуки этого события делят 1,5 секунды (до 60, если явно задать больший timeout).

Отдельно стоят события вокруг задач и сабагентов (SubagentStart, TaskCreated, TaskCompleted), конфигурации (ConfigChange, InstructionsLoaded, FileChanged) и MCP-элиситаций. Если вы строите свою обвязку вокруг агента, загляните в полный список — там почти наверняка есть нужная точка.

Как хук отвечает агенту

Хук получает JSON события на stdin. Общие поля — session_id, transcript_path, cwd, hook_event_name; дальше идут поля конкретного события: tool_name и tool_input для инструментальных, user_prompt для пользовательского запроса, last_assistant_message для Stop.

Ответить можно двумя способами. Первый — кодом выхода:

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

Второй способ — вернуть в stdout JSON. Универсальные поля: continue (при false агент останавливается совсем), stopReason, suppressOutput, systemMessage. Тонкое управление живёт в объекте hookSpecificOutput, где обязательно указать hookEventName. Для PreToolUse там доступны permissionDecision (allow, deny, ask, defer), пояснение permissionDecisionReason и даже updatedInput — подменённые аргументы инструмента. Для SessionStart, UserPromptSubmit и большинства прочих — additionalContext, текст, который агент увидит как системную заметку.

Три сценария, которые окупаются сразу

Защита файлов от правок

Скрипт читает stdin, достаёт путь и выходит с кодом 2, если путь попал в список защищённых:

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED=(".env" "package-lock.json" ".git/")

for p in "${PROTECTED[@]}"; do
  if [[ "$FILE_PATH" == *"$p"* ]]; then
    echo "Заблокировано: $FILE_PATH попадает под правило '$p'" >&2
    exit 2
  fi
done
exit 0

Регистрируется как PreToolUse с матчером Edit|Write и командой "$CLAUDE_PROJECT_DIR"/.claude/hooks/protect-files.sh. Не забудьте chmod +x. Сообщение из stderr придёт модели как обратная связь, и она попробует другой путь вместо того, чтобы упереться в стену.

Уведомление, когда агент ждёт вас

Событие Notification с пустым матчером ловит все типы; матчер permission_prompt — только запросы разрешений, idle_prompt — простой. На macOS команда выглядит как osascript -e 'display notification "Claude Code ждёт ответа"', на Linux — notify-send (нужен пакет libnotify-bin и работающий демон уведомлений: на голом сервере или в контейнере он не заведётся).

Автоодобрение узкого разрешения

Хук PermissionRequest с матчером на конкретный инструмент может ответить вместо вас, вернув JSON с {"decision": {"behavior": "allow"}}. Ключевое слово — узкий: матчер .* или пустой автоматически одобрит вообще всё, включая запись файлов и запуск shell-команд.

Не только shell-команды

Кроме type: "command" есть ещё четыре типа обработчиков. http отправляет событие POST-запросом на ваш эндпоинт — годится для общего аудит-сервиса на команду; заголовки поддерживают подстановку переменных окружения, но только тех, что перечислены в allowedEnvVars. mcp_tool вызывает инструмент подключённого MCP-сервера (подробнее — в заметке о подключении MCP-серверов к Claude Code). prompt отдаёт решение отдельной модели (по умолчанию Haiku), которая возвращает {"ok": true|false, "reason": "..."} — это для случаев, когда нужно суждение, а не жёсткое правило. agent идёт дальше и поднимает сабагента, который может читать файлы и запускать команды, прежде чем вынести вердикт; тип помечен как экспериментальный, для продакшена рекомендуют обычные command-хуки.

Безопасность и грабли

Хук — это код, который выполняется на вашей машине с вашими правами, автоматически и без подтверждения. Отсюда правила: не подключайте чужие конфигурации не глядя, относитесь к .claude/settings.json из pull request как к исполняемому коду и всегда цитируйте пути в кавычках — "$CLAUDE_PROJECT_DIR", а не голая переменная. Данные из tool_input приходят от модели, а значит могут содержать что угодно: не подставляйте их в команды без экранирования. Общая логика тут та же, что и в безопасности AI-агентов: минимум прав, явные границы, журнал.

Из практических граблей: PostToolUse не может отменить уже выполненное действие — блокировать нужно на PreToolUse. Хуки одного события запускаются параллельно, поэтому если два PreToolUse-хука возвращают updatedInput для одного инструмента, победит тот, кто закончил последним, — порядок недетерминирован. Stop срабатывает после каждого ответа модели, а не только по завершении задачи, и не срабатывает при прерывании пользователем. Таймаут по умолчанию — 600 секунд для command/http/mcp_tool, но UserPromptSubmit урезает его до 30 секунд, а MessageDisplay — до 10.

Если хук «не сработал», проверьте по порядку: виден ли он в /hooks, исполняемый ли файл, есть ли в системе jq, правильный ли матчер (регистр имени инструмента важен) и валиден ли JSON настроек. Запуск с --debug показывает, какие хуки подобрались к событию и с каким кодом завершились.

Что запомнить

Хуки закрывают ровно ту дыру, которую не закрывают инструкции: детерминированность. Всё, что должно происходить всегда, — форматирование, линт, защита секретов, журналирование, уведомления — переносите из текстовых правил в хуки, а модели оставляйте то, что требует решения. Разумный порядок внедрения: начать с одного PostToolUse на форматтер, добавить PreToolUse на защиту чувствительных файлов, потом уведомления. Более тонкие вещи вроде подмены аргументов и prompt-хуков лучше подключать, когда база уже работает и вы понимаете, что именно в вашем процессе повторяется каждый день.

Если вы только начинаете с инструментом, полезно сначала пройти практический гайд по Claude Code и настроить правила проекта в CLAUDE.md — хуки хорошо ложатся поверх уже выстроенного процесса, но не заменяют его.

Частые вопросы

Q.Чем хук отличается от инструкции в CLAUDE.md?

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

Q.Как заблокировать опасное действие агента?

Только на событии PreToolUse: это единственная точка, где вызов инструмента ещё не выполнен. Скрипт должен завершиться с кодом 2 — тогда вызов блокируется, а текст из stderr уходит модели как объяснение. PostToolUse отменить действие уже не может.

Q.Можно ли раздать хуки всей команде?

Да. Положите конфигурацию в .claude/settings.json в корне репозитория и закоммитьте — хуки подхватятся у всех. Для организации целиком есть управляемые политики, а для переиспользования между проектами — плагины с файлом hooks/hooks.json.

Q.Насколько это безопасно?

Хук выполняется на вашей машине с вашими правами и без подтверждения, поэтому чужой .claude/settings.json нужно читать так же внимательно, как исполняемый код. Цитируйте пути в кавычках, не подставляйте данные из tool_input в команды без экранирования и держите под рукой аварийный выключатель disableAllHooks.

Источники

Предыдущая
Память у AI-агентов: как агент помнит контекст
Следующая
Кэширование в WordPress: обзор способов

Читайте также

Крупный план рабочего стола разработчика: монитор с диаграммой загрузки страницы, модули оперативной памяти и SSD на столе, тёмная графитовая комната с тёплой подсветкой сбокуЗаметки
11 августа 2026 г.

Кэширование в WordPress: обзор способов

Разбираем все уровни кэша в WordPress — от страниц и объектного кэша до OPcache и CDN, сравниваем WP Super Cache, W3 Total Cache, LiteSpeed Cache и WP Rocket и показываем, что кэшировать нельзя.

Читать →
Тёмный рабочий стол крупным планом: экран ноутбука с открытым файлом заметок и терминалом, рядом коробка с бумажными карточками, стикеры и клавиатураЗаметки
9 августа 2026 г.

Память у AI-агентов: как агент помнит контекст

Языковая модель не помнит ничего: каждый запрос она видит впервые. Всё, что выглядит как «память» у агента, разработчик собирает руками. Разбираем три уровня памяти, приёмы работы с растущим контекстом и конкретные инструменты — от memory tool в Claude API до CLAUDE.md и MCP-серверов.

Читать →
Мини-сервер с сетевыми кабелями и латунный замок на рабочем столе разработчика, на фоне — экран терминала с настройками доступа и логами; макросъёмка.Заметки
8 августа 2026 г.

Безопасность MCP-серверов: чек-лист

MCP-сервер даёт модели доступ к вашим данным и командам — а значит, становится мишенью. Разбираем модель угроз и собираем практический чек-лист: от аутентификации и валидации токенов до защиты от tool poisoning, SSRF и утечки секретов.

Читать →