# Моды в Claude Code: установка, проверка доступа и первый мод

> Моды Claude Code 2.1.287+ работают с вашими правами и без песочницы. Перед установкой прогоните validate, а если мод запускает программы, читайте его код.

- URL: https://blog.laozhang.ai/ru/posts/claude-code-mods
- Published: 2026-10-06
- Updated: 2026-10-06
- Author: LaoZhang AI Team (https://blog.laozhang.ai/ru/about)
- Topic: Claude Code
- Tags: Claude Code, Моды, Плагины, TypeScript, Anthropic

---
Мод в Claude Code — это плагин с кодом на JavaScript или TypeScript, который Claude Code выполняет прямо в своём процессе, когда что-то происходит: Claude собирается вызвать инструмент, вы отправляете запрос, интерфейс перерисовывает спиннер. Мод может переписать запрос до того, как он уйдёт в модель, придержать или подменить вызов инструмента, одобрить его без вашего подтверждения, нарисовать панель рядом с транскриптом и добавить команду, которая отвечает сразу, без хода Claude. Anthropic выпустила моды 1 октября 2026 года ([анонс «Customize Claude Code with mods in TypeScript» в блоге Claude](https://claude.com/blog/claude-code-mods)). Нужен Claude Code 2.1.287 или новее в терминале либо 2.1.286 и новее внутри десктопного приложения; по умолчанию моды включены.

Главное, что стоит знать до установки чужого мода: он не изолирован и работает с теми же правами, что и вы. `claude plugin validate` без запуска кода покажет, какие события мод перехватывает и какие методы вызывает, но не скажет, какую именно программу он запустит. Для этого придётся открыть исходник.

**Что запускалось.** Все выводы команд в тексте получены на Claude Code 2.1.288 в macOS, CLI без входа в аккаунт: `claude plugin validate` для учебного мода и трёх официальных примеров Anthropic, `claude plugin test`, `claude -p "/tally"`, добавление маркетплейса, установка, отключение и удаление примера token-weather.

**Что не запускалось.** Интерактивной сессии с моделью не было, поэтому отрисовка панелей и полос над полем ввода, горячая перезагрузка, строка `mods active` в `/plugin` и одобрение мода, который пишет Claude, описаны по официальной документации. Десктопное приложение, расширение VS Code, Windows, защита на тарифах Team и Enterprise и сторонние моды тоже не проверялись.

## Мод или хук настроек: когда мод вообще нужен

Мод нужен, если задача требует рисовать в интерфейсе Claude Code или вмешиваться в событие изнутри: придержать вызов, ответить вместо инструмента, добавить мгновенную команду. Если хватает «заблокировать, разрешить или записать в лог» готовым скриптом, по-прежнему подходит хук настроек (settings hook) из `settings.json`. Хуки настроек не устарели: в документации для администраторов прямо сказано, что ничего в них не объявлено устаревшим, и они работают рядом с модами.

Отсюда и путаница с формулировкой «хуки на TypeScript». Обработчики событий внутри мода документация тоже называет hooks, но это другой механизм: хук настроек — внешняя команда, HTTP-запрос или промпт, а хук мода — функция, которую Claude Code вызывает в собственном процессе. Чтобы не путать, официальная документация называет хуки из файла настроек «хуками настроек».

| | Мод | Хук настроек | Скилл (skill) | MCP-сервер |
| --- | --- | --- | --- | --- |
| Что это | Функции в плагине, выполняются в процессе Claude Code | Shell-команда, HTTP-запрос или промпт на событие жизненного цикла | Инструкции в `SKILL.md` | Внешний процесс, который даёт Claude инструменты |
| Что меняет | Вызовы инструментов, запросы, команды, ходы, то, что рисует интерфейс | Может пропустить или остановить вызов и запрос, поменять аргументы, результат и контекст | Что Claude знает и как действует | Добавляет инструменты для внешних систем |
| Рисует в интерфейсе | Да | Нет | Нет | Нет |
| На чём пишут | JavaScript или TypeScript | Скрипт и `settings.json` | Markdown | Любой язык |
| Когда выбирать | Нужна панель, полоса над полем ввода, своя команда или переписать событие | Есть скрипт, который должен блокировать, разрешать или логировать | Вы раз за разом вставляете одни и те же инструкции | Claude нужен доступ к внешней системе |

![Выбор по задаче: рисовать в интерфейсе или вмешаться в событие — мод, блокировать или логировать скриптом — хук настроек, повторяющиеся инструкции — скилл, доступ к внешней системе — MCP-сервер](https://blog.laozhang.ai/posts/ru/claude-code-mods/img/mod-or-settings-hook.webp)

Один плагин может содержать всё это сразу. Чем различаются хуки настроек, скиллы и слэш-команды, подробно разобрано в статье [«Claude Code: чем отличаются hooks, skills и слэш-команды»](https://blog.laozhang.ai/ru/posts/claude-code-hooks-slash-commands-skills). Если нужны готовые расширения без своего кода, начните с подборок [«Лучшие skills для Claude Code, которые стоит ставить первыми в 2026 году»](https://blog.laozhang.ai/ru/posts/claude-code-best-skills) и [«Лучшие MCP для Claude Code, которые стоит подключать первыми в 2026 году»](https://blog.laozhang.ai/ru/posts/claude-code-best-mcp-servers).

Что умеет только мод:

- **Рисовать рабочий интерфейс**: панель рядом с транскриптом или полоса над полем ввода с вкладками, кнопками и текстовыми полями.
- **Перекрашивать интерфейс самого Claude Code**: строки вызовов инструментов, спиннер, диалог, в котором Claude задаёт вопрос. Скрипт строки состояния выводит только одну строку внизу (как его настроить, описано в статье [«Claude Code Statusline: маршруты, поля, скрипты и диагностика»](https://blog.laozhang.ai/ru/posts/claude-code-statusline)).
- **Вмешиваться в вызов**: придержать его, ответить без запуска инструмента, отправить запрос в другую модель.
- **Добавлять `/команду`, которая сразу выполняет вашу функцию** без хода Claude, даже пока Claude работает.
- **Делить переменные между обработчиками** в одном файле: один считает вызовы, другой показывает счётчик.

К модам для игр всё это отношения не имеет: речь о расширениях самого Claude Code, а не о плагинах, которые помогают Claude писать моды, скажем, для Minecraft.

## Какая версия Claude Code нужна для модов и где они рисуют

Минимум — 2.1.287 для терминального Claude Code. Десктопное приложение несёт свою копию Claude Code, и там моды работают с 2.1.286. Проверка версии:

- **Терминал**: `claude --version`. На тестовой машине команда вернула `2.1.288 (Claude Code)`. Если версия старше, обновите Claude Code, как описано в [«Как установить Claude Code: полное руководство для всех платформ (2026)»](https://blog.laozhang.ai/ru/posts/claude-code-install).
- **Десктоп**: в локальной сессии на вкладке Code введите `/status` и посмотрите строку **Claude Code**.

Переменная `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` из раннего доступа с 2.1.287 игнорируется, и значение `0` моды не выключает.

Загрузятся ли моды именно у вас, показывает `claude plugin test`, запущенный в пустой папке:

```text
$ claude plugin test
claude plugin test: .../empty: no hooks module to load; there is no hooks/hooks.json naming one in "modules"
```

Код возврата 0. По таблице из [документации «Устранение неполадок с модами»](https://code.claude.com/docs/ru/plugins/mods/troubleshoot) сообщения читаются так:

| Сообщение | Что значит |
| --- | --- |
| `no hooks module to load` | Моды здесь загружаются, просто в папке нет мода |
| `hooks modules are turned off here` | Моды выключены через `disableAllHooks` или политикой организации |
| `hooks modules are turned off in this process` | Anthropic удалённо отключила установленные моды |

Ограничение организации `allowManagedModsOnly` (только моды, которые раздаёт администратор) эта проверка не показывает. На тестовой машине встретилось только первое сообщение.

Хуки мода работают везде, где загружается плагин, а вот рисование доступно не везде. Таблица по [официальному обзору модов](https://code.claude.com/docs/ru/plugins/mods/overview):

| Где вы запускаете Claude Code | Хуки работают | Мод рисует |
| --- | --- | --- |
| `claude` в терминале, включая встроенный терминал редактора и плагин JetBrains | Да | Да |
| Вкладка Code в десктопном приложении (не WSL) | Да | Да, кроме элементов только для терминала |
| Сессия WSL в десктопном приложении | Нет: плагины в WSL-сессиях недоступны | Нет |
| Панель чата расширения VS Code | Да | Нет |
| `claude -p` и Agent SDK | Да | Нет |
| Remote Control из claude.ai или мобильного приложения | Да, в сессии на вашей машине | В том терминале, где идёт сессия |
| Облачная сессия | Если плагин попал в облачную сессию | Нет |

Если вы работаете в чате VS Code, мод с панелью ничего не покажет, хотя его обработчики будут выполняться.

## К чему у установленного мода есть доступ

У загруженного мода те же возможности, что у вас в системе. По [официальному обзору модов](https://code.claude.com/docs/ru/plugins/mods/overview) мод может:

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

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

## Правила ask и deny: что мод может одобрить в обход вас

Мод с обработчиком `tool.check` решает за вас, пропустить вызов или нет, и его ответ сильнее части ваших правил. Порядок описан в [документации «Управление модами для вашей организации»](https://code.claude.com/docs/ru/plugins/mods/admin):

- **Правило `ask`** мод может обойти с защитой и без неё: он одобряет вызов, на который Claude Code иначе показал бы запрос. То же с блокировкой от вашего `PreToolUse`-хука, если он не из управляемых настроек (managed settings).
- **В режиме auto** вызов, одобренный модом, выполняется без проверки классификатором. Как этот режим устроен, разобрано в статье [«Claude Code Auto Mode: как это работает, что блокирует и когда использовать (2026)»](https://blog.laozhang.ai/ru/posts/claude-code-auto-mode).
- **Правило `deny`** держится только там, где загружена встроенная защита `sec-default` (в `/plugin` она видна как `cc-plugin-sec-default`). Она загружается, если на машине есть управляемые настройки или если вы вошли в Claude Code с тарифом Team или Enterprise. При работе через ключ API, Amazon Bedrock, Google Cloud Agent Platform или Microsoft Foundry защита появляется только при управляемых настройках.
- **Даже с защитой** запреты вида `Read(.env)` не касаются собственных вызовов мода `$.fs` и `$.process`: мод прочитает `.env` через `$.fs.read` или запустит программу, которая это сделает.
- **Сетевая политика** организации распространяется на `$.http.fetch`, но не на программу, запущенную через `$.process.run`: та выходит в сеть с вашими правами.

Из двух условий загрузки защиты следует практический вывод: при личном входе с Pro или Max на машине без управляемых настроек защита `sec-default` не загружается, и правило `deny` не мешает моду одобрить вызов. Документация не разбирает этот случай отдельно — это прямое прочтение её условий. Поэтому к установке мода стоит относиться как к запуску чужой программы с вашими правами.

![Что мод может обойти без защиты sec-default и с ней: правило ask обходится всегда, deny держится только с защитой, запрет на чтение .env не касается $.fs и $.process, сетевая политика не касается $.process.run](https://blog.laozhang.ai/posts/ru/claude-code-mods/img/ask-deny-rules.webp)

## Как проверить мод до установки: validate, затем исходник

Проверка не требует установки и не запускает код мода.

1. Скачайте файлы плагина, например клонируйте репозиторий автора.
2. Запустите `claude plugin validate ./some-mod` на папке мода. Команда выполняет тот же статический анализ, что Claude Code делает при загрузке. Мод, который обращается к API так, что анализ не может это прочитать, Claude Code загружать откажется, так что спрятать вызов от этой проверки не получится.
3. Прочитайте две строки: `hooks:` — какие события мод получает (фильтр в фигурных скобках), `calls:` — какие методы API модов он вызывает.
4. Если в этих строках есть что-то из таблиц ниже, откройте исходник и найдите, что именно делает этот вызов.

| В строке `calls:` | Что это значит |
| --- | --- |
| `$.fs.read`, `$.fs.write` | Читает или пишет файлы везде, где можете вы |
| `$.process.run`, `$.process.spawn` | Запускает программы от вашего имени |
| `$.http.fetch` | Делает сетевые запросы |
| `$.env.get`, `$.settings.read` | Читает переменные окружения и настройки, где могут лежать ключи API; строка `env reads:` называет переменные |
| `$.env.set` | Задаёт переменную окружения для Claude Code и всех команд и MCP-серверов, запущенных после; строка `env writes:` называет переменные |
| `$.mcp.call` | Вызывает инструмент подключённого MCP-сервера по правилам разрешений сессии |
| `$.model.complete` | Тратит ваш план или ключ API на вызовы модели |
| `$.prompt.submit` | Отправляет запрос, в том числе от вашего имени |
| `$.session.send` | Пишет сообщение, которое прочитает Claude другой сессии или субагента |

| В строке `hooks:` | Что это значит |
| --- | --- |
| `tool.call`, `prompt.submit` | Видит и может изменить каждый вызов инструмента или каждый запрос |
| `tool.check` | Одобряет или отклоняет вызов до запроса разрешения |
| `session.append` | Может переписать каждую строку разговора до сохранения |
| `ui.render{component=AskUserQuestion}` | Может перерисовать диалог, в котором Claude задаёт вам вопрос |

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

### Что validate показал для трёх примеров Anthropic

Anthropic выложила три примера в [папку mods репозитория anthropics/claude-code-playground](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods) и распространяет их как есть, без поддержки. Вывод `validate` на коммите `569c5283` от 1 октября 2026 года:

```text
$ claude plugin validate claude-code/mods/token-weather
  ❯ ./token-weather.mjs hooks: session.start, turn.complete, ui.render{component=AbovePrompt}
  ❯ ./token-weather.mjs calls: $.session.usage (via takeReading), $.ui.invalidate (via takeReading), $.ui.resolve
✔ Validation passed

$ claude plugin validate claude-code/mods/blast-radius
  ❯ ./blast-radius.mjs hooks: tool.call{tool=Bash}, ui.render{component=Pane}, ui.render{component=AbovePrompt}
  ❯ ./blast-radius.mjs calls: $.clock.now, $.process.run, $.session.cwd, $.ui.close, $.ui.invalidate, $.ui.open, $.ui.resolve, $.ui.toast
✔ Validation passed

$ claude plugin validate claude-code/mods/replay-theater
  ❯ ./replay-theater.mjs hooks: session.start, command.run{command=replay}, tool.call, turn.start, turn.complete, ui.render{component=AbovePrompt}, ui.render{component=Pane}, ui.close
  ❯ ./replay-theater.mjs calls: $.clock.sleep (via openReplay), $.command.register, $.fs.exists (via stepsFor), $.fs.read (via stepsFor), $.session.cwd (via stepsFor), $.ui.close (via replayView), $.ui.invalidate, $.ui.open (via openReplay), $.ui.resolve
✔ Validation passed
```

Ни один из трёх не вызывает `$.http.fetch`, `$.env.get` или `$.model.complete`. token-weather рисует прогноз заполнения контекста над полем ввода и обходится чтением статистики сессии — тут списка `validate` достаточно. У replay-theater есть `tool.call` и `$.fs.read`: он видит каждый вызов инструмента и читает файлы, поэтому стоит посмотреть, какие именно.

Самый показательный случай — blast-radius. Он придерживает рискованную shell-команду и показывает, что она изменит, с кнопками «продолжить» и «отменить». `validate` сообщает лишь, что мод запускает программы (`$.process.run`). Какие — видно только в `hooks/blast-radius.mjs`:

- `sleep` в цикле ожидания, пока вы нажимаете кнопку. Комментарий в коде объясняет: у обработчика есть 10 секунд собственного времени, а время внутри вызова `$` не считается;
- два вспомогательных скрипта через `bash -c`: один определяет, куда ведёт `cd`, другой считает файлы и байты, которые удалит `rm`.

Придерживает он `rm` с `-r` или `-f`, `git reset --hard`, `git push --force` (а также `-f`, `--force-with-lease` и `+ref`), `alembic upgrade`, `rails db:migrate`, `prisma migrate` и `manage.py migrate`. Вывод: `validate` называет возможность, исходник — конкретную программу. Сам blast-radius в сессии не запускался, его поведение здесь описано по коду.

## Как попробовать, установить и удалить мод Claude Code

Сначала мод лучше запустить на одну сессию, а ставить уже после.

### На одну сессию: флаг --plugin-dir

```bash
claude --plugin-dir ./token-weather
```

Флаг загружает папку плагина только для этой сессии и, по документации, перезагружает код мода при каждом сохранении файла. Для нескольких модов флаг повторяют. Там, где флаг передать нельзя, то же делает переменная `CLAUDE_CODE_PLUGIN_DIRS`.

### Установка из маркетплейса: команды и реальный вывод

Мод ставится как обычный плагин. В сессии это `/plugin install <плагин>@<маркетплейс>`, в оболочке — `claude plugin install <плагин>@<маркетплейс>`. Ниже полный цикл для token-weather из локального клона примеров Anthropic. Чтобы не трогать основной `~/.claude`, команды выполнялись с отдельной папкой настроек через `CLAUDE_CONFIG_DIR`.

```text
$ cd claude-code-playground/claude-code/mods
$ claude plugin marketplace add ./
✔ Successfully added marketplace: claude-code-playground-mods (declared in user settings)

$ claude plugin install token-weather@claude-code-playground-mods --scope user
✔ Successfully installed plugin: token-weather@claude-code-playground-mods (scope: user)

$ claude plugin list
Installed plugins:
  ❯ token-weather@claude-code-playground-mods
    Version: 0.1.0
    Scope: user
    Status: ✔ enabled

$ claude plugin disable token-weather@claude-code-playground-mods
✔ Successfully disabled plugin: token-weather (scope: user)

$ claude plugin uninstall token-weather@claude-code-playground-mods
✔ Successfully uninstalled plugin: token-weather (scope: user)
```

Что важно знать про установку:

- Если вы ставите мод из оболочки, пока сессия открыта, выполните в ней `/reload-plugins`, иначе мод загрузится при следующем запуске.
- Область по умолчанию — `user`; есть `--scope project` и `--scope local`.
- Источником маркетплейса может быть репозиторий GitHub в виде `owner/repo` (с `#ref` при необходимости), любой git-URL, локальный путь, начинающийся с `./` или `../`, либо URL файла `marketplace.json`.
- Маркетплейс из локального клона указывает на этот клон: если папку переместить или удалить, мод перестанет загружаться.

По документации, в терминальной сессии под вкладками `/plugin` появляется тусклая строка вида `1 mod active · first-mod`; встроенные моды в ней не перечисляются. На тестовой машине эту строку увидеть было негде — интерактивной сессии не было.

### Отключить один мод или все сразу

| Что выключить | Как |
| --- | --- |
| Один мод | `/plugin` → клавишей Tab перейти на вкладку **Installed** → отключить или удалить; или `claude plugin disable` / `claude plugin uninstall` в оболочке |
| Все установленные моды на одну сессию | Запустить `claude --safe-mode` (выключит и остальные ваши настройки) |
| Все ваши моды во всех сессиях | `"disableAllHooks": true` в `~/.claude/settings.json` (остановит и хуки настроек, и свою строку состояния; то, чем управляет организация, продолжит работать) |

`disableAllHooks` и политика `allowManagedModsOnly` останавливают только код мода: скиллы, команды, агенты и MCP-серверы того же плагина продолжают загружаться.

Встроенные моды Claude Code эти переключатели (`disableAllHooks`, `--bare`, `--safe-mode`) не останавливают. Они перечислены в `/plugin` на вкладке Installed в разделе Built-in: `cc-plugin-agents-md` (загружает `AGENTS.md`), `cc-plugin-diff` (рисует панель `/diff`), `cc-plugin-plugin-authoring` (даёт Claude скилл `plugin-authoring`), `cc-plugin-sec-default` (защита), `cc-plugin-telemetry` и `cc-plugin-you-should-know` — побочный агент с заметками над полем ввода, по умолчанию выключен и включается командой `/plugin enable cc-plugin-you-should-know@builtin`. [Исходники встроенных модов в папке mods репозитория anthropics/claude-code](https://github.com/anthropics/claude-code/tree/main/mods) открыты — это заодно хорошие примеры полноценных модов с тестами.

### Откуда брать моды

Моды распространяются через те же маркетплейсы плагинов, что и остальные расширения. От самой Anthropic есть три примера из claude-code-playground и исходники встроенных модов. Есть и сторонний каталог [awesome-claude-code-mods на GitHub](https://github.com/karanb192/awesome-claude-code-mods): он собирает публичные моды с GitHub вместе с выводом `claude plugin validate`. Автор каталога сам предупреждает, что это независимый скан, а не официальный каталог, и что прохождение `validate` не гарантирует безопасного поведения. Любой мод оттуда проверяйте так же, как описано выше.

## Свой мод Claude Code: попросить Claude или написать три файла

Свой мод можно получить двумя путями: описать его Claude в интерактивной сессии или написать три файла руками. Ни Node.js, ни сборщик, ни шаг сборки не нужны — Claude Code загружает `.js` и `.ts` напрямую. Пошаговое руководство есть в [документации «Создание мода»](https://code.claude.com/docs/ru/plugins/mods/create).

### Попросить Claude написать мод

Этот путь описан по документации: для него нужна интерактивная сессия с входом в аккаунт, и он не проверялся.

1. Опишите мод своими словами, например `make a mod that shows the current git branch above the prompt`. Claude работает по встроенному скиллу `plugin-authoring`; его можно загрузить и вручную командой `/plugin-authoring`.
2. Claude пишет файлы в `~/.claude/dev-mods/<ID сессии>/<имя мода>/`. В режимах `default` и `acceptEdits` Claude Code спросит разрешение на каждый файл: `~/.claude` — защищённый путь.
3. На первом файле Claude Code предложит включить горячую перезагрузку для сессии: **Enable for this session** или **Not now**.
4. Мод, написанный Claude, загружается только в этой сессии, а папка удаляется, когда становится старше `cleanupPeriodDays`. Чтобы сохранить мод, скопируйте папку, например в `~/mods/git-branch`, и запускайте с `claude --plugin-dir ~/mods/git-branch` или добавьте в маркетплейс.

Такой мод не загрузится в `claude -p`, в режиме `dontAsk`, в недоверенной папке и при выключенных модах: одобрить его там некому или нечем.

### Написать first-mod вручную

Учебный мод из документации считает вызовы инструментов, показывает счётчик рядом со спиннером и добавляет команду `/tally`. Структура:

```text
first-mod/
├── .claude-plugin/
│   └── plugin.json
└── hooks/
    ├── hooks.json
    └── register.js
```

`first-mod/.claude-plugin/plugin.json` — манифест плагина:

```json
{
  "name": "first-mod",
  "version": "0.1.0",
  "description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
  "author": { "name": "Your Name" }
}
```

`first-mod/hooks/hooks.json` — именно ключ `modules` делает плагин модом:

```json
{
  "description": "The first-mod hooks module",
  "modules": ["./register.js"]
}
```

`first-mod/hooks/register.js` — сам код. Claude Code вызывает экспортированную функцию `register` и передаёт ей `on`; каждый вызов `on` регистрирует обработчик события. Каждый обработчик получает три аргумента: `$` (API модов), `e` (событие) и `next` (передать событие дальше).

```javascript
// Счётчик, общий для всех обработчиков ниже
let calls = 0

// Claude Code вызывает это один раз при загрузке мода
export function register(on) {
  // При старте сессии: регистрируем команду /tally
  on('session.start', async ($, e, next) => {
    await $.command.register({
      name: 'tally',
      description: 'Show how many tool calls Claude has made',
    })
    return next(e)
  })

  // Перед каждым вызовом инструмента: считаем и просим перерисовать интерфейс
  on('tool.call', async ($, e, next) => {
    calls += 1
    $.ui.invalidate('ui.render')
    return next(e)
  })

  // Только для /tally: отвечаем сами, без хода Claude
  on('command.run', { command: 'tally' }, async () => {
    return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
  })

  // При отрисовке спиннера: оставляем его, добавив счётчик после слова
  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
    return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
  })
}
```

Здесь видны все три способа обработать событие: `session.start` и `tool.call` наблюдают и пропускают событие дальше через `next(e)`, `command.run` отвечает сам и `next` не вызывает, `ui.render` переписывает событие и передаёт изменённую копию.

Проверка без сессии и без входа в аккаунт:

```text
$ claude plugin validate ./first-mod
Validating plugin manifest: .../first-mod/.claude-plugin/plugin.json
Validating hooks: .../first-mod/hooks/hooks.json
  ❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
  ❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
```

Для автотеста документация предлагает файл `first-mod/tests/first-mod.test.ts`: он имитирует два вызова инструментов и проверяет ответ `/tally`.

```typescript
import { expect, test } from 'claude-code/testing'

test('/tally reports the tool calls the mod has seen', async ($, on) => {
  // Отвечаем на вызовы вместо Claude Code, чтобы инструменты не запускались
  on('tool.call', () => ({ result: 'ok' }))

  await $.tool.call({ tool: 'Bash', command: 'ls' })
  await $.tool.call({ tool: 'Read', file_path: 'README.md' })

  const answer = await $.command.run({ command: 'tally', args: '' })
  expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
```

```text
$ cd first-mod && claude plugin test
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [64.48ms]
 1 pass
 0 fail
Ran 1 test across 1 file. [0.34s]
```

Время выполнения от запуска к запуску разное. Команду можно проверить и в неинтерактивном режиме — она сработала без входа в аккаунт, потому что `command.run` отвечает без обращения к модели:

```text
$ claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
```

В интерактивной сессии (`claude --plugin-dir ./first-mod`) счётчик по документации появляется после слова спиннера, например `Thinking · tool calls: 2…`. При сохранении файла мод перезагружается, `register` выполняется заново и `calls` обнуляется; чтобы значение пережило перезагрузку, в API есть `$.state`.

Правила, без которых статический анализ не увидит ваш код:

- пишите каждый вызов целиком: `$.namespace.method(...)`; не присваивайте `$` или его пространство имён переменной и не деструктурируйте (`const ui = $.ui` упадёт с `$.ui is used as a value`);
- имя события в `on` — строковый литерал, не переменная и не цикл по списку;
- внутри `register` не объявляйте второй `on`;
- импортируйте только файлы из папки плагина по относительному пути (из «голых» импортов разрешён лишь `claude-code`) и только объявлениями `import` вверху файла: динамический `import()` и `require` не подходят;
- имя плагина, похожее на имена Anthropic, например начинающееся с `claude-`, не пройдёт `validate`.

При загрузке через `--plugin-dir` Claude Code кладёт в `.claude-plugin/types/` файлы `.d.ts` с событиями и методами вашей версии. Если они расходятся с документацией, верьте им: события и методы меняются от релиза к релизу.

### Две ошибки validate на 2.1.288

Опечатка в имени события (`'tool.calls'` вместо `'tool.call'`):

```text
✘ Found 1 error:
  ❯ modules../register.js: bad-mod: .../hooks/register.js:7: "tool.calls" is not an event; $ is always spelled $.noun.event(...) at the call site, on is always on("<event>", hook), and next.to always next.to(e, "<tier>")
✘ Validation failed
```

Опечатка в ключе `hooks.json` (`"module"` вместо `"modules"`) при отсутствии ключа `hooks`:

```text
✘ Found 1 error:
  ❯ root: hooks.json must have `hooks` (the hook matchers) or `modules` (hooks modules), or both
✘ Validation failed
```

В разделе документации об устранении неполадок сказано, что без ключа `modules` `validate` проходит, но не выводит строку `hooks:`. На 2.1.288 файл, где нет ни `modules`, ни `hooks`, проверку не прошёл; случай из документации, видимо, относится к `hooks.json`, в котором ключ `hooks` есть. В обоих вариантах признак один: нет строки `hooks:` — мод не загрузится.

### Как поделиться модом

Несколько человек — отправьте папку или `.zip`. Команда — заведите свой маркетплейс, например приватный репозиторий с папкой на каждый плагин. Вся организация — администратор устанавливает моды через управляемые настройки. Все — публичный репозиторий маркетплейса или заявка в каталог Anthropic. В README укажите, на какой версии Claude Code мод проверен. Разрабатывайте против папки с `--plugin-dir`: установленная копия кэшируется по версии, и правки до неё не доходят, пока вы не поднимете `version` и не переустановите.

## Мод ничего не делает: где искать причину

Первым делом — `claude plugin validate`. Если нужного события нет в строке `hooks:`, Claude Code этот обработчик тоже не вызовет.

Если `validate` проходит, а мод молчит, причина записана одной строкой с именем мода. Где её искать:

- в сессии с `--plugin-dir` и горячей перезагрузкой — прямо в транскрипте;
- в остальных интерактивных сессиях — только в отладочном журнале, запускайте `claude --debug`;
- в `claude -p` — в stderr.

Строки отказа начинаются с `hooks module <name> not loaded:`, дальше идёт причина, например `disableAllHooks in managed settings`, `only managed plugins and built-in plugins run`, `(--bare)` или `another plugin of that name loads first`. Если мод упёрся в политику организации или в защиту `sec-default`, вы увидите сообщения вида `mods are limited to your organization's by policy (allowManagedModsOnly)` или `tried to lift a deny rule in your settings`.

Ещё два условия: в новой папке ни один мод не загрузится, пока вы не ответите на запрос доверия к ней, а с `--safe-mode` не загружается ничего.

Если мод работает, но обрывается, проверьте лимиты. Значения из [справочника по модам](https://code.claude.com/docs/ru/plugins/mods/reference) могут меняться между релизами:

| Что | Лимит |
| --- | --- |
| Собственное время обработчика на событие | 10 секунд (50 мс для `prompt.edit`) |
| `$.process.run` | 30 секунд по умолчанию, максимум 10 минут |
| `$.fs.read` и `$.fs.write` | 4 МиБ на файл |
| `$.store` | 4 МиБ JSON суммарно |
| `$.model.complete`, `maxTokens` | 1024 по умолчанию, до 64 000 или лимита модели |
| Имена команд, инструментов и панелей | Буквы, цифры, `_` и `-`, до 64 символов |
| Один тест в `claude plugin test` | 5 секунд по умолчанию |

Время внутри вызовов `$` в лимит обработчика не входит — именно поэтому blast-radius ждёт нажатия кнопки через `$.process.run` с `sleep`, а не в самом обработчике.

## Источники

Внешние страницы, на которые ссылается это руководство, в порядке упоминания. Последнее обновление: 2026-10-06.

- [анонс «Customize Claude Code with mods in TypeScript» в блоге Claude](https://claude.com/blog/claude-code-mods) (claude.com)
- [документации «Устранение неполадок с модами»](https://code.claude.com/docs/ru/plugins/mods/troubleshoot) (code.claude.com)
- [официальному обзору модов](https://code.claude.com/docs/ru/plugins/mods/overview) (code.claude.com)
- [документации «Управление модами для вашей организации»](https://code.claude.com/docs/ru/plugins/mods/admin) (code.claude.com)
- [папку mods репозитория anthropics/claude-code-playground](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods) (github.com)
- [Исходники встроенных модов в папке mods репозитория anthropics/claude-code](https://github.com/anthropics/claude-code/tree/main/mods) (github.com)
- [awesome-claude-code-mods на GitHub](https://github.com/karanb192/awesome-claude-code-mods) (github.com)
- [документации «Создание мода»](https://code.claude.com/docs/ru/plugins/mods/create) (code.claude.com)
- [справочника по модам](https://code.claude.com/docs/ru/plugins/mods/reference) (code.claude.com)
