Блог

Хуки в Claude Code: 7 примеров для settings.json

Markdown

Практическая инструкция по хукам Claude Code: настройка через .claude/settings.json, 4 типа обработчиков, 31 событие и 7 примеров для защиты .env, форматирования, Git и контекста сессии.

Читать статью полностью

Хуки Claude Code — это shell-команды, HTTP-запросы или короткие проверки с помощью LLM, которые автоматически запускаются до или после действия Claude. Они помогают обеспечить правила, на которые нельзя полагаться в одном лишь CLAUDE.md: например, запретить удаление .env, блокировать force-push в main, форматировать изменённый код или добавлять актуальный контекст при старте сессии. В статье разобраны 4 типа обработчиков, 31 событие жизненного цикла, способ определить, какое правило стоит вынести в hook, 7 готовых примеров для .claude/settings.json и 3 частые проблемы.

Документация Ailoot: API, ключи и подключение.

Что такое хуки в Claude Code?

Ключевой вывод

Хук — это shell-команда, HTTP-запрос, короткий LLM-промпт или субагент, запускаемый Claude Code при определённом событии жизненного цикла. В отличие от текста в CLAUDE.md, который модель может упустить из внимания, хук при наступлении события выполняется по заданным правилам: это код, а не просьба.

Правило «не редактировать .env напрямую» долго оставалось у меня в CLAUDE.md, но в продолжительных сессиях Claude иногда его забывал. Однажды в файл попал лишний токен, а в другой раз переменная с повторяющимся именем перезаписала рабочую.

При совпавшем прямом Edit с путём .env hook отклоняет этот вызов. Перенаправление Bash, скрипты, MCP и другие пути требуют отдельных ограничений. Файл не становится неизменяемым.

Хуки — это заданные вами shell-команды, HTTP-endpoints или LLM-prompts, которые автоматически запускаются в определённых точках жизненного цикла Claude Code.

— Anthropic, https://code.claude.com/docs/en/hooks

Проще говоря, хук — ваша команда, которую Claude Code запускает при заданном событии. Таким событием может быть редактирование файла, запуск Bash, начало сессии или завершение субагента.

Хуки добавляют проверки отдельных событий, форматирование после правки и свежий контекст сессии. Совпадающий PreToolUse может отклонить вызов; regex-примеры — удобные ограничения конкретных путей, не полная защита доступа к файлам и Git. Примеры:

  • Защищать файлы с секретами от случайных изменений.
  • Запускать prettier или eslint после генерации кода.
  • Запрещать git push --force в main без явного разрешения.
  • Добавлять свежий контекст при старте каждой сессии.
  • Записывать действия Claude для последующего аудита.

CLAUDE.md задаёт контекст, а хуки управляют действиями. Это разные механизмы, и смешивать их назначение — распространённая ошибка.

Чем хуки отличаются от CLAUDE.md и скиллов?

Ключевой вывод

CLAUDE.md содержит текст, который модель читает в начале сессии и старается учитывать. Хук — команда, запускаемая процессом Claude Code при событии, независимо от модели. Скилл — набор инструкций с именем, который Claude может вызвать через инструмент Skill. У каждого из этих механизмов своя задача.

Anthropic отдельно объясняет различие между CLAUDE.md и хуками:

Содержимое CLAUDE.md поступает как user message после system prompt, а не включается в system prompt. Claude читает его и старается следовать указаниям, однако строгой гарантии соблюдения нет.

— Anthropic, документация по памяти, https://code.claude.com/docs/en/memory

Иными словами, содержимое CLAUDE.md приходит Claude как начальное сообщение пользователя, а не как системная инструкция. Модель прочитает его и постарается учесть, но гарантировать выполнение нельзя. В длинной сессии контекст увеличивается, и ранние инструкции могут потеряться.

Hook запускается процессом Claude Code по событию. PreToolUse может проверить и отклонить ещё не выполненный вызов, PostToolUse обрабатывает результат уже выполненного действия. Не приравнивайте обратную связь после записи к запрету самой записи.

ПризнакCLAUDE.mdHookSkill
Где хранитсяCLAUDE.md в корне проекта.claude/settings.jsonПапка .claude/skills/
Кто запускаетМодель читает как часть user messageПроцесс Claude Code при событииМодель через инструмент Skill
Насколько гарантирован запускНет, выполнение зависит от моделиСовпавший настроенный command-hook выполняет код; отказ и exit-поведение зависят от событияВызывается моделью; грант есть, но на уровне shell гарантий нет
Для чего подходитКонтекст проекта, тон, стиль, маршрутизацияЗапреты, форматирование и проверки отдельных файловСложные многоэтапные процедуры с параметрами
Когда срабатываетПри каждом сообщении модели в сессииПри событии жизненного циклаПо вызову модели
Может ли заблокировать действиеНетСовпавший PreToolUse с exit2; PostToolUse только сообщает после выполненияНет

Шаблон CLAUDE.md и правила его поддержки приведены в гайде Как настроить CLAUDE.md в 2026: готовый шаблон и 6 правил. О создании именованных навыков читайте в статье Claude Code Skills: как собрать библиотеку под себя.

Хук работает на нижнем уровне: это обычная shell-команда, которой не требуется интерпретация модели.

Хуки — один из инструментов контекст-инжиниринга, наряду с Plan Mode и /compact.

4 типа хуков: Command, HTTP, Prompt, Agent — какой выбрать?

Ключевой вывод

В Claude Code доступны 4 типа обработчиков. Command запускает shell-скрипт и подходит для большинства проверок. HTTP отправляет POST-запрос на заданный URL — например, для командных интеграций. Prompt передаёт короткий запрос модели Haiku для семантической оценки. Agent запускает субагента с доступом к инструментам для более глубокой проверки. Для начала обычно достаточно Command и Prompt.

ТипЧто делаетКогда подходитСложность
CommandПередаёт JSON через stdin shell-команде; результат задаётся exit code и stdoutПроверка путей, форматирование, поиск по содержимому, lsof и операции Gitнизкая
HTTPОтправляет JSON в теле POST-запроса на заданный URL и получает JSON-ответДля командных процессов и интеграций: общий валидатор, панель статусов или удалённый аудитсредняя (нужен сервер)
PromptОтправляет короткий промпт в Haiku (по умолчанию) и получает ответ для семантической оценкиКогда поиска по текстовому шаблону недостаточно, например для оценки корпоративного тонанизкая
AgentЗапускает субагента с доступом к Read/Grep/Glob для проверки в несколько шаговДля сложных задач, например проверки соответствия изменённых файлов тестамвысокая

Частая ошибка — пытаться решить с помощью Command задачу, требующую семантической оценки. Правило «не использовать длинное тире в .md» можно проверить через grep, поэтому здесь подходит Command. А оценка «не писать корпоративным тоном» требует понимания смысла и лучше подходит для Prompt.

Для начала попробуйте Command: все 7 примеров ниже относятся к этому типу. HTTP, Prompt и Agent можно подключить позже, когда возникнет подходящая задача.

Где находится settings.json и какие есть 4 уровня конфигурации?

Ключевой вывод

Claude Code использует конфигурации четырёх уровней: корпоративную, распространяемую через MDM; пользовательскую (~/.claude/settings.json); проектную (.claude/settings.json в корне репозитория); локальную (.claude/settings.local.json, не включаемую в Git). При запуске сессии файлы объединяются, а при конфликте приоритет получает более низкий уровень.

УровеньПутьГде применяетсяВ git?
Корпоративный/Library/Application Support/ClaudeCode/settings.json (Mac), /etc/claude-code/settings.json (Linux)Для команды через MDMнет
Пользовательский~/.claude/settings.jsonВо всех проектах пользователянет
Проектный.claude/settings.json в корне репозиторияВ текущем проекте для всей командыда
Локальный.claude/settings.local.jsonТолько в этом проекте и для этого пользователянет (в .gitignore)

Я размещаю все 7 описанных ниже хуков в проектном .claude/settings.json. Тогда при клонировании репозитория настройки доступны команде, и Claude применяет общие правила. Личные эксперименты храню в .claude/settings.local.json.

Все четыре файла объединяются при запуске сессии. Если настройки конфликтуют, приоритет у более низкого уровня: локальный важнее проектного, а проектный — пользовательского.

Самый быстрый способ добавить первый хук — открыть в Claude Code меню /hooks. Выберите событие и matcher, например Bash или Edit, затем задайте команду. Claude Code сохранит корректную структуру JSON в settings.json.

31 событие жизненного цикла: что пригодится начинающим?

Ключевой вывод

В Claude Code указано 31 событие жизненного цикла. Они происходят с разной частотой: один раз за сессию (SessionStart/SessionEnd), один раз за ход (UserPromptSubmit/Stop) или при вызове инструмента (PreToolUse/PostToolUse). Для начала обратите внимание на четыре: PreToolUse для запретов, PostToolUse для изменений после действия, SessionStart для загрузки контекста и Stop для финальной проверки.

События возникают с тремя частотами: один раз за сессию (SessionStart, SessionEnd), один раз за turn (UserPromptSubmit, Stop, StopFailure) и при каждом tool call внутри agentic loop (PreToolUse, PostToolUse).

— Anthropic, справочник по хукам, https://code.claude.com/docs/en/hooks

Чтобы выбрать подходящее событие, полезно различать три частоты срабатывания:

  1. Один раз за сессию — SessionStart, SessionEnd, Setup. Эти события возникают при открытии или закрытии Claude Code и подходят для инициализации контекста или очистки временных файлов.
  2. Один раз за ход — UserPromptSubmit, UserPromptExpansion, Stop, StopFailure. Они срабатывают при отправке сообщения и остановке модели; их можно использовать для записи диалога и итоговой проверки результата.
  3. При каждом вызове инструмента — PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied, PostToolBatch, SubagentStart, SubagentStop. Такие события возникают при действиях вроде редактирования файла, запуска Bash, чтения и поиска. Здесь размещают запреты, форматирование и проверки.

Среди 31 события есть также асинхронные и реактивные: TeammateIdle, PreCompact, PostCompact, Elicitation, ElicitationResult, InstructionsLoaded, ConfigChange, CwdChanged, FileChanged, WorktreeCreate, WorktreeRemove, Notification. Они нужны для более специализированных сценариев.

Проверка из 5 вопросов: когда правило переносить в hook, а когда оставить в CLAUDE.md?

Ключевой вывод

Переносите правило в хук, если оно относится к действиям, касается безопасности, денег или данных, поддаётся проверке через grep или regex, а нарушение сложно отменить либо Claude уже забывал это правило. Если утвердительно ответили менее чем на три из пяти вопросов, оставьте его в CLAUDE.md. При разборе своего файла на 80 строк автор перенёс в хуки 5 правил.

Я использую пять вопросов, чтобы решить, где хранить правило — в CLAUDE.md или в хуке. Если набирается не менее трёх ответов «да», правило подходит для хука. При меньшем числе ответов его можно оставить в текстовом файле.

Проверьте, стоит ли перенести правило в hook:

  1. Оно задаёт действие, а не описывает контекст? («не запускай rm -rf» — действие; «проект написан на TypeScript» — контекст.)
  2. Оно связано с безопасностью, деньгами или данными? Например, «не меняй .env» или «не публикуй ключи».
  3. Его можно проверить через grep, regex или exit code shell-команды? Например, проверить длинное тире после редактирования .md.
  4. Нарушение будет сложно исправить? Например, force push в main отменить сложнее, чем пропущенную точку в конце предложения.
  5. Вы уже замечали, что Claude забывает это правило?

Пример: «не используй длинное тире, пиши обычный дефис». Это правило о действии — да. О безопасности — нет. Проверяется через grep — да. Последствия сложно отменить — нет. Нарушение уже встречалось — да. 3 ответа из 5: правило подходит для хука.

Контрпример: «пиши без сленга для пользователей, которые не занимаются разработкой». Это скорее контекст, чем действие. К безопасности не относится. Надёжно проверить regex сложно. Нарушение легко исправить. Даже если оно уже случалось, получается 1 ответ из 5 — правило остаётся в CLAUDE.md.

В описанном примере проверку прошли 5 правил, которые автор перенёс в хуки. Остальные 75% остались в текстовом файле.

7 готовых хуков для копирования

Ключевой вывод

Ниже приведены 7 примеров для .claude/settings.json: запрет на изменение .env, запуск prettier, блокировка force-push в main, проверка длинного тире, освобождение порта перед npm run dev, автоматический коммит после успешных тестов и добавление свежих сведений при старте сессии. Вставьте выбранный пример в файл настроек и перезапустите Claude Code.

Все семь хуков размещаются в .claude/settings.json в корне проекта. Каждый содержит событие, фильтр инструмента (matcher) и shell-команду. Exit2 отклоняет совпавший PreToolUse до исполнения. В PostToolUse действие уже произошло: hook сообщает обратную связь, не отменяя его.

Для большинства событий блокировку вызывает только exit code 2. Claude Code считает exit code 1 неблокирующей ошибкой и продолжает действие, хотя в Unix-контексте 1 обычно означает ошибку.

— Anthropic, справочник по хукам, https://code.claude.com/docs/en/hooks

exit 0 означает успешное выполнение. exit 1 сообщает об ошибке, но не останавливает Claude — это первая ловушка, описанная ниже. exit 2 зависит от события: PreToolUse может блокироваться, а PostToolUse сообщает о уже выполненном действии.

Хук1. Проверка прямой правки .env (PreToolUse, exit2)

Этот хук блокирует прямое редактирование файлов .env, .env.local, .env.production и других имён, соответствующих шаблону.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '(^|/)\\.env(\\.|$)' && { echo 'Запрещено редактировать .env напрямую. Скажи мне какую переменную добавить, я сам открою файл.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

Как работает: перед Edit, Write или MultiEdit команда получает путь, проверяет его через grep и при совпадении с шаблоном .env возвращает exit 2 с пояснением. Claude получает stderr и сообщает, что попытка изменения заблокирована.

Хук 2. Запуск prettier после редактирования кода (PostToolUse, exit 0)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(ts|tsx|js|jsx|json)$' && npx prettier --write \"$FILE\" 2>/dev/null & exit 0"
          }
        ]
      }
    ]
  }
}

Как работает: после Edit или Write команда проверяет расширение файла. Для .ts, .tsx, .js, .jsx и .json она запускает prettier в фоне через &. Хук всегда возвращает exit 0: форматирование не должно блокировать действие. Если prettier завершится с ошибкой, исходный файл останется без этой дополнительной обработки.

Хук 1 срабатывает на PreToolUse и exit 2, поэтому блокирует действие до его выполнения. Хук 2 запускается на PostToolUse и возвращает exit 0, чтобы обработать результат после редактирования. Эти режимы предназначены для разных задач.

Хук 3. Запрет git push --force в main (PreToolUse, exit 2)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE 'git[[:space:]]+push' && echo \"$CMD\" | grep -qE '(\\-\\-force|\\-\\-force-with-lease|[[:space:]]-f([[:space:]]|$))' && echo \"$CMD\" | grep -qE '([[:space:]/:])main([[:space:]/:]|$)' && { echo 'Force push в main запрещён. Сделай новую ветку или попроси меня вручную.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

Как работает: перед запуском Bash проверяется, содержит ли команда push, флаг --force, --force-with-lease или -f, а также указание на main. Если совпали все три условия, хук останавливает действие и сообщает причину.

В исходном примере автор хранил это правило в CLAUDE.md, но Claude иногда его нарушал при просьбе выполнить force push для отката неудачного коммита. Hook ловит показанные буквальные команды. Алиасы, скрипты, неявная ветка назначения и другой Git-клиент обходят строковую проверку. Защитите main на сервере и ограничьте ключи агента.

Хук 4. Поиск длинного тире в текстовых файлах (PostToolUse, exit 2)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(md|mdx|tsx?)$' && grep -q $'\\xe2\\x80\\x94' \"$FILE\" && { echo \"В файле $FILE найдено длинное тире (U+2014). Замени на обычный дефис (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

Как работает: PostToolUse ищет длинное тире после уже выполненной правки. Exit2 сообщает Claude об ошибке последующей проверки, но не отменяет и не предотвращает запись. Затем можно исправить символ.

Команда ищет именно символ U+2014 через bash-запись $'\xe2\x80\x94', задающую его байты в UTF-8. Если искать обычный дефис с помощью grep -q '-', проверка будет срабатывать на каждом файле, где есть дефис.

Хук 5. Освобождение порта 3000 перед npm run dev (PreToolUse, exit 0)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE '(npm|pnpm|yarn)[[:space:]]+run[[:space:]]+dev' && { lsof -ti:3000 | xargs -r kill -9 2>/dev/null; echo 'Порт 3000 освобождён перед запуском' >&2; }; exit 0"
          }
        ]
      }
    ]
  }
}

Как работает: перед командой npm run dev, pnpm run dev или yarn run dev хук завершает процесс, занимающий порт 3000. Он всегда возвращает exit 0, поскольку подготавливает среду, а не запрещает действие. Это помогает избежать ситуации, когда запущенный ранее сервер мешает новому запуску.

Хук 6. Автоматический коммит после успешных тестов (PostToolUse, exit 0)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat); CMD=$(printf '%s' \"$INPUT\" | jq -r '.tool_input.command // empty') || exit 1; EXIT=$(printf '%s' \"$INPUT\" | jq -r '.tool_response.exit_code // empty') || exit 1; case \"$CMD\" in 'npm test'|'pnpm test'|'yarn test') ;; *) exit 0 ;; esac; [ \"$EXIT\" = \"0\" ] || exit 0; git diff --cached --quiet || { echo 'Existing staged changes: review manually' >&2; exit 0; }; git add -- src/orders.ts tests/orders.test.ts || exit 1; git diff --cached --quiet && exit 0; git commit -m 'tests: checkpoint after successful run'"
          }
        ]
      }
    ]
  }
}

Как работает: читает JSON stdin один раз, принимает только показанные точные команды тестов и продолжает лишь при явном tool_response.exit_code=0. При отсутствии поля в вашей версии пропускает коммит, не угадывая успех. Замените src/orders.ts и tests/orders.test.ts точными файлами задачи. Уже добавленные в индекс изменения останавливают рецепт, ошибка git add тоже; пустой diff не создаёт коммит. Обычные проверки коммита включены. До активации офлайн испытайте успех, отказ и отсутствующий результат.

Не добавляйте git commit --no-verify к каждому Edit. Проверки коммита оставьте включёнными. При рекурсии сузьте триггер или измените проверку вместо стандартного обхода. До автокоммитов изучите индекс и владение задачей.

Хук 7. Добавление актуального контекста при запуске (SessionStart и additionalContext)

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo 'no-branch'); STATUS=$(git status --short 2>/dev/null | head -10); echo \"{\\\"additionalContext\\\":\\\"Текущая ветка: $BRANCH. Незакоммиченные изменения:\\n$STATUS\\\"}\""
          }
        ]
      }
    ]
  }
}

Как работает: при старте сессии команда получает текущую ветку и первые десять строк списка незакоммиченных изменений. Результат возвращается в JSON в поле additionalContext, которое Claude Code добавляет в контекст сессии.

Используйте additionalContext для сведений о текущем состоянии среды или только что выполненной операции. Например, передавайте текущую ветку, цель развёртывания, активные feature flags, правила проекта для тестирования изменённого файла или внешние данные — открытые issues и последние результаты CI.

— Anthropic, справочник по хукам, https://code.claude.com/docs/en/hooks

В этот хук также можно добавить сведения о последнем коммите, статусе GitHub Actions, текущем спринте в Linear или Jira и открытых задачах репозитория.

3 частые ошибки при настройке хуков

Ключевой вывод

Первая проблема: exit 1 может не остановить действие, и Claude продолжит работу. Вторая: длительная команда может превысить таймаут в 60 секунд. Третья: хук получает окружение процесса Claude Code, но не обязательно переменные из .env проекта. Учитывайте эти особенности при настройке и проверке команд.

Ошибка 1. Хук с exit 1 не блокирует действие

Если hook-команда завершилась с exit 1, Claude Code может воспринять это как неблокирующую ошибку и продолжить действие. Например, проверка пути через jq -r '.tool_input.file_path' может завершиться ошибкой, если значение пустое или равно null. Тогда защитный хук не остановит редактирование .env.

Чтобы избежать этого, проверяйте входные значения и заранее задавайте поведение для пустого поля. Не рассчитывайте, что exit 1 заблокирует действие: для блокировки используйте предусмотренный для неё exit code.

# Шаблон безопасного начала и конца:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... твоя логика ...
exit 0   # или || exit 0 в одну строку

Ошибка 2. Хук превышает таймаут

Если после редактирования запускать eslint --fix для большого файла, обработка может занять несколько секунд. В документации Claude Code указан стандартный таймаут хука в 60 секунд. При серии действий длительные проверки могут задержать работу и завершиться по таймауту.

Для тяжёлой фоновой задачи можно запустить команду с &, чтобы хук не ждал её завершения:

# Тяжёлая операция в background:
npx eslint --fix "$FILE" &
exit 0

Ошибка 3. Хук не получает переменные из окружения проекта автоматически

Хук запускается в shell-процессе Claude Code. Он видит переменные окружения, доступные этому процессу, но значения из .env проекта автоматически не появляются. Если команда зависит, например, от OPENAI_API_KEY из .env, файл нужно загрузить явно.

Решение: подключите .env в команде хука или используйте инструмент загрузки переменных окружения.

# Прокидывание env проекта в hook:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# Или через dotenv:
dotenv -e .env -- node my-validator.js

В монорепозитории разные пакеты могут использовать отдельные .env. В таком случае укажите рабочую папку явно и выполните cd "$PWD" в начале команды.

Как проверить хуки до коммита?

Ключевой вывод

Хук можно запустить как обычную shell-команду: передать тестовый JSON через stdin и проверить exit code и stderr. Такая проверка помогает обнаружить ошибки до того, как команда будет запущена Claude Code.

Простейшая ручная проверка. Скопируйте команду из JSON, откройте терминал и передайте ей JSON, аналогичный входным данным Claude Code:

# Тест хука 1 (защита .env):
echo '{"tool_input":{"file_path":".env"}}' | bash -c '
FILE=$(jq -r ".tool_input.file_path // empty")
[ -z "$FILE" ] && exit 0
echo "$FILE" | grep -qE "(^|/)\.env(\.|$)" && {
  echo "BLOCKED" >&2
  exit 2
} || exit 0
'
echo "Exit code: $?"

Ожидаемый результат — BLOCKED в stderr и Exit code: 2. Если команда вернула exit 0, проверьте шаблон grep.

Проверьте три ситуации:

  1. Ожидаемая блокировка — .env для хука 1 и git push --force main для хука 3.
  2. Допустимое действие — README.md для хука 1 и git push origin feature-branch для хука 3.
  3. Граничные случаи — пустой ввод, null и неожиданные символы. Хук должен завершиться с подходящим кодом или выдать понятную ошибку, а не молча пропустить проблему.

Где проверять экспериментальные хуки. Для проб используйте .claude/settings.local.json, который не включается в Git. Например, временно добавьте в PostToolUse команду с echo, чтобы выяснить, срабатывает ли matcher, прежде чем включать настоящее действие в проектный settings.json.

// Эксперимент в settings.local.json - пишет в /tmp/hook-debug.log:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[hook fired] $(date) $(jq -r '.tool_input.file_path // \"?\"')\" >> /tmp/hook-debug.log"
          }
        ]
      }
    ]
  }
}

После нескольких правок проверьте /tmp/hook-debug.log. В нём должны появиться файлы, для которых сработал хук. Если журнал пуст, проверьте matcher и настройки события.

Что делать дальше?

Ключевой вывод

Начните с PostToolUse и exit 0: такие хуки выполняют действие после инструмента, не блокируя его. Позже добавьте PreToolUse и exit 2 для ограничений вроде защиты .env или запрета force-push. По мере переноса проверяемых ограничений в хуки CLAUDE.md можно оставить для описания контекста проекта.

На ближайшую неделю можно запланировать пять шагов:

  1. Просмотрите CLAUDE.md и найдите слова «никогда», «запрещено», «всегда» и «обязательно». Такие формулировки могут указывать на правила, которые стоит проверять автоматически.
  2. Начните с PostToolUse и exit 0. Это более мягкий вариант: он выполняет действие после инструмента и не блокирует его. Примеры — prettier (хук 2), освобождение порта (хук 5) и коммит после тестов (хук 6).
  3. Добавляйте PreToolUse с exit 2, когда готовы проверять блокировки. Например, можно запретить изменение .env (хук 1) или force-push (хук 3). Перед использованием тщательно проверьте grep: ошибочное условие может остановить допустимое действие.
  4. Проверяйте команды вручную перед коммитом, как описано выше. Это помогает находить ошибки до запуска через Claude Code.
  5. Используйте settings.local.json для экспериментов. На первых этапах замените настоящее действие командой echo.

После настройки хуков можно прочитать, как откатить изменения в Claude Code, если проверка пропустила нежелательное изменение. Для работы с контекстом проекта между сессиями см. Второй мозг в Claude Code. Переиспользуемые инструкции проекта описаны в гайде Claude Code Skills.

Если вам нужен доступ к нейросетям через Ailoot, ознакомьтесь с Ailoot.

Подробнее

Вопросы и ответы

Почему hook должен прочитать входной JSON только один раз?

Stdin — поток: первый jq потребляет его, следующие получают EOF. Сохраните вход и извлекайте поля команды и результата из копии. Неизвестный или отсутствующий результат непроверен: автокоммит нужно пропустить.

Создают ли regex-примеры hooks границу безопасности?

Нет. Они проверяют отдельные события и узнаваемые строки. Перенаправления оболочки, алиасы, скрипты, MCP и другой путь записи могут обойти их. Для жёстких пределов нужны права файлов, ограниченные ключи, sandbox и защищённые ветки.

Все статьи