Anthropic выкатила большой практический гид по модам Claude Code, автор — Addy Osmani. Мод — это маленький файл на JavaScript или TypeScript, который живёт прямо внутри твоей сессии: он видит все события, может переписывать поведение агента и рисовать собственный интерфейс в терминале или десктоп-приложении.
Главная мысль простая: чтобы написать первый мод, учить API не нужно — достаточно описать желаемое словами, и Claude Code сам соберёт файл с горячей перезагрузкой. В гиду с нуля делают Token Weather — прогноз заполнения контекстного окна над промптом, строк на 80, — а потом показывают два примера пожирнее: предохранитель перед опасными командами и плеер для просмотра правок. Ниже перевод целиком, код не трогал.
Мод — это небольшой файл на JavaScript или TypeScript, который работает прямо внутри твоей сессии Claude Code. Он умеет следить за происходящим, менять то, что делает Claude Code, или рисовать собственный интерфейс — в терминале и в десктопном приложении. Чтобы попробовать, API учить не надо: запусти claude и опиши словами, какой мод хочешь. Когда спросит про горячую перезагрузку — разреши, и мод появится, как только закончится ход.
Claude Code и так можно многое настроить: настройки, правила разрешений, слэш-команды, скиллы и статус-строка. Моды идут дальше: они могут переписывать или подменять то, что делает Claude Code, и рисовать кастомный интерфейс. Под капотом мод — это хук, упакованный в плагин, и каждый мод видит все события сессии по мере их появления.
По сути это способ подогнать Claude Code под свой стиль работы: добавить показометр, который смотришь постоянно, поставить предохранитель перед командами, от которых не по себе, или собрать свой вью для чтения правок.
Этот гид собирает один мод с пустой папки — Token Weather, живой прогноз заполнения контекстного окна, нарисованный над промптом. Это примерно 80 строк. Потом показывает два мода побольше — Blast Radius и Replay Theater, чтобы продемонстрировать остальное, на что способен API.
Видео (терминал): Token Weather, Blast Radius и Replay Theater одна за другой
Видео (десктоп): те же три демо в приложении Claude для десктопа
Нужен Claude Code 2.1.287 или новее. Моды включены по умолчанию, ничего включать не надо. API может меняться между релизами. Каждый раз, когда Claude Code загружает мод, он пишет декларации типов для твоей сборки в папку мода .claude-plugin/types/ — это и есть эталон для твоей версии.
Как устроен мод
Мод — это плагин Claude Code, чьё поведение живёт в модуле на JavaScript или TypeScript:
- Папка — обычный плагин с манифестом
.claude-plugin/plugin.json. hooks/hooks.jsonназывает один модуль в полеmodules.- Модуль экспортирует
register(on, options). Внутриon(event, matcher?, hook)добавляет хук.
У каждого хука одна и та же форма:
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
// $ the mods API: ui, session, state, store, fs, process, clock, http, tool, command, model, ...
// e this event's input, as plain data
// next passes e to the other plugins and then to Claude Code's own behavior
return next(e);
});
Хуки образуют цепочку, как middleware в веб-фреймворках. Сначала отрабатывает твой, затем next(e) передаёт событие следующему плагину, а в самом низу Claude Code делает то, что сделал бы и без вас. Хук может сделать одно из трёх:
Схема: событие проходит через твой хук, потом другие плагины, потом Claude Code. Каждый передаёт его дальше через next(e), и результат возвращается обратно. Хук, который отвечает сам, завершается без вызова next.
| Ход | Как | Пример |
|---|---|---|
| Наблюдай | const r = await next(e); /* look */ return r | Записывать каждую правку файла. Снимать показания после каждого хода. |
| Перепиши | return next({ ...e, command: safer }) | Изменить то, что увидит остальная цепочка. |
| Ответь сам | return { deny: "…" } без вызова next | Отказать вызову инструмента. Самому обслужить команду или инструмент. |
События покрывают вызовы инструментов, отправленный промпт, начало и конец хода, старт и завершение сессии, слэш-команды и ui.render — каждый кусок интерфейса в момент отрисовки. Модуль работает в собственной песочнице, без DOM и без Node, поэтому всё снаружи идёт через $.
Чем это отличается от хуков из настроек. Хук из настроек запускает shell-команду на каждое событие и гоняет JSON через stdin и stdout. Мод загружается один раз и остаётся в сессии. Он может хранить состояние, рисовать интерфейс, который обновляется по мере событий, и дёргать Claude Code обратно: открыть панель, запустить процесс, зарегистрировать слэш-команду или инструмент, который сможет вызывать модель.
Claude Code сам их использует. Часть собственных функций Claude Code написана как моды, включая поддержку AGENTS.md и панель /diff рядом с диалогом. Их исходники с тестами лежат в открытом репозитории anthropics/claude-code в папке mods/ — можно почитать, как команда пишет моды.
Собираем первый мод: Token Weather
Token Weather после каждого хода смотрит, насколько заполнено контекстное окно, и рисует одну строку над промптом: иконку погоды, проценты, сколько токенов из окна занято, маленький график последних ходов и прирост за последний ход.
| Занято | Прогноз |
|---|---|
| до 25% | ☀ Clear |
| 25–49% | ☁ Cloudy |
| 50–74% | ☂ Showers |
| 75–89% | ☇ Storm |
| 90% и выше | ↯ Compact soon |
Вот он в реальной сессии. С каждым ходом читается больше файлов, и полоска ползёт от ☀ Clear к ☂ Showers и ☇ Storm:
Видео (терминал): Token Weather за три хода — 18%, 67% и 81% от окна в 200k
Видео (десктоп): Token Weather за три хода — 10%, 54% и 77% от окна в 200k
Быстрый путь: пусть мод напишет Claude
Шесть шагов можно пропустить. Claude Code умеет писать моды, так что опиши нужный и доверь работу ему. Запусти сессию через claude и вставь промпт ниже:
Make me a Claude Code mod called token-weather: a live forecast of my context window, shown in the band above the prompt.
What it should show, on one line:
- A weather icon and word for how full the context window is: under 25% ☀ Clear (yellow), 25–49% ☁ Cloudy (cyan), 50–74% ☂ Showers (blue), 75–89% ☇ Storm (magenta), 90% and up ↯ Compact soon (red).
- The percentage used, then the tokens used out of the window, like "134.4k / 200k".
- A small chart of the last 12 turns, drawn with ▁▂▃▄▅▆▇█.
- How much the last turn added, like "▲ +98.3k last turn".
It should update after every turn.
Claude один раз спросит, включать ли горячую перезагрузку для этой сессии. Разреши — и полоска появится над промптом, когда ход Claude закончится. Дальше любое изменение подхватывается на месте: можно продолжать просить правки («пусть Storm начинается с 70%», «добавь цену в долларах в конце») и смотреть, как полоска меняется. Мод живёт только в этой сессии, а его папку потом подчистят, так что если понравился — скопируй папку и поставь как обычный плагин (см. «Шаг 6»).
Обрати внимание: промпт описывает только то, что ты хочешь видеть. Знать API не нужно. Встроенный гид Claude Code по написанию модов знает «как»: где хранить состояние, чтобы оно пережило перезагрузку, как проверить плагин через claude plugin validate, какие события перехватывать. Поменяй строки «What it should show» — и это уже твой мод, а не авторский.
Если хочешь сначала понять, как он устроен, или проверить, что там написал Claude, — читай дальше.
Шаг 1: создай папку
Проверь, что твой Claude Code достаточно свежий:
claude --version # 2.1.287 or later
Создай такую структуру:
token-weather/
├── .claude-plugin/
│ ├── plugin.json
│ └── types/ (written by Claude Code when it loads the mod)
├── hooks/
│ ├── hooks.json
│ └── token-weather.mjs
├── types/
│ └── index.d.ts (added in step 3)
└── tests/
└── token-weather.test.ts (added in step 5)
.claude-plugin/plugin.json — стандартный манифест плагина:
{
"name": "token-weather",
"version": "0.1.0",
"description": "A live forecast of the context window, drawn above the prompt.",
"author": { "name": "You" }
}
hooks/hooks.json указывает на модуль. У мода он ровно один:
{
"modules": ["./token-weather.mjs"]
}
Шаг 2: нарисуй что-нибудь
Полоска прямо над промптом — это компонент AbovePrompt. Сам Claude Code там ничего не рисует, так что это хорошая первая цель. Подцепи его событие ui.render и верни дерево элементов:
// hooks/token-weather.mjs
export function register(on) {
on("ui.render", { component: "AbovePrompt" }, ($, e, next) => {
const { Box, Text } = $.ui.resolve(e);
return Box({
paddingX: 1,
children: [Text({ color: "yellow", bold: true, children: "☀ Clear skies" })],
});
});
}
Элементы — не глобальные переменные. $.ui.resolve(e) возвращает конструкторы для той поверхности, которую рисуют: каждая поддерживает чуть свой набор. JSX тоже работает, фабрикой служит h.
Запусти сессию с плагином:
claude --plugin-dir ./token-weather
Над промптом появляется «☀ Clear skies». Не закрывай сессию: папка под наблюдением, каждое сохранение перезагружает модуль на месте, без перезапуска. Именно этот быстрый цикл «сохранил — увидел» и делает написание модов приятным.
Совет: когда понял устройство, следующий мод просто опиши Claude, как в «быстром пути». Он запишет плагин в папку, которая горячо перезагружается в той же сессии.
Шаг 3: читай реальные цифры и храни их в $.state
$.session.usage() возвращает те же цифры, что и статус-строка. context.tokens — вход, по которому отвечал последний ответ, context.window — окно модели, а context.percent — отношение первого ко второму. Вызов бесплатный: запрос на подсчёт токенов уходит, только если попросить breakdown.
Снимай показания на старте сессии и после каждого хода:
on("session.start", async ($, e, next) => {
const result = await next(e);
await takeReading($);
return result;
});
on("turn.complete", async ($, e, next) => {
const result = await next(e);
if (!e.agentId) {
await takeReading($); // main-loop turns only, not subagents
}
return result;
});
Оба хука сначала вызывают next(e) и только потом наблюдают. Поведение они не меняют.
Где хранить показания. Обычная переменная на уровне модуля кажется очевидным выбором, но горячая перезагрузка — это загрузка с нуля: register отработает снова, session.start сработает снова, а переменные модуля обнулятся. Историю держи в $.state. Он хранит именованные значения на стороне хоста в течение всей сессии, и они переживают перезагрузки.
// Held by the host, so the history survives a hot reload of this file.
const readings = { plugin: "token-weather", key: "readings" };
async function takeReading($) {
const { context } = await $.session.usage();
if (!context?.window) return;
const tokens = context.tokens ?? 0;
const percent = context.percent ?? Math.round((tokens / context.window) * 100);
const { value: history = [] } = await $.state.get(readings);
await $.state.set(readings, [...history, { tokens, window: context.window, percent }].slice(-HISTORY));
}
Значение состояния объявляется в контракте типов плагина — небольшом файле .d.ts, на который указывает манифест. Добавь types/index.d.ts:
export type TokenWeatherReading = { tokens: number; window: number; percent: number };
declare module "claude-code" {
interface PluginState {
"token-weather": { readings: TokenWeatherReading[] };
}
}
Затем добавь "types": "./types/index.d.ts" в plugin.json. Пропустишь этот шаг — claude plugin validate остановит тебя с ошибкой, которая прямо называет лекарство: token-weather.readings is not declared: the manifest's types contract must name it in interface PluginState { … }.
В награду перерисовка достаётся бесплатно. $.state.get, сделанный во время работы рендер-хука, подписывает этот рисунок, и каждый последующий $.state.set перерисовывает полоску. Вызывать $.ui.invalidate никогда не нужно.
Шаг 4: рисуй прогноз
Вот весь модуль целиком:
// Token Weather: a live forecast of the context window, above the prompt.
const HISTORY = 12;
const BARS = "▁▂▃▄▅▆▇█";
const FORECAST = [
{ upTo: 25, icon: "☀", word: "Clear", color: "yellow" },
{ upTo: 50, icon: "☁", word: "Cloudy", color: "cyan" },
{ upTo: 75, icon: "☂", word: "Showers", color: "blue" },
{ upTo: 90, icon: "☇", word: "Storm", color: "magenta" },
{ upTo: Infinity, icon: "↯", word: "Compact soon", color: "red" },
];
// Held by the host, so the history survives a hot reload of this file.
const readings = { plugin: "token-weather", key: "readings" };
export function register(on) {
on("session.start", async ($, e, next) => {
const result = await next(e);
await takeReading($);
return result;
});
on("turn.complete", async ($, e, next) => {
const result = await next(e);
if (!e.agentId) {
await takeReading($); // main-loop turns only, not subagents
}
return result;
});
on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
const { value: history = [] } = await $.state.get(readings);
if (e.props.hasSurvey || history.length === 0) {
return next(e);
}
const { Box, Text } = $.ui.resolve(e);
return band(Box, Text, history, e.props.bodyColumns);
});
}
async function takeReading($) {
const { context } = await $.session.usage();
if (!context?.window) return;
const tokens = context.tokens ?? 0;
const percent = context.percent ?? Math.round((tokens / context.window) * 100);
const { value: history = [] } = await $.state.get(readings);
await $.state.set(readings, [...history, { tokens, window: context.window, percent }].slice(-HISTORY));
}
function band(Box, Text, history, columns) {
const now = history[history.length - 1];
const f = FORECAST.find((b) => now.percent < b.upTo);
const parts = [
Text({ color: f.color, bold: true, children: `${f.icon} ${f.word}` }),
Text({ children: ` ${now.percent}% of context` }),
Text({ dimColor: true, children: ` ${short(now.tokens)} / ${short(now.window)}` }),
];
if (columns >= 60) {
parts.push(Text({ dimColor: true, children: " last turns " }));
parts.push(Text({ color: f.color, children: sparkline(history) }));
if (history.length > 1) {
parts.push(Text({ dimColor: true, children: trend(history) }));
}
}
return Box({ flexDirection: "row", paddingX: 1, children: parts });
}
function sparkline(history) {
const top = Math.max(...history.map((r) => r.tokens), 1);
return history.map((r) => BARS[Math.floor((r.tokens / top) * (BARS.length - 1))]).join("");
}
function trend(history) {
const delta = history[history.length - 1].tokens - history[history.length - 2].tokens;
if (delta === 0) return " steady";
return delta > 0 ? ` ▲ +${short(delta)} last turn` : ` ▼ ${short(-delta)} last turn`;
}
function short(n) {
if (n >= 1_000_000) return `${+(n / 1_000_000).toFixed(1)}M`;
if (n >= 1_000) return `${+(n / 1_000).toFixed(1)}k`;
return String(n);
}
Три детали стоит унести в свои моды:
- Пропсы компонента лежат в
e.props.hasSurveyговорит, что полоску хочет опрос, — тогда хук уступает место черезnext(e).bodyColumns— реальная ширина полоски: она уже терминала, когда сбоку от диалога пристыкована панель. Подгоняй дерево под неё. На верхнем уровнеeлежат толькоe.component,e.surface,e.requestIdиe.viewport. - Нечего рисовать — передавай дальше. Возврат
next(e)отдаёт полоску Claude Code и другим модам. - Используй символы одинарной ширины, не эмодзи. ☀ ☁ ☂ ☇ ↯ ровно встают в любом терминальном шрифте.
Сохрани файл — запущенная сессия его подхватит. После нескольких ходов с чтением больших файлов полоска доползёт от Clear до Showers и Storm, как в записи в начале раздела.
Шаг 5: проверь и протестируй
claude plugin validate читает манифест и исходник модуля так же, как это будет делать Claude Code, и сообщает, какие хуки и вызовы есть в модуле:
$ claude plugin validate ./token-weather
> types ./types/index.d.ts declares state: token-weather.readings
> ./token-weather.mjs hooks: session.start, turn.complete, ui.render{component=AbovePrompt}
> ./token-weather.mjs calls: $.session.usage (via takeReading), $.state.get, $.state.set (via takeReading), $.ui.resolve
> ./token-weather.mjs state writes: token-weather.readings
> ./token-weather.mjs state reads: token-weather.readings
√ Validation passed
claude plugin test гоняет *.test.ts-файлы плагина на настоящем рантайме Claude Code. Хуки, которые тест регистрирует через on, встают в цепочку после мода и подменяют то, что ответил бы Claude Code, — так ты точно контролируешь, что вернёт $.session.usage():
// tests/token-weather.test.ts
import { describe, expect, test } from "claude-code/testing";
describe("token-weather", () => {
test("the band follows the context window", async ($, on) => {
// Hooks registered here run after the mod and stub what Claude Code would answer.
let tokens = 36_100;
on("session.start", ($, e) => ({ cwd: e.cwd }));
on("session.usage", () => ({
value: { startedAt: 0, rateLimits: [], context: { tokens, window: 200_000, percent: Math.round(tokens / 2_000) } },
}));
on("turn.complete", () => ({ text: "" }));
await $.session.start({ surface: "terminal", isInteractive: true, cwd: "/work" } as any);
const ui = await $.ui.mount({
plugin: "token-weather",
surface: "terminal",
component: "AbovePrompt",
props: { hasSurvey: false, isWorking: false, maxRows: 10, bodyColumns: 120 },
} as any);
expect(await ui.find({ type: "Text", text: /Clear/ })).toBeDefined();
tokens = 134_400;
await $.turn.complete({ reason: "answer", answer: "ok", durationMs: 1 } as any);
expect(await ui.find({ type: "Text", text: /Showers/ })).toBeDefined();
expect(await ui.find({ type: "Text", text: /67% of context/ })).toBeDefined();
expect(await ui.find({ type: "Text", text: /▲ \+98\.3k last turn/ })).toBeDefined();
await ui.unmount();
});
});
$ claude plugin test ./token-weather
(pass) token-weather > the band follows the context window
1 pass
0 fail
Тест заодно проверяет перерисовку из шага 3: полоска обновляется после turn.complete, хотя мод ни разу не просил перерисовать.
Шаг 6: поделись
Мод — это плагин, значит и распространяется он так же. Положи его в маркетплейс, а маркетплейс может быть просто папкой с файлом .claude-plugin/marketplace.json:
{
"name": "my-mods",
"owner": { "name": "You" },
"plugins": [{ "name": "token-weather", "source": "./token-weather" }]
}
claude plugin marketplace add ./my-mods
claude plugin install token-weather@my-mods --scope user
Как поделиться модом
Мод — это плагин Claude Code, поэтому делится он как любой другой плагин, и учить ничего нового не надо. Кладёшь мод в репозиторий на GitHub вместе с файлом маркетплейса — и репозиторий становится твоим маркетплейсом. Любой может оттуда поставить, а ты обновляешь обычным пушем.
Установка — три команды в Claude Code:
/plugin marketplace add your-org/my-mods
/plugin install token-weather@my-mods
/reload-plugins
Мод стартует после перезагрузки плагинов. Не появился — перезапусти Claude Code.
Мод — это код, который работает внутри Claude Code на твоей машине с теми же правами, что есть у Claude Code, и написал его издатель, а не Anthropic. Поэтому ставь моды как пакеты: сначала прочитай репозиторий и ставь только от тех, кому доверяешь. Ничего не устанавливается, пока ты сам не выполнил команду.
Каталог Claude принимает плагины с модами, свой можно подать через claude.ai/directory/manage — так его найдут и без твоей ссылки.
Ещё два мода
Token Weather только наблюдает и рисует. Следующие два мода вмешиваются в события, открывают панели и принимают ввод.
Blast Radius: смотри, что изменит рискованная команда, до её запуска
Когда Claude вызывает Bash с rm -rf, git reset --hard, git clean, форс-пушем или миграцией базы, Blast Radius придерживает вызов. Он вычисляет, что команда затронет, и открывает панель с кнопками Proceed и Cancel. Нажмёшь 2 — Claude получит отказ с причиной. Нажмёшь 1 — команда выполнится как написано.
Видео (десктоп): Blast Radius на десктопе — те же 9 файлов (498 KB)
Он использует три хука: tool.call на Bash и ui.render на Pane и на AbovePrompt. Ядро его — ход «ответь сам» из таблицы выше:
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
const risk = classify(String(e.command ?? ""));
if (risk === null) return next(e); // everything else runs as normal
const report = await measure($, risk, await $.session.cwd()); // git status, git clean -n, du, ...
held = { command: e.command, risk, report, decision: null };
const opened = await $.ui.open({ id: "blast-radius", title: "Blast Radius", focus: true });
if (!opened.isPlaced) held.where = "band"; // too narrow for a pane: draw above the prompt
while (held.decision === null && !next.signal.aborted) {
await $.process.run(["sleep", "0.25"]); // time inside $ calls doesn't count against the hook's time limit
}
if (held.decision === "proceed") return next(e); // let it run
return { deny: `Blast Radius held this command: the user pressed Cancel. It would have: ${report.summary}.` };
});
Чему он учит:
$.process.runдля пробных прогонов. Отчёт собирается командами самих инструментов:git status --porcelain,git clean -n,git log HEAD..origin/main,showmigrations. Аргументы передаются массивом argv, так что ничего из пути не исполнится как shell-код.- Удержание вызова. Хук получает 10 секунд собственного времени на каждое событие, но время ожидания внутри
$-вызова не считается. Цикл ждёт короткими процессамиsleep, пока кнопка вonPressне запишет решение, и сдаётся, когдаnext.signalпрерывается (ты нажал Esc). - Кнопки с горячими клавишами.
Button({ label: "Proceed", hotkey: "1", onPress })работает и мышкой, и через Tab с Enter, и по цифре. - Деградация до полоски. Панель пристыковывается сбоку от диалога, когда терминал достаточно широкий. Если
$.ui.openотвечаетisPlaced: false, тот же отчёт рисуется над промптом:

При 120 колонках Blast Radius рисует отчёт по git reset —hard в полоске над промптом
Это страховка, а не система разрешений. Он читает текст команды, поэтому $(…), алиасы и скрипты, вызывающие rm, проходят мимо. Для жёсткого блока используй правила разрешений.
Replay Theater: прокрути правки последнего хода
Пока идёт ход, Replay Theater записывает каждый вызов Edit и Write: файл плюс текст до и после. Когда ход заканчивается, над промптом появляется подсказка. Нажми r (или набери /replay) — и панель проведёт тебя по правкам, по одному диффу за шаг, с полоской пронумерованных шагов и кнопками Prev, Next и Close.
Видео (десктоп): Replay Theater на десктопе — 6 правок в 4 файлах, затем шаги с 1 по 6 в панели
Он никогда не блокирует и не меняет правки. Он наблюдает:
on("tool.call", async ($, e, next) => {
if (EDIT_TOOLS.has(e.tool)) state.pending.push(...(await stepsFor($, e))); // old/new text → diff
return next(e); // the edit runs untouched
});
on("turn.start", ($, e, next) => { if (!e.agentId) state.pending = []; return next(e); });
on("turn.complete", async ($, e, next) => {
const r = await next(e);
if (!e.agentId && state.pending.length) state.replay = state.pending; // one replay per turn
return r;
});
on("session.start", async ($, e, next) => {
const r = await next(e);
await $.command.register({ name: "replay", description: "Step through the last turn's file edits" });
return r;
});
on("command.run", { command: "replay" }, async ($, e) => ({ text: (await openReplay($)) ? "Replaying" : "No edits" }));
Чему он учит:
- Сцепление событий.
turn.startиturn.completeсобирают правки в один проигрыш за ход, аe.agentIdне пускает в группировку ходы субагентов. - Регистрация слэш-команды.
$.command.registerвsession.start, затем ответ на неё вcommand.run. - Чтение файлов. Для Write вызов
$.fs.readзабирает старое содержимое прямо перед тем, как запись сядет, — дифф получается настоящий. - Размещение — забота поверхности. В полном экране панель встаёт справа. При 80 колонках открывается строкой над промптом. Мод рисует одно и то же дерево в обоих случаях.

Replay Theater при 80 колонках, нарисован строкой над промптом
Четыре привычки, которые стоит забрать себе
- Опирайся на типы, которые Claude Code пишет за тебя. При каждой загрузке мода он пишет декларации для твоей сборки в папку мода
.claude-plugin/types/, так что редактор иtsc -pработают без лишних шагов. Это справочник по всем событиям, всем методам$и всем пропсам элементов. - Читай пропсы из
e.props.hasSurvey,bodyColumnsи остальные лежат там, а не на самомe. - Рассчитывай на горячую перезагрузку. Каждое сохранение заново гоняет
registerиsession.start, поэтому данные держи в$.state, а не в переменных модуля. - Если рисунок не появился — смотри лог. Запусти
claude --debugи ищи строку про хук, вернувший дерево, которое не прошло валидацию.
А что замодишь ты?
Все три мода выросли из одного вопроса каждый: насколько полон мой контекст?, что вот-вот удалит эта команда? и что Claude только что поменял? Твои вопросы будут другими — в этом и смысл. Идеи для старта:
- Счётчик расходов или лимитов из
$.session.usage()— как статус-строка через$.ui.status - Хук
prompt.submit, который добавляет конвенции твоей команды к каждому промпту - Панель со списком файлов, которые Claude читал за сессию, — живая карта того, что он видел
- Таймер фокуса, который шлёт уведомление через
$.ui.toast, когда заканчивается длинный ход - Предохранитель на
tool.callпод свой стек: например, продакшн-контексты kubectl илиterraform apply
Покажи, что собрал
Собрал мод, которым теперь пользуешься каждый день? Выложи в X или LinkedIn с гифкой или скриншотом в работе — пусть другие разработчики видят, что возможно. Положи плагин в маркетплейс (см. «Как поделиться модом») и дай ссылку: каждому, кому понравится, хватит трёх команд для установки.
Фактчек
- Хуки, плагины, маркетплейсы — реальность. Хуки-на-событиях с shell-командами и JSON через stdin/stdout — задокументированный механизм Claude Code. Плагины с манифестом
.claude-plugin/plugin.json, маркетплейсы, команды/plugin marketplace add,/plugin install,/reload-pluginsи локальная загрузка при разработке — тоже существующие вещи, гид с ними согласуется. - База кастомизации названа верно. Настройки, правила разрешений, слэш-команды, скиллы, статус-строка — всё это в Claude Code есть, тут статья ничего не выдумывает.
- Сами моды — новое слово. В моих знаниях о Claude Code фичи mods не описано: API
register(on), события вродеui.render, компонентAbovePrompt, песочница без Node и DOM, автогенерация типов в.claude-plugin/types/— всё это можно принимать только на веру из статьи. Проверяется за пять минут, если поставить свежий Claude Code. - «Часть функций Claude Code — сами моды» — проверяемая претензия: статья отправляет в открытый репозиторий
anthropics/claude-code, папкаmods/. Есть ли там поддержка AGENTS.md и панель/diff— увидишь сам; заочно подтверждать не буду. Отмечу: исторически Claude Code использовалCLAUDE.md, так что поддержку AGENTS.md тоже стоит проверить глазами в репо. - Addy Osmani — известный инженер (годами вёл Chrome-команду в Google, автор книг по фронтенду). Строка «Member of Technical Staff» (в Anthropic) — принимается на веру по строке Author; подтвердить переход мне нечем.
- Версия 2.1.287, 10 секунд на хук, «бесплатный» вызов
$.session.usage(), подчистка папки мода после сессии — внутренние детали продукта; проверить нечем. Звучат правдоподобно и легко проверяются экспериментом. - Цифры демо (окно 200k, 18% → 81%, «9 файлов, 1.1 MB») — иллюстрации в записях экрана, а не какие-то данные Anthropic.
- Маркетингового тумана мало. Тон восторженный, но по делу. Хороший знак — честная оговорка про безопасность (мод — код с правами Claude Code, читай репозиторий перед установкой, «это страховка, а не система разрешений»): её легко можно было «забыть».
Как применить
- Сначала обновись.
claude --version— нужно 2.1.287 или новее; если старее — обнови (claude update). - Быстрый старт без кода. Запусти
claude, вставь промпт из раздела «Быстрый путь» (он в статье на английском, вставляется как есть), разреши горячую перезагрузку — полоска появится после первого хода. Понравился мод — скопируй его папку и установи как плагин. - Или руками. Собери структуру папок из шага 1, запусти
claude --plugin-dir ./token-weatherи правьhooks/token-weather.mjsна живую — сессия перезагружает мод при каждом сохранении. - Проверяй, прежде чем делиться.
claude plugin validate ./token-weatherиclaude plugin test ./token-weather— так ловишь ошибки типов и поведения до публикации. - Идеи под свою работу — из конца статьи: счётчик стоимости через
$.session.usage(), хукprompt.submitс конвенциями команды, панель прочитанных файлов, уведомление после длинного хода, предохранитель на прод-контексты kubectl илиterraform apply. Помни: такой предохранитель — страховка, а не запрет; жёсткие блоки — через правила разрешений. - Про безопасность. Мод — чужой код с правами Claude Code на твоей машине. Ставь только из репозиториев, которые прочитал.
- Кому всё это нужно: тем, кто проводит в Claude Code весь день и не понаслышке знает свои повторяющиеся ритуалы — индикаторы, стоп-краны и свои вью для чтения правок собираются именно под них.
