Мастер Claude · Блок 5. Hooks

Урок 47 из 93 · ~10 мин чтения

Prompt hooks — модификация запроса налету

Урок пока закрыт

Курс проходится последовательно.

К текущему уроку

Почему это важно именно вам

Каждый раз, когда вы начинаете диалог с Claude по рабочей задаче, вы мысленно готовите преамбулу. «Сегодня вторник, у нас совет директоров в пятницу, поэтому нужно...» или «Сейчас конец месяца, закрываем отчётный период, поэтому цифры нужны за май...». Этот контекст вы каждый раз добавляете вручную — или забываете добавить, и тогда получаете ответ без учёта ситуации.

Или представьте: вы ведёте переписку с несколькими отделами и хотите, чтобы Claude всегда знал, что писать нужно в корпоративном стиле — без сленга, с обращением на «вы», с подписью. Каждый раз объяснять это заново — раздражает. Вынести в CLAUDE.md — помогает, но не всегда: иногда нужно добавлять что-то динамическое, зависящее от текущего момента: дня недели, времени суток, статуса задачи.

Prompt hooks решают именно эту задачу. Вместо того чтобы каждый раз вручную дописывать контекст, вы один раз описываете правило: «перед каждым моим запросом — добавь вот эту информацию». Claude получает расширенный промт автоматически. Вы этого не видите, не думаете об этом — оно просто работает. Это как шаблон письма в почтовом клиенте: вы пишете только суть, а подпись, тема и копии подставляются сами.

Что это такое

В жизненном цикле Claude Code есть несколько ключевых событий. Одно из них — UserPromptSubmit: момент, когда вы нажали Enter и ваш запрос отправился к Claude, но Claude ещё не начал отвечать. Это идеальная точка для вмешательства: запрос уже сформирован, но его ещё можно дополнить.

Добавить контекст в этой точке умеет хук типа command: он печатает JSON с полем additionalContext, и этот текст Claude видит вместе с вашим запросом. Схема простая: ваш текст плюс additionalContext от хука — и это уходит модели. Тип prompt устроен иначе: он не выполняет команду, а одним обращением к модели выносит вердикт {"ok": true/false}. Схема простая: ваш текст + текст от hook = финальный промт для Claude.

Хук на `UserPromptSubmit` — это не подсказка Claude, а добавка к вашему запросу

Разница принципиальная. Инструкция в CLAUDE.md говорит Claude «веди себя так всегда». Хук типа command на UserPromptSubmit говорит «к этому конкретному запросу добавь вот это прямо сейчас». Одно статично, другое динамично и может меняться каждую секунду.

Есть и родственный тип — agent. Как и prompt, он возвращает вердикт {"ok": ...}, но проверяющий субагент может по дороге читать файлы и запускать проверки. Помечен как экспериментальный. Он не добавляет текст, а именно проверяет: отдельный Claude-процесс, который выполняет какую-то проверку или действие параллельно с основным. Например, можно настроить agent-hook на событие Stop, который запускает отдельную сессию для проверки корректности финального результата. Это мощный инструмент, но более дорогой — каждый запуск субагента тратит токены. Об этом подробнее — в уроке про субагентов (День 21), а сегодня сосредоточимся на prompt.

Аналогия из бизнеса: представьте секретаря, который каждое ваше письмо перед отправкой дополняет шаблонной преамбулой — реквизитами, датой, ссылкой на предыдущую переписку. Вы пишете только суть, он оборачивает в стандартную форму. Prompt hook — это такой секретарь для ваших запросов к Claude.

Важный нюанс: prompt hooks работают прозрачно для вас. Вы не видите в интерфейсе, что именно добавилось к вашему запросу — вы видите только свой исходный текст. Но Claude получает уже расширенную версию. Если хотите проверить, что добавляется, попросите Claude показать полный текст полученного промта — он честно его воспроизведёт.

Как работает на практике

Hooks настраиваются в файле .claude/settings.json — в разделе hooks. Давайте разберём конкретный пример: hook, который автоматически добавляет текущую дату и день недели к каждому вашему запросу.

Шаг 1. Откройте файл настроек.

Файл .claude/settings.json находится в корне вашего рабочего проекта. Если его нет — создайте. Если есть раздел hooks — добавьте в него, если нет — создайте раздел.

Шаг 2. Опишите hook в конфигурации.

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "printf '{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"additionalContext\":\"Сегодня %s\"}}' \"$(date '+%A, %d %B %Y, %H:%M')\""
          }
        ]
      }
    ]
  }
}

Что здесь происходит:

Шаг 3. Проверьте результат.

После сохранения настроек начните новую сессию Claude Code и задайте любой вопрос. В начале сессии вы можете попросить Claude: «Покажи мне полный текст запроса, который ты получил». Вы увидите, что ваш исходный вопрос дополнен строкой с датой.

У `UserPromptSubmit` матчера нет

Поле matcher на этом событии молча игнорируется — хук срабатывает на каждый запрос. Чтобы реагировать выборочно, читайте поле prompt из JSON на стандартном входе. Это удобно: один hook добавляет контекст для деловых писем, другой — для аналитики, третий — для задач с дедлайнами.

Шаг 4. Более полезный пример — hook для рабочего контекста.

Вместо инлайн-команды echo можно запускать скрипт. Создайте файл ~/.claude/hooks/work_context.sh:

#!/bin/bash
DAY=$(date '+%A')
DATE=$(date '+%d.%m.%Y')
HOUR=$(date '+%H')

# Определяем часть дня
if [ "$HOUR" -lt 12 ]; then
  PART="утро"
elif [ "$HOUR" -lt 18 ]; then
  PART="рабочий день"
else
  PART="вечер"
fi

echo "Контекст: $DATE, $DAY, $PART. Москва, UTC+3."

Сделайте скрипт исполняемым: chmod +x ~/.claude/hooks/work_context.sh

Обновите settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/work_context.sh"
          }
        ]
      }
    ]
  }
}

Теперь каждый ваш запрос автоматически получает временной контекст. Claude будет знать, что сейчас вторник вечером — и не будет предлагать созвониться «сегодня в 3 часа дня». Не будет писать «С добрым утром» в ответе на вечернее письмо. Не будет рекомендовать «успеть до конца дня» в 23:45.

Для директора, работающего в разное время суток — рано утром до детей, поздно вечером после — это особенно ценно. Claude перестаёт быть собеседником, который не знает, который сейчас час.

Шаг 5. Несколько hooks на одно событие.

Вы можете добавить несколько hooks в массив для одного события. Они выполнятся последовательно, и весь их вывод объединится в одну добавку к запросу:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/work_context.sh"
          },
          {
            "type": "command",
            "command": "printf '{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"additionalContext\":\"Отвечай кратко, без вступлений.\"}}'"
          }
        ]
      }
    ]
  }
}

Первый hook добавляет временной контекст, второй — инструкцию по стилю. В итоге Claude получает оба.

Шаг 6. Hook на событие Stop — проверка выполнения.

Есть ещё одно полезное применение: hook на событие Stop. Оно срабатывает, когда Claude заканчивает отвечать. Hook типа prompt на Stop позволяет автоматически добавить в конец задачи проверочный вопрос.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Оцени по данным события, выполнена ли задача полностью: $ARGUMENTS. Ответь JSON: {\"ok\": true} если можно завершать, или {\"ok\": false, \"reason\": \"что осталось\"}."
          }
        ]
      }
    ]
  }
}

Это работает как встроенный контролёр: Claude не просто остановится, а сначала ответит на вопрос «ты точно закончил?».

Hook типа prompt на Stop может создать бесконечный цикл

Если хук на Stop возвращает {"ok": false, "reason": …}, Claude получает reason как следующую инструкцию и продолжает работу — а по её окончании Stop срабатывает снова. Claude Code сам обрывает петлю после восьми блокировок подряд. Чтобы избежать петли, убедитесь, что hook на Stop возвращает текст только при определённых условиях, а не всегда. Матчера у Stop нет: проверяйте в промте поле stop_hook_active из входного JSON и возвращайте {"ok": true}, если цикл уже идёт.

Частые ошибки

Ошибка 1: Hook добавляет слишком много текста.

Если ваш скрипт возвращает несколько абзацев контекста, Claude тратит токены на его обработку и иногда начинает «отвечать» на добавленный контекст, а не на ваш вопрос. Правило: hook типа prompt должен добавлять не больше 1-2 строк. Это маркер, а не эссе.

Как исправить: сокращайте вывод hook до минимума. Дату — в одну строку, флаг стиля — в одно слово. Детальный контекст лучше держать в CLAUDE.md.

Ошибка 2: Скрипт hook падает с ошибкой — Claude ничего не получает.

Если команда завершается с ненулевым кодом выхода, контекст не добавится. Вы думаете, что контекст добавлен, — а его нет. В транскрипте при этом появится строка hook error и первая строка stderr — так что упавший хук не молчит; полный вывод смотрите в отладочном логе (/debug или claude --debug).

Как исправить: тестируйте команду вручную в терминале перед добавлением в settings.json. Запустите ~/.claude/hooks/work_context.sh и убедитесь, что он выводит текст и завершается с кодом 0. Добавьте в скрипт обработку ошибок: если что-то пошло не так — всё равно выводить пустую строку, а не падать.

Ошибка 3: Путаница между prompt и agent.

Оба типа возвращают вердикт {"ok": …}, а не текст. Разница в другом: prompt — одно обращение к модели по данным события, agent — субагент, который может по дороге читать файлы и запускать проверки. А «добавить дату» — это вообще command-хук с additionalContext. Если вы хотите «запустить проверку в параллельном процессе» — agent. Использование agent там, где нужен prompt, приводит к запуску лишних Claude-сессий и трате бюджета.

Как исправить: простое правило — если результат нужен как текст в запросе, выбирайте prompt. Если нужно что-то выполнить как отдельную задачу — agent.

Ошибка 4: Hook настроен глобально, а нужен только для одного проекта.

Если добавить hook в ~/.claude/settings.json (глобальные настройки), он будет работать во всех проектах. Рабочий контекст для проекта по недвижимости не нужен в личных задачах — и наоборот.

Как исправить: для проектно-специфичных hooks используйте .claude/settings.json в папке конкретного проекта. Для универсальных (дата, временная зона) — можно и в глобальных. Это стандартная иерархия настроек Claude Code: глобальные → проектные, проектные имеют приоритет там, где определены.

Когда нужно / когда нет

Используйте prompt hooks, если:

Не используйте prompt hooks, если:

Нулевой порог применения

Самый простой prompt hook — это однострочный echo. Не нужен Python, не нужен bash-профи. Если вы умеете написать echo "Сегодня $(date)" в терминале — вы уже умеете писать prompt hooks.

Связь с другими уроками

День 41 — Что такое hooks и как они работают. Базовая архитектура событий, типы хуков, структура settings.json. Если prompt hooks кажутся магией — вернитесь туда: там объясняется, как система знает, когда вызывать ваш скрипт.

День 11 — CLAUDE.md: инструкции для Claude. Статичный контекст — туда. Динамический — в prompt hook. Понимание разницы между этими двумя механизмами экономит время и токены. CLAUDE.md и hooks дополняют друг друга: один задаёт постоянное поведение, второй добавляет ситуативный контекст.

День 42 — 20+ событий hooks. Если prompt hook работает на уровне «что Claude получил», то PreToolUse/PostToolUse работают на уровне «что Claude делает с инструментами». Когда освоите prompt hooks — следующий уровень именно там: перехватывать вызовы инструментов, добавлять проверки перед чтением файлов, логировать каждое действие.

Задание на сегодня

Откройте .claude/settings.json в вашем рабочем проекте (или создайте его, если нет) и добавьте один command-хук на событие UserPromptSubmit.

Hook должен добавлять к каждому запросу текущую дату в формате «ДД.ММ.ГГГГ» через echo "$(date '+%d.%m.%Y')".

Критерий «выполнено»: запустите новую сессию Claude Code, напишите любой вопрос, затем спросите Claude: «Что именно ты получил как запрос? Покажи полный текст». Если в ответе есть сегодняшняя дата — задание выполнено.

На это уйдёт не больше пяти минут. Если настройки уже есть — три минуты.

Резюме

Следующий урок откроется после отметки.