$, с методами, сгруппированными в пространства имён, такие как $.ui и $.fs. События определяют, когда выполняется hook, а mods API — это то, что hook вызывает после этого.
Создайте свой первый mod перед тем, как начать здесь. Для каждого метода см. методы mods API или прочитайте типы для вашей сборки.
Добавление команды или инструмента
Mod может добавить команду для запуска пользователем и инструмент для вызова Claude. Зарегистрируйте оба в hooksession.start. Claude Code ждёт этого hook перед первым запросом, поэтому то, что вы регистрируете, доступно с первого хода.
Добавление команды
Команда предназначена для пользователя. Зарегистрируйте её, затем обработайтеcommand.run для её имени. Этот пример добавляет команду /standup, которая принимает необязательное количество дней:
/standup появляется с его описанием в списке, который вы видите при вводе /. argumentHint отображается в подсказке после ввода команды и пробела, как в /standup [days]. Когда вы запускаете /standup 3, второй hook возвращает Summary for the last 3 day(s): ..., и стенограмма показывает этот текст после имени плагина. Hook никогда не вызывает next, потому что команда не имеет поведения, кроме вашего.
text, который вы возвращаете, печатается в стенограмме и Claude его читает. Чтобы ничего не печатать, как команда, которая только открывает pane, верните {}. Чтобы позволить команде выполняться, пока Claude работает, добавьте immediate: true к регистрации.
Выберите имя, которое не использует ни одна встроенная команда. Введите / в сеансе, чтобы увидеть их. $.command.register выбрасывает исключение для занятого имени с сообщением, таким как "/focus" refused: it is the built-in /focus". Hook, который выбрасывает исключение, пропускается, поэтому остальная часть вашего hook session.start тоже не выполняется. Регистрируйте команды в последнюю очередь в этом hook или оберните вызов в try и catch.
Добавление инструмента
Инструмент предназначен для Claude. Зарегистрируйте его с именем, описанием, которое читает Claude, и JSON Schema для его входных данных. Claude видит его под более длинным именем, состоящим изmcp__, имени вашего плагина, двух подчёркиваний и имени, которое вы зарегистрировали. Вы обрабатываете его вызовы в hook tool.call, отфильтрованном по этому полному имени. Этот пример из плагина с именем my-mod регистрирует ticket, поэтому полное имя — mcp__my-mod__ticket. Он даёт Claude инструмент, который ищет билет в трекере проблем:
mcp__my-mod__ticket с его id. Второй hook получает билет и возвращает тело ответа, которое Claude читает как результат инструмента. Когда сервер отвечает с кодом ошибки, Claude читает Lookup failed with status и номер.
Вызов модели
Mod может задать модели вопрос самостоятельно, вне разговора, для небольшой работы, такой как сортировка или суммирование текста.$.model.complete отправляет один запрос модели с учётными данными вашего сеанса и разрешается в ответ. У неё нет истории разговора.
Этот hook отвечает на команду /triage, зарегистрированную как команда, попросив небольшую модель пометить текст, введённый после неё:
/triage the export button does nothing, mod отправляет этот текст модели и печатает её ответ, например Label: bug. Разговор Claude не является частью запроса. Когда модель не отвечает, метка — unknown.
Сбой Claude API не отклоняет вызов, поэтому проверьте r.isAnswered и прочитайте r.reason, когда это false. Вызов отклоняется только для запроса, который Claude Code не отправит, например для модели, которую блокирует ваша организация. Типы для вашей сборки перечисляют другие параметры, такие как effort, а ограничения дают значение по умолчанию maxTokens.
$.model.fork({ prompt }) вместо этого задаёт один вопрос по текущему разговору с той же моделью и системным запросом, поэтому Claude API обслуживает большую часть из кэша запросов.
Эти вызовы используют план пользователя или ключ API.
Запуск работы в фоне
Работа, которая переживает одно событие, например проверка чего-либо раз в минуту, выполняется на таймере, который вы запускаете изsession.start. Сам hook выполняется для одного события и имеет ограничение по времени в 10 секунд собственного времени выполнения. Время, потраченное на ожидание next или вызова mods API, не учитывается, кроме $.clock.sleep. $.clock.every и $.clock.after заменяют setInterval и setTimeout, с задержкой в миллисекундах в первую очередь: $.clock.after(5000, fn) вызывает fn один раз через пять секунд. Каждый возвращает таймер с методом cancel(), и await $.clock.now() даёт время в миллисекундах.
Этот hook ищет проверки pull request один раз в минуту и показывает результат под запросом. summarize — это функция вашего собственного, которая превращает вывод JSON команды в несколько слов:
⚠, именем mod и затем checks: и вашей сводкой. Она заменяется один раз в минуту после этого. Обратный вызов таймера выполняется вне любого события, поэтому он продолжает работать между ходами и не запускает один. Если обратный вызов выбрасывает исключение, ошибка переходит в журнал отладки и таймер снова выполняется в следующем интервале.
Показать что-то без запуска хода
Фоновая работа может показать пользователю что-то без запуска хода. Каждый из этих вызовов помещает текст в другое место:Запуск хода из фоновой работы
Когда фоновая работа находит что-то, что требует внимания Claude, она может запустить ход, отправив запрос с$.prompt.submit({ text }). Claude читает текст после предложения, которое называет ваш mod как отправителя. Чтобы отправить его как собственные слова пользователя, без этого предложения, добавьте asUser: true. Вызов ждёт, пока сеанс будет неактивным, а затем запускает новый ход. Он разрешается при запуске этого хода, поэтому не await его в обработчике, который выполняется, пока Claude работает.
Остановка фоновой работы
Фоновая работа останавливается двумя способами. Таймеры останавливаются при перезагрузке модуля. Для долгоживущей работы внутри hook,next.signal — это AbortSignal, который прерывается, когда событие, которое обрабатывает ваш hook, отменяется, например когда пользователь прерывает, поэтому передайте его чему-либо долгоживущему.
Отправка и получение сообщений между сеансами
Mod может отправить простое текстовое сообщение другому вашему сеансу или одному из подагентов этого сеанса и наблюдать сообщения, которые приходят и уходят.$.session.send({ to, text }) отправляет один, такую же доставку, которую делает инструмент SendMessage. to — это { sessionId } для сеанса, { agentId } для подагента из $.agent.list() или адрес строки, из которого пришло полученное сообщение. Вызов разрешается после того, как сообщение поставлено в очередь, с { isDelivered: true }. Когда ничего не было доставлено, он разрешается с { isDelivered: false, reason }, и reason говорит почему.
Этот hook отвечает на команду /ping, зарегистрированную как команда, попросив сеанс, чей id вы вводите после неё, получить статус:
Status? One line. Когда ничего не было доставлено, небольшой прямоугольник в верхнем правом углу даёт причину и исчезает через несколько секунд.
Два события позволяют mod наблюдать сообщения. Верните next(e) из обоих, чтобы пропустить каждое сообщение без изменений:
Сеанс, установленный на отказ входящих сообщений, отказывает сообщение перед срабатыванием
session.receive, поэтому hook никогда его не видит. Сообщение, которое удерживается для вашего одобрения, сначала достигает hook, поэтому mod может прочитать сообщение, которое вы ещё не одобрили. next(e) hook отклоняет, когда сообщение не доставляется.
Имя отправителя в полученном сообщении — это то, что написал отправитель, поэтому не основывайте решение на нём.
Доступ к файлам, процессам и сети
Mod получает доступ к файловой системе, процессам и сети через mods API с теми же разрешениями, что и пользователь, запускающий Claude Code. Сам модуль hooks не имеет API Node.js, нет глобальных таймеров, таких какsetTimeout, и нет собственного доступа к сети или файлам. Доступны стандартный JavaScript и веб-API, такие как URL, TextEncoder, AbortController и crypto.subtle. Каждое пространство имён ниже охватывает один вид доступа:
Файлы и процессы имеют несколько собственных правил:
- Пути: относительный путь находится в рабочем каталоге сеанса
$.fs.list: возвращает записи одного каталога как{ name, kind, size, isLink }и не спускается в подкаталоги$.process.run: принимает список аргументов и не использует оболочку. Он разрешается в{ exitCode, stdout, stderr }независимо от кода выхода. Он отклоняется, если программа не может запуститься или всё ещё выполняется при истечении времени ожидания, которое по умолчанию составляет 30 секунд, поэтому оберните его вtryиcatch.
$., например fs.read для $.fs.read. Mod ранее в цепи может наблюдать, переписывать или отказывать ваш вызов, что является тем, как организация ограничивает то, что mods достигают.
Следующие шаги
- Реагирование на события: hook вызовы инструментов, запросы и ходы
- Рисование в интерфейсе: показать то, что собирает ваш mod в pane или над запросом
- Тестирование mod: заглушка любого из этих вызовов в тесте
- Справочник Mods: каждое событие, каждый метод mods API и ограничения