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

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

Практика: автонотификации через hooks

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

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

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

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

Вы отправили Claude разбирать квартальный отчёт — десятки файлов, сравнения, сводные таблицы. Задача уйдёт минут на восемь. Вы переключились на другое. Через десять минут думаете: «закончил или нет?» — переключаетесь обратно, смотрите на терминал, видите курсор. Ещё не готово. Возвращаетесь к своему делу. Через пять минут снова проверяете. Это называется «опрос вручную» — и это потеря времени и внимания.

Профессиональные системы устроены иначе. Когда долгая операция завершается — сервер сам шлёт вам уведомление. Деплой прошёл — пришло в Slack. Экспорт данных готов — письмо на почту. Вы не проверяете статус, вы получаете сигнал. Это принцип «push вместо pull», и он работает так же хорошо применительно к Claude.

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


Что это такое

Stop hook срабатывает каждый раз, когда Claude закончил отвечать, — то есть после каждого хода, а не один раз за сессию. Если нужно ровно одно уведомление на всю сессию, вешайте хук на событие SessionEnd: дальше в уроке мы так и сделаем.

Представьте, что у вас есть помощник, которому вы поручаете работу и уходите из кабинета. Договорённость простая: когда закончит — позвонит. Stop hook — это тот самый звонок. Вы не сидите рядом, не следите, не прерываетесь. Помощник работает сам. Когда готово — уведомляет.

Тип http означает, что уведомление отправляется как HTTP-запрос: Claude Code вызывает ваш скрипт, скрипт делает POST на Telegram Bot API. Это не требует никакого сервера с вашей стороны — только пара строк shell-кода и Telegram-бот, который вы создадите за две минуты.

Почему именно Telegram

Telegram Bot API — самый простой способ получать уведомления без регистрации сервисов. Один запрос curl отправляет сообщение куда угодно: в личку, в группу, в канал. Никаких ключей OAuth, никаких веб-консолей. Создали бота через @BotFather, получили токен — готово.

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

Важно понимать, что Stop hook умеет и вмешиваться: код выхода 2 или {"decision":"block","reason":"…"} не даст Claude остановиться и вернёт его к работе — на этом строятся сценарии «продолжай, пока не готово». Для простого уведомления просто выходите с кодом 0. Его единственная задача — среагировать на факт завершения. Это делает его безопасным: что бы ни произошло в скрипте — отправка уведомления успешна или нет, сеть недоступна, Telegram API лежит — на работу Claude это никак не влияет. Скрипт отработал в фоне и забыл.

Данные, которые Claude Code передаёт скрипту, приходят через стандартный ввод (stdin) в формате JSON. Структура включает идентификатор сессии, рабочую директорию, текст последнего ответа Claude (last_assistant_message), флаг stop_hook_active и списки фоновых задач. Списка изменённых файлов и длительности сессии там нет — то, что Claude сформулировал как ответ на вашу задачу. Именно из этого JSON скрипт извлекает то, что нужно для уведомления.


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

Разберём по шагам. Сценарий: вы попросили Claude проанализировать папку с договорами и подготовить сводку с ключевыми условиями по каждому. Задача долгая — десятки файлов, много текста. Вы хотите получить уведомление в Telegram, когда она закончится, и не переключаться туда-обратно.

Шаг 1. Создайте Telegram-бота

Откройте Telegram, найдите @BotFather. Отправьте /newbot. Придумайте имя и username (username должен заканчиваться на bot). В ответ получите токен вида 7123456789:AAF.... Сохраните его — он понадобится через минуту.

Затем напишите своему боту любое сообщение (просто откройте бота и нажмите Start или напишите «привет»). Это нужно, чтобы бот мог вам писать.

Теперь нужен ваш chat_id. Откройте в браузере:

https://api.telegram.org/bot<ВАШ_ТОКЕН>/getUpdates

В ответе найдите "chat":{"id": — это и есть ваш chat_id. Обычно это число вроде 252679756. Запишите его.

Шаг 2. Напишите скрипт уведомления

Создайте файл ~/.claude/notify-done.sh:

#!/bin/bash

# Токены — задаём один раз здесь
TG_TOKEN="7123456789:AAF..."
TG_CHAT_ID="252679756"

# Claude Code передаёт данные сессии через stdin как JSON
SESSION_JSON=$(cat)

# Извлекаем нужные поля
# Что реально приходит в событии Stop: last_assistant_message, cwd,
# session_id, background_tasks. Полей вроде duration_seconds там нет.
PROJECT=$(echo "$SESSION_JSON" | python3 -c "
import json, sys, os
print(os.path.basename(json.load(sys.stdin).get('cwd', '')) or 'проект')
" 2>/dev/null || echo "проект")

LAST_LINE=$(echo "$SESSION_JSON" | python3 -c "
import json, sys
msg = json.load(sys.stdin).get('last_assistant_message', '') or ''
lines = [l.strip() for l in msg.strip().split('\n') if l.strip()]
print(lines[-1][:120] if lines else 'задача завершена')
" 2>/dev/null || echo "задача завершена")

# Формируем сообщение
MESSAGE="Claude завершил ход

Проект: ${PROJECT}
Итог: ${LAST_LINE}"

# Отправляем в Telegram
curl -s -X POST \
  "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
  -d "chat_id=${TG_CHAT_ID}" \
  -d "text=${MESSAGE}" \
  > /dev/null 2>&1

exit 0

Сделайте файл исполняемым:

chmod +x ~/.claude/notify-done.sh

Шаг 3. Подключите hook в настройках Claude Code

Откройте ~/.claude/settings.json. Если файл не существует — создайте. Добавьте секцию hooks:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/notify-done.sh"
          }
        ]
      }
    ]
  }
}

Про тип command vs http

В документации Claude Code Stop hook может быть типа command (запускает shell-скрипт) или http (делает POST напрямую на URL). В данном случае мы используем command — это гибче: скрипт сам формирует сообщение из данных сессии и отправляет его. Тип http удобен, если у вас есть собственный сервер, принимающий вебхуки.

Шаг 4. Проверьте

Запустите простую задачу в Claude Code:

claude "напиши привет в файл test.txt"

Когда сессия завершится — через несколько секунд в Telegram должно прийти уведомление. Если не пришло — проверьте токен и chat_id, запустите скрипт вручную:

echo '{"hook_event_name":"Stop","cwd":"/Users/вы/Проекты/отчёты","last_assistant_message":"Готово: сводка собрана в отчёт.md"}' | ~/.claude/notify-done.sh

Если сообщение пришло при ручном запуске, но не через Claude — проверьте путь в settings.json и перезапустите Claude Code.

Шаг 5. Настройте фильтр по времени (опционально)

Уведомление после каждого хода быстро надоедает: Stop срабатывает часто. Есть два способа проредить поток.

Первый — перевесить хук на SessionEnd. Тогда сообщение придёт один раз, когда сессия закрывается:

{
  "hooks": {
    "SessionEnd": [{
      "hooks": [{ "type": "command", "command": "~/.claude/notify-done.sh", "timeout": 15 }]
    }]
  }
}

Обратите внимание на timeout: у SessionEnd бюджет по умолчанию всего полторы секунды, отправка в Telegram в него может не уложиться.

Второй — поставить в самом скрипте паузу между уведомлениями, чтобы не писать чаще раза в N минут:

# Не чаще одного уведомления в 10 минут
STAMP=~/.claude/.last-notify
NOW=$(date +%s)
if [ -f "$STAMP" ] && [ $(( NOW - $(cat "$STAMP") )) -lt 600 ]; then
  exit 0
fi
echo "$NOW" > "$STAMP"

Шаг 6. Добавьте имя рабочей директории (опционально)

Если вы запускаете Claude из разных папок — полезно знать, в каком проекте он работал. Добавьте в скрипт ещё одну строку извлечения:

WORK_DIR=$(echo "$SESSION_JSON" | python3 -c "
import json, sys
data = json.load(sys.stdin)
cwd = data.get('cwd', '')
# Показываем только последнюю часть пути
print(cwd.split('/')[-1] if cwd else 'неизвестно')
" 2>/dev/null || echo "неизвестно")

И обновите сообщение:

MESSAGE="Claude завершил ход

Проект: ${WORK_DIR}
Итог: ${LAST_LINE}"

Теперь если вы работаете с несколькими проектами — Договоры, Отчёты, Маркетинг — уведомление сразу говорит, откуда оно пришло, без необходимости вспоминать, что вы запускали.


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

1. Скрипт не исполняемый

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

Симптом: hook подключён, сессия завершается, уведомлений нет, ручной запуск скрипта через bash работает.

Решение: chmod +x ~/.claude/notify-done.sh. Всегда делайте это сразу после создания скрипта.

2. Токен или chat_id с опечаткой

Telegram API при неверном токене возвращает ошибку, но поскольку в скрипте мы перенаправляем вывод curl в /dev/null, ошибка не видна. Кажется, что скрипт работает — уведомлений просто нет.

Решение: при отладке временно уберите > /dev/null 2>&1 из строки curl. Запустите скрипт вручную и посмотрите на вывод. Если Telegram вернул {"ok":false} — проверьте токен и chat_id.

3. Тильда в пути не раскрывается в JSON

В settings.json путь ~/.claude/notify-done.sh может не раскрываться до домашней директории в зависимости от среды. Это зависит от конфигурации системы.

Решение: используйте полный путь /Users/ваше_имя/.claude/notify-done.sh. Узнайте его командой echo ~/.claude/notify-done.sh.

Не кладите токены в файлы на Яндекс.Диске

Если ваш .claude/ синхронизируется в облако — не храните токены прямо в скрипте. Используйте переменные окружения из ~/.zshenv: добавьте туда export TG_TOKEN="..." и export TG_CHAT_ID="...", а в скрипте обращайтесь как $TG_TOKEN и $TG_CHAT_ID. Файл ~/.zshenv остаётся только на вашем компьютере.

4. Stop hook срабатывает на короткие сессии и засоряет телефон

Если порог не настроен, уведомление приходит после каждой команды — даже если вы просто спросили «какой сейчас год». Через день это начинает раздражать.

Решение: перевесьте хук на SessionEnd или поставьте паузу между уведомлениями (Шаг 5 выше). Десять минут — разумный интервал.


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

Нужно, если:

Не нужно, если:

Уведомление — это не замена контролю

Stop hook сообщает «закончил», но не «закончил правильно». Если Claude завершил сессию с ошибкой или неполным результатом — уведомление всё равно придёт. Добавьте в скрипт поле из данных сессии, которое сигнализирует об успехе или ошибке, если хотите различать исходы.


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

День 41 — Hooks: события и архитектура. Там объяснена общая логика hooks: почему они работают, какие бывают типы, как Claude Code передаёт данные в скрипт через stdin. Если Stop hook ведёт себя неожиданно — вернитесь к базовой архитектуре.

День 43 — Matchers: точечная настройка триггеров. Если вы хотите получать уведомление не на все сессии, а только на те, где Claude работал с определённой папкой или определённым инструментом — matchers позволяют добавить это условие. Уведомление только когда изменялись файлы в ~/Documents/Договоры/ — это именно про матчеры.

День 9 — Флаг --bg: фоновые задачи. Фоновый режим и Stop hook — естественная пара. Вы запускаете Claude в фоне, уходите заниматься своим делом, а уведомление сообщает о завершении. Без Stop hook фоновые задачи требуют периодической проверки статуса вручную.


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

Создайте скрипт ~/.claude/notify-done.sh и подключите его как Stop hook в ~/.claude/settings.json. Запустите любую задачу в Claude Code — например, claude "опиши структуру текущей директории" — и убедитесь, что в Telegram пришло уведомление.

Критерий «выполнено»: сообщение от вашего Telegram-бота появилось в телефоне после завершения сессии Claude.

Если Telegram-бота нет — создайте через @BotFather, это займёт две минуты. Токен и chat_id заносите в ~/.zshenv, не в файл на Яндекс.Диске.

Если уведомление сработало — попробуйте запустить реально долгую задачу: попросите Claude разобрать несколько документов или проанализировать папку с файлами. Уйдите на другое дело. Посмотрите, как ощущается получить уведомление вместо того, чтобы периодически поглядывать на экран. Скорее всего, захочется оставить это навсегда.


Резюме

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