# Хуки Claude Code: 9 шагов для защиты опасного файла через PreToolUse

> Хук Claude Code проверяет действие до изменения файла и может остановить опасный вызов. Показываю настройку защиты одного файла через PreToolUse.

Источник: https://vibeceh.ru/guides/huki-claude-code-ostanovit-opasnoe-izmenenie-fajla
Автор: Сергей Мазур · опубликовано 2026-08-17

Claude Code может переписать рабочий файл после одной неточной команды. Хук помогает **защитить файл**: он автоматически проверяет действие до запуска и останавливает изменение выбранного пути. Если нужен общий безопасный допуск агента к проекту, смотри [инструкцию о доступе Claude Code к проекту](/guides/claude-code-bezopasnyy-dopusk-agenta-k-proektu), а о хуках и блокировке опасных изменений читай [опорный материал о хуках Claude Code](/guides/huki-ostanovit-opasnye-izmeneniya-claude-code). Ниже я показываю минимальную схему, чтобы защитить файл без сложной архитектуры: один локальный скрипт, событие `PreToolUse`, инструменты `Edit|Write` и блокировка через `exit 2`.

## Что такое хук Claude Code и как защитить файл с его помощью?

Хук Claude Code помогает защитить файл: это локальная команда, которую программа запускает в определённой точке, например прямо перед действием. Перед изменением файла она получает JSON с параметрами вызова, проверяет путь и может остановить действие. Текстовая инструкция лишь просит модель соблюдать правило, а детерминированный command hook проверяет конкретный вызов независимо от того, вспомнила ли модель инструкцию. Prompt- и agent-hooks используют модельную оценку.

Если цель - сделать хук для конкретной опасной правки, не начинай с большого набора автоматизаций. Нужна одна проверка:

1. Claude собирается вызвать `Edit` или `Write`.
2. Claude Code запускает `PreToolUse`.
3. Локальный скрипт читает путь файла.
4. Скрипт сравнивает его с защищённым шаблоном.
5. При совпадении скрипт возвращает `exit 2`.
6. Вызов инструмента останавливается.

Anthropic описывает хуки как способ получить детерминированный контроль: команда запускается в фиксированной точке жизненного цикла. Это отличается от строки в `CLAUDE.md` или от сообщения в чате. Такая строка остаётся частью контекста модели. Модель может не выбрать нужное действие, неправильно понять исключение или перестать внимательно читать длинные инструкции.

По документации Anthropic, command hooks являются пользовательскими командами оболочки: Claude Code запускает их в определённых местах работы, поэтому ими можно задавать повторяемую проверку. Для правила с точным путём я использую именно такой детерминированный hook.
> - Anthropic, [Automate actions with hooks](https://code.claude.com/docs/en/hooks-guide)

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

Хук не делает Claude Code умнее. Он ставит маленькую заслонку перед конкретным действием. Если заслонка настроена на неправильное событие или возвращает неправильный код, защиты нет.

## Почему Claude Code может ошибиться даже после предупреждения?

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

Я бы разделял две задачи.

| Механизм | Что делает | Зависит от решения модели | Когда применять |
|---|---|---|---|
| Инструкция | Объясняет правило словами | Да | Для предпочтений и пояснений |
| Хук | Проверяет конкретный вызов до действия | Нет, если скрипт настроен верно | Для запрета изменения выбранного пути |

Поэтому инструкция объясняет, что делать, а хук проверяет, что действие действительно разрешено.

Модель может ошибиться по нескольким причинам:

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

У детерминированного command hook другая логика: он не рассуждает о задаче и не решает, хорошая ли правка. Он получает конкретный вызов инструмента. Если путь совпал с шаблоном, скрипт возвращает отказ. Prompt- и agent-hooks используют модельную оценку, поэтому это уточнение относится именно к command hook.

В руководстве Anthropic хуки описаны как механизм повторяемой технической проверки, которая не должна зависеть от выбора LLM.
> - Anthropic, [Automate actions with hooks](https://code.claude.com/docs/en/hooks-guide)

Там же Anthropic формулирует смысл механизма ещё жёстче: определённые действия должны происходить всегда. Их выполнение задаётся правилом и не зависит от решения LLM.

Поэтому маленький локальный сторож полезнее ещё одного длинного абзаца в инструкции. Чем уже правило, тем проще его проверить. Для сценария «этот файл не трогать» не нужен агент, который анализирует весь проект. Достаточно команды, которая сравнивает путь.

## Какой файл можно поставить под защиту?

Под защиту можно поставить один конкретный файл, набор файлов или шаблон пути. Если тебе нужен веб хук, уточню границу: здесь разбирается локальный хук Claude Code, который помогает защитить файл и проверяет вызов перед изменением. Проверяется путь из входных данных вызова, а не содержимое файла. Такой подход подходит для `.env`, `package-lock.json`, каталога `.git/` и любого чувствительного пути, который нельзя менять автоматически.

Запрос **защищенный режим файла** здесь означает не режим просмотра и не пароль. Claude Code получает запрет на конкретное действие с конкретным путём.

Начни с одного файла. Например:

```text
src/config/production.json
```

Скрипт не обязан читать этот файл и разбирать его содержимое. Ему достаточно получить путь из JSON, который Claude Code передаёт через стандартный ввод. Совпадение с заданным шаблоном означает блокировку.

В документации Claude Code в качестве примеров защищаемых путей рассматриваются `.env`, `package-lock.json` и `.git/`. Это варианты, а не обязательная последовательность: я начинаю с одного конкретного файла, а затем добавляю остальные пути только после проверки.

Можно выбрать и шаблон пути:

```text
src/config/*.json
```

Но широкое правило требует осторожности. Если закрыть слишком много файлов, Claude Code начнёт получать отказ на обычные действия. Слишком широкий matcher может блокировать больше операций, чем планировалось, поэтому сначала тестируй правило на одном пути.

Я бы двигался так:

1. Выбери один файл, который уже однажды было опасно менять.
2. Проверь точное написание пути относительно корня проекта.
3. Подключи защиту только к `Edit` и `Write`.
4. Проверь безопасный файл.
5. Проверь настоящий защищённый путь.
6. Только после этого добавляй `.env`, `.git/` или шаблон каталога.

Путь и содержимое - разные проверки. В минимальном сценарии не надо искать запрещённый текст внутри файла. Цель проще: не пропустить вызов инструмента, если он направлен на выбранный путь. Если хочешь собрать такой процесс вместе с другими правилами работы агента, посмотри практикум по вайб-кодингу для специалистов и фрилансеров. **Пройти практикум:** собери проверку на практике и сначала проверь безопасный файл, затем защищённый путь. Только после успешного теста расширяй правило.

## Как настроить хук для защиты файла по шагам?

Создай локальный скрипт, который читает `tool_input.file_path` из JSON, подключи его к событию `PreToolUse` и ограничь matcher инструментами `Edit|Write`. При совпадении пути выведи короткую причину в stderr и заверши скрипт через `exit 2`. Согласно разделу о кодах завершения в [официальном руководстве Claude Code по хукам](https://code.claude.com/docs/en/hooks-guide#exit-code-2), `exit 2` останавливает вызов инструмента.

![Кот с тревогой смотрит на три карточки шагов настройки хука.](https://s3.regru.cloud/crossmark/statejnik/images/guides/huki-claude-code-ostanovit-opasnoe-izmenenie-fajla/kadr-1.webp)

Выбирай путь относительно корня проекта, но помни: в JSON хук получает абсолютный `tool_input.file_path`. Сравнивай нормализованный абсолютный путь. В примере защищён файл `src/config/production.json`.

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

В проекте создай каталог `.claude/hooks`, а внутри файл `protect-file.sh`.

Скрипт читает весь JSON из стандартного ввода. Затем достаёт поле `tool_input.file_path`. Если путь совпал с защищённым, команда пишет короткое сообщение и возвращает `exit 2`.

   ```bash
   #!/usr/bin/env bash

   input="$(cat)"
   file_path="$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')"
   file_path="${file_path//\\//}"
   project_dir="${CLAUDE_PROJECT_DIR//\\//}"
   protected_path="$project_dir/src/config/production.json"

   if [[ "$file_path" == "$protected_path" || "$file_path" == "src/config/production.json" ]]; then
     printf '%s\n' "Blocked: protected file. Ask before editing." >&2
     exit 2
   fi

   exit 0
   ```

Для простого правила подходит `command` hook. Не подключай prompt- или agent-hook: здесь не требуется оценка смысла правки. Путь можно сравнить обычным условием.

На macOS или Linux выполни команду из корня проекта.

   ```bash
   chmod +x .claude/hooks/protect-file.sh
   ```

Если команда не подходит среде, сохрани сам скрипт и используй способ запуска, который принят в установленной оболочке. Формат конфигурации при этом остаётся тем же, но путь к команде должен реально запускаться в системе.

Открой проектную конфигурацию `.claude/settings.json` и добавь секцию `hooks`.

   ```json
   {
     "hooks": {
       "PreToolUse": [
         {
           "matcher": "Edit|Write",
           "hooks": [
             {
               "type": "command",
               "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-file.sh"
             }
           ]
         }
       ]
     }
   }
   ```

`PreToolUse` срабатывает до разрешения и выполнения вызова инструмента. `matcher` ограничивает проверку инструментами `Edit` и `Write`. Остальные действия этот скрипт не ловит.

В скрипте должно читаться именно поле `tool_input.file_path`.

Путь в настройке выбирай относительно корня проекта, но в JSON хук получает абсолютный `tool_input.file_path`; сравнивай нормализованный абсолютный путь. Содержимое файла и полный текст запроса для этой проверки не нужны. Хук должен работать с адресом файла, который передал инструмент. Так правило привязано к вызову инструмента, к его реальному пути.

Для остановки опасного вызова оставь в запрещённой ветке именно `exit 2`.

В запрещённой ветке оставь именно `exit 2`: одного `exit 1` для нужной блокировки недостаточно.

   В [разделе о кодах завершения руководства Claude Code по хукам](https://code.claude.com/docs/en/hooks-guide#exit-code-2) код `2` описан как способ остановить вызов инструмента до проверки правил разрешений. Проверь это поведение на тестовом файле.

   7. **Сохрани короткое сообщение.** Оставь одну понятную строку в stderr: `Blocked: protected file. Ask before editing.`

      Не печатай полный лог, трассировку и лишний JSON. При `exit 2` причина блокировки может передаваться Claude как feedback, поэтому я держу сообщение коротким. Оно объясняет причину и не засоряет следующие ходы.

   8. **Проверь конфигурацию и запусти Claude Code.** Убедись, что JSON не содержит лишних запятых и команда указывает на существующий файл. После этого открой интерфейс `/hooks` и посмотри, видит ли Claude Code подключённый `PreToolUse`.

      Затем попроси Claude изменить безопасный тестовый файл. После этого отдельно попроси изменить `src/config/production.json`. Первый вызов должен пройти, второй - остановиться сообщением из скрипта.

   9. **Расширяй список после теста.** Когда один файл защищён и проверка проходит, добавляй новые шаблоны в условие скрипта.

      Для `.env`, `package-lock.json` и `.git/` понадобятся отдельные сравнения пути. Не превращай условие в универсальный анализатор всего проекта. Узкое правило проще поддерживать и диагностировать.

## Как понять, что хук действительно установился?

Проверь не только JSON, но и загрузку события. Выполни `claude --version`, открой `/hooks`, затем протестируй безопасное изменение и отдельно реальный вызов `Edit` или `Write` по защищённому пути. Валидный `settings.json` сам по себе ничего не доказывает: Claude Code мог не загрузить matcher или событие.

![Сиба-ину поднимает лапу возле чек-листа проверки установленного хука.](https://s3.regru.cloud/crossmark/statejnik/images/guides/huki-claude-code-ostanovit-opasnoe-izmenenie-fajla/kadr-2.webp)

Запрос **установить хук** закрывается только после проверки поведения. Файл с правильными скобками - ещё не работающая защита.

Порядок проверки:

1. Выполни `claude --version`.
2. Запусти Claude Code из корня нужного проекта.
3. Открой `/hooks`.
4. Найди `PreToolUse` и matcher `Edit|Write`.
5. Попроси изменить безопасный тестовый файл.
6. Попроси изменить защищённый файл.
7. Убедись, что второе действие остановлено.

Безопасный тест проверяет, что команда вообще может пройти через событие. Реальный `Edit` или `Write` проверяет саму защиту. Это разные проверки, и одна не заменяет другую.

Проверка синтаксиса не равна проверке регистрации: открой `/hooks` и выполни тестовый вызов по безопасному и защищённому пути.

Если безопасный файл меняется, а защищённый тоже меняется, остановись. Не добавляй новые условия наугад. Сначала проверь `PreToolUse`, matcher, путь и код завершения.

## Что ломается, если хук вроде бы настроен, но файл всё равно меняется?

Чаще всего проблема в `exit 1`, неправильном событии, слишком широком или проигнорированном фильтре либо ошибке во входном JSON. Сначала проверь загрузку `PreToolUse` на `Edit|Write`, затем путь и код завершения. Я держу сообщение об ошибке коротким, чтобы не засорять следующие ходы.

![Мужчина закрывает лицо ладонью рядом с неверным кодом выхода и красным крестом.](https://s3.regru.cloud/crossmark/statejnik/images/guides/huki-claude-code-ostanovit-opasnoe-izmenenie-fajla/kadr-3.webp)

### `exit 1` вместо `exit 2`

Коротко: Для защиты файла через `PreToolUse` в запрещённой ветке оставь именно `exit 2`. В [официальном руководстве Claude Code по хукам](https://code.claude.com/docs/en/hooks-guide#exit-code-2) этот код описан как способ остановить вызов инструмента, тогда как `exit 1` сообщает об ошибке hook и сам по себе не задаёт нужную блокировку. Я проверяю оба результата на тестовом файле: безопасный путь получает `exit 0`, защищённый путь останавливается с `exit 2`.

```bash
printf '%s\n' "Blocked: protected file. Ask before editing." >&2
exit 2
```

Не делай так:

```bash
printf '%s\n' "Blocked: protected file." >&2
exit 1
```

Один из практических разборов [о хуках Claude Code](https://israynotarray.com/en/ai/2026/05/31/claude-code-hooks-complete-guide/) предупреждает, что новички ошибочно используют `exit 1` вместо `exit 2`. Я поэтому проверяю код возврата отдельно на безопасном и защищённом пути.

### Выбран не тот event

Коротко: Если выбран `PostToolUse`, запрет не сработает до изменения. Для защиты файла используй `PreToolUse`.

`PostToolUse` запускается после вызова. Он может сообщить о результате, но не должен быть основой запрета до изменения.

Проверяй именно тот event, который участвует в правиле. Другой hook может работать, пока `PreToolUse` не запускается.

### Matcher слишком широкий или не работает

Коротко: Matcher `Edit|Write` ограничивает защиту нужными инструментами. Если убрать фильтр, скрипт начнёт получать лишние вызовы, поэтому для первого варианта оставь простой matcher и проверяй путь внутри скрипта.

Для первого варианта не полагайся на поздние фильтры. Оставь простой matcher и проверяй путь внутри скрипта.

### JSON читается неправильно

Коротко: Проверь поле `tool_input.file_path`: если скрипт ищет путь в другом месте, переменная `file_path` останется пустой, условие не совпадёт, и скрипт вернёт `exit 0`.

### Как сделать сообщение об ошибке коротким?

Коротко: При `exit 2` причина блокировки может передаваться Claude как feedback, поэтому сообщение лучше делать коротким. Я оставляю в stderr одну строку с причиной отказа, проверяю её на защищённом пути и не добавляю трассировку, полный JSON или отладочные данные.

При `exit 2` причина блокировки может передаваться Claude как feedback, поэтому сообщение лучше делать коротким.

Поэтому блокирующее сообщение должно быть коротким. Причина, действие и ничего лишнего:

```text
Blocked: protected file. Ask before editing.
```

Ненулевой код сам по себе не означает блокировку. Для остановки вызова оставь `exit 2`, а перед этим проверь, что сработал именно `PreToolUse`.

## Как не превратить защиту файла в новую дыру?

Хук - исполняемый код, не безобидная настройка. Не принимай автоматически `.claude/settings.json` и хуки из чужого репозитория. Держи правило узким, проверяй команду и не считай hook заменой ограничениям среды выполнения, файловой системы и сети.

Чужой проект может содержать `.claude/settings.json` с командой, которую Claude Code запустит при старте. Официальная [инструкция по безопасному допуску Claude Code к проекту](/guides/claude-code-bezopasnyy-dopusk-agenta-k-proektu) предупреждает: настройки проекта и команды hooks нужно проверить до запуска чужой папки.

В материале [о сдерживании Claude](https://www.anthropic.com/engineering/how-we-contain-claude) Anthropic рекомендует учитывать, какие настройки и команды запускаются при работе с проектом. Поэтому перед запуском чужого проекта проверь настройки и команды hooks.

Перед запуском чужого проекта проверь:

1. `.claude/settings.json`.
2. `.claude/settings.local.json`.
3. Команды внутри секции `hooks`.
4. Файлы скриптов, на которые ссылается конфигурация.

Для своей защиты оставь маленький `command` hook с одной понятной проверкой. В [документации Claude Code по хукам](https://code.claude.com/docs/en/hooks-guide) agent-hooks помечены как экспериментальные; для проверки точного пути я использую command hook.

Один hook не закрывает всю систему. Он защищает выбранный вызов в Claude Code. Для настройки разрешений перед подключением GitHub смотри [отдельную инструкцию по разрешениям Claude Code](/guides/claude-code-zakryt-put-k-opasnym-github-deystviyam). Права операционной системы и сетевые ограничения решают другие задачи. Если агент получил лишний доступ, запрет на один путь не исправит остальные риски.

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

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

Чтобы защитить файл, нужен локальный command hook на событии `PreToolUse`. Он проверяет путь в `tool_input.file_path`, пропускает обычные файлы и возвращает `exit 2` для защищённого пути. После настройки проверь загрузку через `/hooks`, безопасный вызов и отдельную попытку изменить защищённый файл. Если защищённый путь меняется, проверь matcher, абсолютный путь и код завершения.

Создай локальный command hook на событии `PreToolUse`. Ограничь matcher значениями `Edit|Write`, прочитай `tool_input.file_path`, сравни путь с защищённым шаблоном и верни `exit 2`. После настройки проверь безопасный файл и отдельный вызов по настоящему защищённому пути.

В `.claude/settings.json` нужна секция `hooks` с событием `PreToolUse`, matcher `Edit|Write` и command hook, который запускает локальный скрипт. Сам скрипт читает JSON из стандартного ввода и проверяет поле `tool_input.file_path`.

Выполни `claude --version`, открой `/hooks` и найди подключённый `PreToolUse`. Затем проверь изменение тестового файла и отдельно попробуй изменить защищённый файл через `Edit` или `Write`. Валидный JSON без такого теста не подтверждает загрузку.

Нет. Для сравнения пути подходит простой `command` hook. Prompt- и agent-hooks не нужны для правила с точным совпадением. Документация Claude Code помечает agent-hooks как экспериментальные, а здесь достаточно command hook, который сравнивает путь без модельной оценки.

Скрипт проверяет путь файла из `tool_input.file_path`, а не содержимое. Под защиту можно поставить один выбранный файл, `.env`, `package-lock.json`, `.git/` или шаблон пути. Начинать безопаснее с одного конкретного файла.

Хороший hook узкий и проверяемый. Он ловит только нужные инструменты, читает конкретное поле JSON, выводит короткую причину и возвращает `exit 2`. Такой hook проще проверить и сложнее случайно применить к лишним действиям.

Для этой задачи нужен `PreToolUse`. Он запускается до вызова инструмента и способен заблокировать его. В статье используются `Edit` и `Write`. Другие события не нужны для минимальной защиты конкретного пути.

Актуальное описание находится на странице [хук официальный сайт Claude Code](https://code.claude.com/docs/en/hooks): там `PreToolUse` описан как событие до выполнения вызова, способное его заблокировать. Проверяй версию документации перед настройкой. Перед публикацией я сначала проверяю безопасный файл, затем защищённый путь через `Edit` и `Write`. Только после успешного теста добавляю новые шаблоны. > **Пройти практикум.** Собери такой инструмент на практике: практикум по вайб-кодингу для специалистов и фрилансеров.

- [Automate actions with hooks - Claude Code Docs](https://code.claude.com/docs/en/hooks-guide)
- [Configure permissions - Claude Code Docs](https://code.claude.com/docs/en/permissions)
- [Hooks reference - Claude Code Docs](https://code.claude.com/docs/en/hooks)
- [How we contain Claude across products](https://www.anthropic.com/engineering/how-we-contain-claude)
- [What Are Claude Code Hooks?](https://israynotarray.com/en/ai/2026/05/31/claude-code-hooks-complete-guide/)
