Ваш первый плагин
Создайте плагин через bb plugin new, установите его в bb, используйте в трёх местах и измените, пока bb его перезагружает. Шаблон плагина — это работающий список задач с одним хранилищем и тремя способами доступа: страница на боковой панели, команда bb hello и скилл, который указывает агенту использовать эту команду. Каждая часть соответствует разделу Как устроен плагин, который, как предполагается, вы уже прочитали.
Потребуется запущенный bb 0.43 и npm. Держите открытым макет зон, чтобы видеть, где в окне располагается каждая поверхность.
1. Создайте плагин
Заголовок раздела «1. Создайте плагин»$ bb plugin new helloCreated bb-plugin-hello/ (bb-plugin-hello).Installed dependencies (npm install).Next steps: cd bb-plugin-hello bb plugin install .Команда сама устанавливает зависимости. Если этот шаг завершается ошибкой, в выводе среди следующих действий будет npm install --include=dev; выполните её до шага 5.
Команда не принимает флагов. Пакет называется bb-plugin-hello, и bb получает id плагина hello из этого имени, отбрасывая префикс bb-plugin-. Id появляется в URL страницы, в пути к хранилищу и как имя команды CLI.
2. Найдите точки входа в шаблоне плагина
Заголовок раздела «2. Найдите точки входа в шаблоне плагина»Шаблон плагина содержит четыре файла, составляющие плагин, и три каталога UI-кода, которым вы управляете.
package.json— манифест: идентификатор npm плюс блокbb, который указываетserver.tsиapp.tsxкак две точки входа.server.ts— серверная точка входа: хранилище задач, четыре метода RPC и командаbb hello.app.tsx— клиентская точка входа: страница «Example todos» на боковой панели.skills/example-todos/SKILL.mdговорит агенту, какие командыbb helloсуществуют и когда их использовать.components/ui/,hooks/иlib/содержат вендорные компоненты shadcn и хелперы, которые вы можете менять.
Каждый сгенерированный файл, поля манифеста и правило зависимостей описаны в Анатомии пакета.
3. Изучите серверную точку входа
Заголовок раздела «3. Изучите серверную точку входа»Откройте server.ts. Без лишних деталей он делает следующее:
// server.ts, abridgedexport const rpcContract = defineRpcContract({ todos_list: { input: z.null(), output: z.object({ todos: z.array(todoSchema) }) }, todos_add: { input: z.object({ title: z.string().trim().min(1).max(200) }), output: todoSchema }, // todos_set_done, todos_remove});
export default async function plugin(bb: BbPluginApi) { const writeTodos = async (todos: Todo[]) => { await bb.storage.kv.set("todos", todos); bb.realtime.publish("todos-changed", { count: todos.length }); };
bb.rpc.register(rpcContract, { todos_list: …, todos_add: …, /* … */ }); bb.cli.register({ name: "hello", commands: [/* list, add, done, undo, remove */], async run(argv) { … } }); bb.onDispose(() => { bb.log.info("disposed"); });}Экспорт по умолчанию — это фабрика из Как устроен плагин. Она регистрирует четыре метода RPC и одну команду CLI, и оба вызывают одни и те же хелперы поверх bb.storage.kv. Каждая запись заканчивается вызовом bb.realtime.publish, который сообщает всем открытым страницам об изменении списка. Контракт экспортируется, чтобы app.tsx мог импортировать его тип.
4. Изучите клиентскую точку входа
Заголовок раздела «4. Изучите клиентскую точку входа»Откройте app.tsx. Страница запрашивает список у сервера по RPC и повторяет запрос при каждом сигнале:
// app.tsx, abridgedfunction useTodos() { const rpc = useRpc<typeof rpcContract>(); // typed by the server's contract // rpc.call("todos_list") on mount, and again on every "todos-changed" signal useRealtime("todos-changed", refetch); …}
function TodosPage() { const { rpc, todos, refetch } = useTodos(); …}
export default definePluginApp((app) => { app.slots.navPanel({ id: "example-todos", title: "Example todos", icon: "ListTodo", path: "example-todos", component: TodosPage, // route /plugins/hello/example-todos });});app.slots.navPanel добавляет строку в боковую панель bb и выделяет плагину целую страницу. Файл импортирует только тип rpcContract, поэтому серверный код не попадает в бандл браузера. React и SDK тоже не собираются в бандл: bb поставляет их в рантайме, поэтому app.tsx работает только внутри bb.
5. Установите плагин и используйте его из трёх мест
Заголовок раздела «5. Установите плагин и используйте его из трёх мест»cd bb-plugin-hellobb plugin install . # a path install: bb loads server.ts directly and builds only app.tsxbb plugin dev # leave running: rebuilds and reloads on every savebb plugin install предупреждает, что плагины выполняются с полным доверием, и просит подтверждения; ответьте y. Без терминала для подтверждения команда откажется работать, если не передать --yes. После установки bb plugin list покажет hello@0.1.0 running.
Теперь проверьте плагин тремя способами, которые настраивает шаблон:
- В bb откройте Example todos на боковой панели и добавьте задачу.
- В терминале запустите
bb hello add "Ship it". Страница покажет новую задачу без перезагрузки, потому что запись команды опубликовалаtodos-changed. - В треде попросите агента добавить задачу. Скилл велит ему выполнить
bb hello add, поэтому изменение пойдёт по тому же пути, что и команда в терминале.
app.tsx · navPanel “Example todos”
/plugins/hello/example-todos
rpc.call("todos_add", …)
useRealtime("todos-changed", refetch)
1↓ rpc.call(…) → POST /api/v1/plugins/hello/rpc/<method>
POST /api/v1/plugins/hello/rpc/<method>
input validated by the schema
{ ok:true, result } | { ok:false, error }
↓ server.ts — jiti, same process
server.ts — jiti, same process
bb.settings.define({ showDone })
bb.rpc.register(rpcContract, …)
bb.cli.register({ name: "hello" })
bb.realtime.publish("todos-changed")
bb.onDispose(() => …)
↓ read and write → bb.storage.kv → plugin_kv in bb.db
4↓ publish to every client → WebSocket /ws — { type:"plugin-signal", pluginId:"hello", channel:"todos-changed", payload } (signal back)
bb hello add "Ship it"
run() executes on the server, not in the CLI
2↓ CLI → server → POST /api/v1/plugins/hello/cli
POST /api/v1/plugins/hello/cli
{ argv, cwd?, threadId? }
↓ server.ts — jiti, same process
the agent in the thread
skills/example-todos/SKILL.md is injected into the thread
3↓ the same command → the same bb hello command
the same bb hello command
the agent never reaches the store directly
↓ server.ts — jiti, same process
bb.storage.kv → plugin_kv in bb.db
key "todos", JSON ≤ 256 KB
beside it: <dataDir>/plugins/hello/
WebSocket /ws — { type:"plugin-signal", pluginId:"hello", channel:"todos-changed", payload }
ephemeral: nothing is stored and nothing is replayed; the client does the filtering, so this is not a confidentiality boundary
↓ every page refetches → app.tsx · navPanel “Example todos” (signal back)
Все три пути заканчиваются в одном server.ts, и ни один из них не обращается к хранилищу напрямую. bb plugin logs hello -f показывает логи плагина, а bb plugin list показывает его статус.
6. Измените плагин
Заголовок раздела «6. Измените плагин»Добавьте команду count. В server.ts добавьте запись в список commands и case в switch внутри run:
commands: [ /* … */ { name: "count", summary: "Count todos", usage: "bb hello count" } ],
case "count": { const todos = await listTodos(); return { exitCode: 0, stdout: String(todos.length) };}Сохраните файл. bb plugin dev выведет reloaded hello, а bb hello count напечатает число. Теперь bb plugin logs hello показывает loaded от нового экземпляра перед disposed от старого: это порядок перезагрузки из Как устроен плагин.
bb считывает список commands, не запуская ваш код, и добавляет bb hello count в скилл, который генерирует для команд плагина. Два текста вам нужно обновить вручную: строку usage в server.ts, которую выводит bb hello --help, и таблицу команд в skills/example-todos/SKILL.md — скилле, который агент загружает для этого плагина.
Если что-то не работает
Заголовок раздела «Если что-то не работает»| Симптом | Причина | Решение |
|---|---|---|
@get-bb/plugin-sdk не резолвится | типы SDK не установлены | запустите npm install --include=dev; внутри монорепозитория bb выполните pnpm exec turbo run build:types --filter=@get-bb/plugin-sdk |
| порталы, тосты или фокус ведут себя странно | пакет с заглушкой (shimmed), такой как React или radix, находится в dependencies, поэтому в бандл попала вторая копия | перенесите каждый пакет из списка RUNTIME_SLOT_BY_SPECIFIER в devDependencies; zod остаётся в dependencies |
плагин остаётся в статусе needs-configuration | плагин вызвал bb.status.needsConfiguration(...), либо сервис завершился с NeedsConfigurationError | исправьте конфигурацию, затем запустите bb plugin reload hello |
| версия SDK старше, чем запущенный bb | шаблон плагина закрепил версию SDK того bb, который его создал | запустите bb plugin types; bb plugin types --check делает ту же проверку в CI без записи |
Что дальше
Заголовок раздела «Что дальше»- Чтобы решить, где ваша идея появится в окне или в агенте, прочитайте Выбор поверхности.
- Чтобы сохранить секрет, объявите настройку с
secret: true; её значение никогда не попадёт в клиентскую точку входа. Подробности — в карточкеbb.settings. - Чтобы отрендерить компонент внутри ответа агента, посмотрите слот
messageDirective. - Чтобы задать пользователю вопрос из CLI или инструмента, изучите
bb.ui, слотpendingInteractionи паттерн propose-and-confirm в Модели доверия.