> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Использование mods API

> Вызывайте mods API из Claude Code mod для добавления команд и инструментов, вызова модели, запуска работы по таймеру, отправки сообщений другим сеансам и доступа к файлам и сети.

mods API — это набор методов, которые mod вызывает для выполнения действий: добавления команд и инструментов, вызова модели, запуска работы между событиями и доступа к файловой системе, процессам и сети. Каждый hook получает её в качестве первого аргумента, `$`, с методами, сгруппированными в пространства имён, такие как `$.ui` и `$.fs`. [События](/docs/ru/plugins/mods/events) определяют, когда выполняется hook, а mods API — это то, что hook вызывает после этого.

Создайте свой [первый mod](/docs/ru/plugins/mods/create) перед тем, как начать здесь. Для каждого метода см. [методы mods API](/docs/ru/plugins/mods/reference#mods-api-methods) или прочитайте [типы для вашей сборки](/docs/ru/plugins/mods/create#get-the-types-for-your-build).

<h2 id="add-a-command-or-a-tool">
  Добавление команды или инструмента
</h2>

Mod может добавить команду для запуска пользователем и инструмент для вызова Claude. Зарегистрируйте оба в hook [`session.start`](/docs/ru/plugins/mods/reference#session). Claude Code ждёт этого hook перед первым запросом, поэтому то, что вы регистрируете, доступно с первого хода.

<h3 id="add-a-command">
  Добавление команды
</h3>

Команда предназначена для пользователя. Зарегистрируйте её, затем обработайте [`command.run`](/docs/ru/plugins/mods/reference#commands-and-configuration) для её имени. Этот пример добавляет команду `/standup`, которая принимает необязательное количество дней:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // Add /standup to the command list, with the description the user sees there
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// The matcher limits the hook to /standup, so other commands don't reach it
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args is the text typed after the command name, or an empty string
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})
```

После запуска сеанса `/standup` появляется с его описанием в списке, который вы видите при вводе `/`. `argumentHint` отображается в подсказке после ввода команды и пробела, как в `/standup [days]`. Когда вы запускаете `/standup 3`, второй hook возвращает `Summary for the last 3 day(s): ...`, и стенограмма показывает этот текст после имени плагина. Hook никогда не вызывает `next`, потому что команда не имеет поведения, кроме вашего.

`text`, который вы возвращаете, печатается в стенограмме и Claude его читает. Чтобы ничего не печатать, как команда, которая только открывает [pane](/docs/ru/plugins/mods/interface#pick-where-to-draw), верните `{}`. Чтобы позволить команде выполняться, пока Claude работает, добавьте `immediate: true` к регистрации.

Выберите имя, которое не использует ни одна встроенная команда. Введите `/` в сеансе, чтобы увидеть их. `$.command.register` выбрасывает исключение для занятого имени с сообщением, таким как `"/focus" refused: it is the built-in /focus"`. Hook, который выбрасывает исключение, пропускается, поэтому остальная часть вашего hook `session.start` тоже не выполняется. Регистрируйте команды в последнюю очередь в этом hook или оберните вызов в `try` и `catch`.

<h3 id="add-a-tool">
  Добавление инструмента
</h3>

Инструмент предназначен для Claude. Зарегистрируйте его с именем, описанием, которое читает Claude, и JSON Schema для его входных данных. Claude видит его под более длинным именем, состоящим из `mcp__`, имени вашего плагина, двух подчёркиваний и имени, которое вы зарегистрировали. Вы обрабатываете его вызовы в hook [`tool.call`](/docs/ru/plugins/mods/events#guard-or-change-a-tool-call), отфильтрованном по этому полному имени. Этот пример из плагина с именем `my-mod` регистрирует `ticket`, поэтому полное имя — `mcp__my-mod__ticket`. Он даёт Claude инструмент, который ищет билет в трекере проблем:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude decides when to call the tool from this description
    description: 'Look up a ticket by its id and return its title and status',
    // The arguments Claude has to send: one required string named id
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// The full tool name is mcp__, the plugin's name, and the registered name
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // The tool's arguments are fields of e, so the id is e.id
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // Return a result either way, so Claude learns when the lookup failed
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})
```

Когда вы спрашиваете о билете, Claude может вызвать `mcp__my-mod__ticket` с его id. Второй hook получает билет и возвращает тело ответа, которое Claude читает как результат инструмента. Когда сервер отвечает с кодом ошибки, Claude читает `Lookup failed with status` и номер.

<h2 id="call-a-model">
  Вызов модели
</h2>

Mod может задать модели вопрос самостоятельно, вне разговора, для небольшой работы, такой как сортировка или суммирование текста. `$.model.complete` отправляет один запрос модели с учётными данными вашего сеанса и разрешается в ответ. У неё нет истории разговора.

Этот hook отвечает на команду `/triage`, [зарегистрированную как команда](#add-a-command), попросив небольшую модель пометить текст, введённый после неё:

```javascript theme={null}
on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    // The system prompt sets the job, and the prompt carries the text to label
    system: 'Reply with one word: bug, feature, or question.',
    prompt: e.args,
    // One word needs few tokens, and the call gives up after 15 seconds
    maxTokens: 20,
    timeoutMs: 15000,
  })
  // r.text exists only when the model answered, so check r.isAnswered first
  const label = r.isAnswered ? r.text.trim() : 'unknown'
  return { text: 'Label: ' + label }
})
```

Когда вы запускаете `/triage the export button does nothing`, mod отправляет этот текст модели и печатает её ответ, например `Label: bug`. Разговор Claude не является частью запроса. Когда модель не отвечает, метка — `unknown`.

Сбой Claude API не отклоняет вызов, поэтому проверьте `r.isAnswered` и прочитайте `r.reason`, когда это `false`. Вызов отклоняется только для запроса, который Claude Code не отправит, например для модели, которую блокирует ваша организация. [Типы для вашей сборки](/docs/ru/plugins/mods/create#get-the-types-for-your-build) перечисляют другие параметры, такие как `effort`, а [ограничения](/docs/ru/plugins/mods/reference#limits) дают значение по умолчанию `maxTokens`.

`$.model.fork({ prompt })` вместо этого задаёт один вопрос по текущему разговору с той же моделью и системным запросом, поэтому Claude API обслуживает большую часть из кэша запросов.

Эти вызовы используют план пользователя или ключ API.

<h2 id="run-work-in-the-background">
  Запуск работы в фоне
</h2>

Работа, которая переживает одно событие, например проверка чего-либо раз в минуту, выполняется на таймере, который вы запускаете из `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 команды в несколько слов:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // Call the function every 60,000 milliseconds, starting one minute from now
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // Replace the line under the prompt with the latest summary
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // Return without waiting for the timer, so the session starts right away
  return next(e)
})
```

Сеанс начинается как обычно. Через минуту под запросом появляется строка с `⚠`, именем mod и затем `checks:` и вашей сводкой. Она заменяется один раз в минуту после этого. Обратный вызов таймера выполняется вне любого события, поэтому он продолжает работать между ходами и не запускает один. Если обратный вызов выбрасывает исключение, ошибка переходит в [журнал отладки](/docs/ru/plugins/mods/troubleshoot#read-the-debug-log) и таймер снова выполняется в следующем интервале.

<h3 id="show-something-without-starting-a-turn">
  Показать что-то без запуска хода
</h3>

Фоновая работа может показать пользователю что-то без запуска хода. Каждый из этих вызовов помещает текст в другое место:

| Вызов | Что видит пользователь |
| :- | :- |
| `$.ui.status(text)` | Одна строка под запросом, которая остаётся, пока вы её не измените. Она начинается с `⚠` и имени mod, как в `⚠ my-mod: checks: 3 passing`. |
| `$.ui.toast(text)` | Небольшой прямоугольник в верхнем правом углу с именем mod над текстом, который исчезает через несколько секунд |
| `$.ui.log(text)` | Тусклая строка в стенограмме, которую Claude не читает. Она начинается с `●` и имени mod, как в `● my-mod: build finished`. |

<h3 id="start-a-turn-from-a-background-job">
  Запуск хода из фоновой работы
</h3>

Когда фоновая работа находит что-то, что требует внимания Claude, она может запустить ход, отправив запрос с `$.prompt.submit({ text })`. Claude читает текст после предложения, которое называет ваш mod как отправителя. Чтобы отправить его как собственные слова пользователя, без этого предложения, добавьте `asUser: true`. Вызов ждёт, пока сеанс будет неактивным, а затем запускает новый ход. Он разрешается при запуске этого хода, поэтому не `await` его в обработчике, который выполняется, пока Claude работает.

<h3 id="stop-background-work">
  Остановка фоновой работы
</h3>

Фоновая работа останавливается двумя способами. Таймеры останавливаются при перезагрузке модуля. Для долгоживущей работы внутри hook, [`next.signal`](/docs/ru/plugins/mods/reference#the-hook-function) — это `AbortSignal`, который прерывается, когда событие, которое обрабатывает ваш hook, отменяется, например когда пользователь прерывает, поэтому передайте его чему-либо долгоживущему.

<h2 id="send-and-receive-messages-between-sessions">
  Отправка и получение сообщений между сеансами
</h2>

Mod может отправить простое текстовое сообщение другому вашему сеансу или одному из подагентов этого сеанса и наблюдать сообщения, которые приходят и уходят. `$.session.send({ to, text })` отправляет один, такую же доставку, которую делает инструмент SendMessage. `to` — это `{ sessionId }` для сеанса, `{ agentId }` для подагента из `$.agent.list()` или адрес строки, из которого пришло полученное сообщение. Вызов разрешается после того, как сообщение поставлено в очередь, с `{ isDelivered: true }`. Когда ничего не было доставлено, он разрешается с `{ isDelivered: false, reason }`, и `reason` говорит почему.

Этот hook отвечает на команду `/ping`, [зарегистрированную как команда](#add-a-command), попросив сеанс, чей id вы вводите после неё, получить статус:

```javascript theme={null}
on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args is the session id typed after /ping
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // The call resolves either way, so check isDelivered to learn what happened
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // An empty result prints nothing in this session's transcript
  return {}
})
```

Когда сообщение поставлено в очередь, в вашем сеансе ничего не появляется, и Claude другого сеанса читает `Status? One line.` Когда ничего не было доставлено, небольшой прямоугольник в верхнем правом углу даёт причину и исчезает через несколько секунд.

Два события позволяют mod наблюдать сообщения. Верните `next(e)` из обоих, чтобы пропустить каждое сообщение без изменений:

| Событие | Срабатывает когда | Полезные поля |
| :- | :- | :- |
| `session.receive` | Сообщение поступает для этого сеанса, перед тем как Claude его прочитает | `e.text` и `e.origin.kind`, такие как `peer` или `peer-send-message` для другого сеанса или агента, `task-notification` или `scheduled-trigger`. Верните `{ consumed: reason }`, чтобы помешать Claude. |
| `session.send` | Сообщение вот-вот уйдёт, из инструмента SendMessage или mod | `e.to`, `e.text` и `e.origin.kind`, который является `model` или `plugin` |

Сеанс, установленный на [отказ входящих сообщений](/docs/ru/cross-session-messaging#control-inbound-messages), отказывает сообщение перед срабатыванием `session.receive`, поэтому hook никогда его не видит. Сообщение, которое удерживается для вашего одобрения, сначала достигает hook, поэтому mod может прочитать сообщение, которое вы ещё не одобрили. `next(e)` hook отклоняет, когда сообщение не доставляется.

Имя отправителя в полученном сообщении — это то, что написал отправитель, поэтому не основывайте решение на нём.

<h2 id="reach-files-processes-and-the-network">
  Доступ к файлам, процессам и сети
</h2>

Mod получает доступ к файловой системе, процессам и сети через mods API с теми же разрешениями, что и пользователь, запускающий Claude Code. Сам модуль hooks не имеет API Node.js, нет глобальных таймеров, таких как `setTimeout`, и нет собственного доступа к сети или файлам. Доступны стандартный JavaScript и веб-API, такие как `URL`, `TextEncoder`, `AbortController` и `crypto.subtle`. Каждое пространство имён ниже охватывает один вид доступа:

| Пространство имён | Что оно делает |
| :- | :- |
| `$.fs` | `read(path)`, `write(path, text)`, `exists(path)`, `stat(path)` и `list(path)` работают с файлами и каталогами |
| `$.process` | `run(['git', 'status'])` запускает команду и разрешается при выходе. `spawn` потоком выводит долгоживущую команду. |
| `$.http` | `fetch(url, init)` по `http` или `https`. Он разрешается в `{ status, ok, headers, text }` после прочтения тела. |
| `$.store` | Хранилище JSON ключ-значение вашего собственного плагина, сохраняемое между сеансами |
| `$.env` | `get` и `set` переменные окружения. Напишите имя как буквальную строку. |
| `$.settings` | `read` то, что содержат файлы параметров и управляемая политика |
| `$.session` | `messages()` возвращает стенограмму как список `{ role, text, toolUses }`. Также рабочий каталог, модель и многое другое. [`usage()`](/docs/ru/plugins/mods/reference#mods-api-methods) возвращает использование контекстного окна и ограничения плана. |
| `$.mcp` | `call` инструмент на подключённом MCP сервере |

Файлы и процессы имеют несколько собственных правил:

* **Пути**: относительный путь находится в рабочем каталоге сеанса
* **`$.fs.list`**: возвращает записи одного каталога как `{ name, kind, size, isLink }` и не спускается в подкаталоги
* **`$.process.run`**: принимает список аргументов и не использует оболочку. Он разрешается в `{ exitCode, stdout, stderr }` независимо от кода выхода. Он отклоняется, если программа не может запуститься или всё ещё выполняется при истечении времени ожидания, которое по умолчанию составляет 30 секунд, поэтому оберните его в `try` и `catch`.

Каждый из этих вызовов сам по себе является событием, названным по его пространству имён и методу без `$.`, например `fs.read` для `$.fs.read`. Mod [ранее в цепи](/docs/ru/plugins/mods/events#the-order-mods-run-in) может наблюдать, переписывать или отказывать ваш вызов, что является тем, как организация ограничивает то, что mods достигают.

<h2 id="next-steps">
  Следующие шаги
</h2>

* [Реагирование на события](/docs/ru/plugins/mods/events): hook вызовы инструментов, запросы и ходы
* [Рисование в интерфейсе](/docs/ru/plugins/mods/interface): показать то, что собирает ваш mod в pane или над запросом
* [Тестирование mod](/docs/ru/plugins/mods/test): заглушка любого из этих вызовов в тесте
* [Справочник Mods](/docs/ru/plugins/mods/reference): каждое событие, каждый метод mods API и ограничения
