Use cases
Start from what you want to do. Each row links to the guide page that builds it. The list is generated from the guides' "Use this when" sections.
| I want to… | Guide |
|---|---|
| Start a plugin by hand. You want to understand each required file before you use a template. | Getting started: a plugin from an empty directory |
| Try a local plugin. You want bb to build, install, watch, and reload a source directory. | Getting started: a plugin from an empty directory |
| Add a server service. You want other plugins to use one typed, versioned capability. | Getting started: a plugin from an empty directory |
| Add a first app surface. You want to add UI through a contract that its owner declared. | Getting started: a plugin from an empty directory |
| Share behavior between plugins. Define one typed contract, provide it in one plugin, and use it from another plugin. | Services and service edges |
| Follow the user's provider choice. A replaceable service resolves through the global winner picker. | Services and service edges |
| Control startup after a dependency change. Choose a required, optional, or watched edge for each service dependency. | Services and service edges |
Use a bb domain service. Consume bb.threads, bb.workspace, bb.files, bb.vcs, or bb.providers through typed contracts. |
Services and service edges |
Give an agent a typed action. Claim one bb.agents.tool key and add its behavior with api.tools.add(). |
Agent tools, tool-call policy, and AI services |
| Replace a tool implementation. Claim the same replaceable key and let the user select its winner. | Agent tools, tool-call policy, and AI services |
Apply policy to every tool call. Add a member to the ordered bb.agents.toolCalls chain. |
Agent tools, tool-call policy, and AI services |
Offer helper AI operations. Claim one bb.agents.ai key for structured inference, voice transcription, or both. |
Agent tools, tool-call policy, and AI services |
| Add an action to menus, keybindings, and the palette. Register one app command, and all command sources use it. | Commands, keybindings, and interception |
| Give an action a default shortcut. Add keybindings to the command record and let user overrides stay with its ID. | Commands, keybindings, and interception |
| Stop or change a command call. Add a member to the shared command interception chain. | Commands, keybindings, and interception |
Add a top-level bb verb. Claim one non-core key through bb.commands.cli. |
Commands, keybindings, and interception |
| Replace the command palette. Claim its surface and keep the headless command service. | Commands, keybindings, and interception |
| Keep a small private value. Use a key-value store for a cursor, a count, or cached JSON. | Data and settings |
| Own tables and migrations. Use a private SQLite database for records and queries. | Data and settings |
Use an API token. Read a declared secret through bb.secrets, and never store or log its value. |
Data and settings |
Add user settings. Declare a schema and let bb.settings make a form. |
Data and settings |
Add a custom settings page. Claim the bb.settings.pages list surface. |
Data and settings |
Update a surface after a thread change. Subscribe through bb.threads, then load the new record. |
Live updates |
| Keep a plugin active without a service provider. Use a watched edge and show an unavailable state. | Live updates |
Send short progress events to app sessions. Declare an event and publish it through bb.realtime. |
Live updates |
React to a durable thread lifecycle event. Use threads.events.subscribe() for delivery and events.list() for history. |
Live updates |
| Handle a service winner change without a restart. Replace the old subscription when the watched provider changes. | Live updates |
Replace one UI region. Claim a single surface such as bb.layout.sidebar. |
Surfaces and kinds |
Add one item beside other items. Claim a list surface such as bb.thread-ui.sidePanels. |
Surfaces and kinds |
Handle one data type. Claim one key of a keyed surface such as bb.thread-ui.timeline.item. |
Surfaces and kinds |
Wrap one operation. Claim a chain surface such as bb.thread-ui.composer.send. |
Surfaces and kinds |
| Let the user select an implementation. Claim a replaceable surface and let the global picker control the winner. | Surfaces and kinds |
Add an app page. Claim bb.layout.panels and give the panel navigation data. |
Pages, panels, and commands |
| Open content in a shell dock. Let the panel controller select the right, bottom, or sidebar dock. | Pages, panels, and commands |
Add a command to the palette. Claim one key in bb.commands.command and call api.commands.add(). |
Pages, panels, and commands |
Add a small global action. Add an item to bb.layout.statusbar. |
Pages, panels, and commands |
Show modal content. Add a declaration to bb.layout.modals and let the shell control focus. |
Pages, panels, and commands |
Replace a large shell region. Claim bb.layout.main or bb.layout.sidebar and use the typed Original component. |
Pages, panels, and commands |
Render one timeline item type. Claim the keyed bb.thread-ui.timeline.item surface. |
Thread timeline, composer, and side panels |
Replace or wrap the composer. Claim the single bb.thread-ui.composer surface. |
Thread timeline, composer, and side panels |
Add a composer control or card. Claim the list bb.thread-ui.composer.actions surface. |
Thread timeline, composer, and side panels |
Check or change a send. Claim the chain bb.thread-ui.composer.send surface. |
Thread timeline, composer, and side panels |
Add a panel beside a thread. Claim the view-declared bb.thread-ui.sidePanels surface. |
Thread timeline, composer, and side panels |
Keep a draft through a UI change. Store it through the bb.threads service. |
Thread timeline, composer, and side panels |
| Run a machine tool. Put Git, a compiler, or another local process in a host role. | The host tier |
| Use the machine that owns a repository. Select a host target before you call the role. | The host tier |
| Watch machine files. Emit a typed signal when a repository changes. | The host tier |
| Keep a public service independent of placement. Let consumers use one server service for local and remote work. | The host tier |
| Set process limits. Declare memory, request, response, start, stop, and idle limits for each role. | The host tier |
| Create an isolated worktree. Give each environment its own branch and root. | Environment providers |
| Create a personal workspace. Put a managed directory below the provider data directory. | Environment providers |
| Use an existing directory. Return an unmanaged workspace after strict path checks. | Environment providers |
Run on a remote host. Let bb.workspace select the host and route each host call. |
Environment providers |
| Replace one provider implementation. Claim the same provider key and join the global winner picker. | Environment providers |
Add a coding agent to bb. Claim one bb.providers.provider key for the agent. |
Provider plugins |
Connect a CLI or SDK agent. Translate its native protocol through the bb.providers.bridge host role. |
Provider plugins |
| Support models, health, and usage. Use the provider contract and the shared host services. | Provider plugins |
| Report a rate limit to the UI. Send normalized limit data and a provider error that settles the turn. | Provider plugins |
Request approval before a command. Send an interaction/request and wait for the typed result. |
Provider plugins |
| Replace an installed provider implementation. Claim the same provider key and use the global winner picker. | Provider plugins |
| Keep provider processes reliable. Use the shared supervisor instead of local process control. | Provider plugins |
Check a contract before a build. assertContract finds invalid declarations and schema problems. |
Test plugin factories and contracts |
Run a server factory with controlled ports. createServerHarness uses the real loader graph and commit rules. |
Test plugin factories and contracts |
| Replace one service in a test. A named service double gives typed calls without a broad runtime fake. | Test plugin factories and contracts |
Mount one declared surface. renderSurface supplies owner props, service doubles, routing, settings, and winner changes. |
Test plugin factories and contracts |
| Check cleanup and reload behavior. The harnesses stop generations and make old service handles stale. | Test plugin factories and contracts |
Call a host role without a daemon. createHostHarness checks schemas, signals, cancellation, and retained leases. |
Test plugin factories and contracts |
| Change a first-party screen. Fork the plugin that wins its surface contract. | Composition, forks, and distribution |
| Replace a backend service. Keep the parent contract ID so current consumers follow the winner. | Composition, forks, and distribution |
| Test a change safely. Install a fork and flip one winner without removal of the parent. | Composition, forks, and distribution |
| Publish a plugin. Distribute a verified artifact through npm, Git, or a marketplace. | Composition, forks, and distribution |
| Recover from a bad override. Start safe mode and restore a default winner. | Composition, forks, and distribution |
| Move a 1.x plugin. Replace its package manifest, broad SDK access, and app slots. | Migrating from the 1.x API |
| Plan a gradual move. Keep 1.x plugins active while other plugins use 2.0. | Migrating from the 1.x API |
| Find a new API home. Use the coverage matrix for each old member. | Migrating from the 1.x API |
| Choose a first plugin. Use the rebuild stories to compare real plugin shapes. | Migrating from the 1.x API |
| Review a port. Check the manifest, contract edges, surface claims, and tier factories. | Migrating from the 1.x API |
Publish a typed service or surface. Put its public shape in src/contracts.ts and declare it in bb.plugin.jsonc. |
Typed contracts and contract.json |
| Use one contract from two package copies. The kernel matches the contract ID and version, not a JavaScript object. | Typed contracts and contract.json |
Review the exact public shape. Read the generated dist/contract.json file. |
Typed contracts and contract.json |
| Change a stable contract. Use semantic version rules and keep a deprecation period. | Typed contracts and contract.json |
Try an unsettled contract. Mark it as experimental and expect source changes after any release. |
Typed contracts and contract.json |
Find an install failure. Compare claims and service edges with the declarations in contract.json. |
Typed contracts and contract.json |
Add content to another plugin's UI. Claim its typed list surface. |
Cross-plugin claims |
| Keep domain data separate from presentation. Put data in a headless plugin and claims in a UI plugin. | Cross-plugin claims |
| Follow the active surface graph. Let a claim become inactive when the declaring winner omits its child surface. | Cross-plugin claims |
| Compose a replaceable surface. Render the current global winner instead of importing one implementation. | Cross-plugin claims |
| Handle an owner change. Keep independent claims active when only one graph path changes. | Cross-plugin claims |
| Share a concrete React component. Export a stable component that another plugin can place in its own UI. | Module exports and the import map |
| Share a pure app helper. Export code that needs React or another app-only module. | Module exports and the import map |
| Publish exact code. Use an export when a winner change must not select different code. | Module exports and the import map |
| Keep one React instance. Let the host import map supply React to all app modules. | Module exports and the import map |
| Understand app reloads. Treat each changed ESM bundle as a new generation. | Module exports and the import map |
By guide
Getting started: a plugin from an empty directory
- Start a plugin by hand. You want to understand each required file before you use a template.
- Try a local plugin. You want bb to build, install, watch, and reload a source directory.
- Add a server service. You want other plugins to use one typed, versioned capability.
- Add a first app surface. You want to add UI through a contract that its owner declared.
Services and service edges
- Share behavior between plugins. Define one typed contract, provide it in one plugin, and use it from another plugin.
- Follow the user's provider choice. A replaceable service resolves through the global winner picker.
- Control startup after a dependency change. Choose a required, optional, or watched edge for each service dependency.
- Use a bb domain service. Consume
bb.threads,bb.workspace,bb.files,bb.vcs, orbb.providersthrough typed contracts.
Agent tools, tool-call policy, and AI services
- Give an agent a typed action. Claim one
bb.agents.toolkey and add its behavior withapi.tools.add(). - Replace a tool implementation. Claim the same replaceable key and let the user select its winner.
- Apply policy to every tool call. Add a member to the ordered
bb.agents.toolCallschain. - Offer helper AI operations. Claim one
bb.agents.aikey for structured inference, voice transcription, or both.
Commands, keybindings, and interception
- Add an action to menus, keybindings, and the palette. Register one app command, and all command sources use it.
- Give an action a default shortcut. Add keybindings to the command record and let user overrides stay with its ID.
- Stop or change a command call. Add a member to the shared command interception chain.
- Add a top-level
bbverb. Claim one non-core key throughbb.commands.cli. - Replace the command palette. Claim its surface and keep the headless command service.
Data and settings
- Keep a small private value. Use a key-value store for a cursor, a count, or cached JSON.
- Own tables and migrations. Use a private SQLite database for records and queries.
- Use an API token. Read a declared secret through
bb.secrets, and never store or log its value. - Add user settings. Declare a schema and let
bb.settingsmake a form. - Add a custom settings page. Claim the
bb.settings.pageslist surface.
Live updates
- Update a surface after a thread change. Subscribe through
bb.threads, then load the new record. - Keep a plugin active without a service provider. Use a watched edge and show an unavailable state.
- Send short progress events to app sessions. Declare an event and publish it through
bb.realtime. - React to a durable thread lifecycle event. Use
threads.events.subscribe()for delivery andevents.list()for history. - Handle a service winner change without a restart. Replace the old subscription when the watched provider changes.
Surfaces and kinds
- Replace one UI region. Claim a
singlesurface such asbb.layout.sidebar. - Add one item beside other items. Claim a
listsurface such asbb.thread-ui.sidePanels. - Handle one data type. Claim one key of a
keyedsurface such asbb.thread-ui.timeline.item. - Wrap one operation. Claim a
chainsurface such asbb.thread-ui.composer.send. - Let the user select an implementation. Claim a replaceable surface and let the global picker control the winner.
Pages, panels, and commands
- Add an app page. Claim
bb.layout.panelsand give the panel navigation data. - Open content in a shell dock. Let the panel controller select the right, bottom, or sidebar dock.
- Add a command to the palette. Claim one key in
bb.commands.commandand callapi.commands.add(). - Add a small global action. Add an item to
bb.layout.statusbar. - Show modal content. Add a declaration to
bb.layout.modalsand let the shell control focus. - Replace a large shell region. Claim
bb.layout.mainorbb.layout.sidebarand use the typedOriginalcomponent.
Thread timeline, composer, and side panels
- Render one timeline item type. Claim the keyed
bb.thread-ui.timeline.itemsurface. - Replace or wrap the composer. Claim the single
bb.thread-ui.composersurface. - Add a composer control or card. Claim the list
bb.thread-ui.composer.actionssurface. - Check or change a send. Claim the chain
bb.thread-ui.composer.sendsurface. - Add a panel beside a thread. Claim the view-declared
bb.thread-ui.sidePanelssurface. - Keep a draft through a UI change. Store it through the
bb.threadsservice.
The host tier
- Run a machine tool. Put Git, a compiler, or another local process in a host role.
- Use the machine that owns a repository. Select a host target before you call the role.
- Watch machine files. Emit a typed signal when a repository changes.
- Keep a public service independent of placement. Let consumers use one server service for local and remote work.
- Set process limits. Declare memory, request, response, start, stop, and idle limits for each role.
Environment providers
- Create an isolated worktree. Give each environment its own branch and root.
- Create a personal workspace. Put a managed directory below the provider data directory.
- Use an existing directory. Return an unmanaged workspace after strict path checks.
- Run on a remote host. Let
bb.workspaceselect the host and route each host call. - Replace one provider implementation. Claim the same provider key and join the global winner picker.
Provider plugins
- Add a coding agent to bb. Claim one
bb.providers.providerkey for the agent. - Connect a CLI or SDK agent. Translate its native protocol through the
bb.providers.bridgehost role. - Support models, health, and usage. Use the provider contract and the shared host services.
- Report a rate limit to the UI. Send normalized limit data and a provider error that settles the turn.
- Request approval before a command. Send an
interaction/requestand wait for the typed result. - Replace an installed provider implementation. Claim the same provider key and use the global winner picker.
- Keep provider processes reliable. Use the shared supervisor instead of local process control.
Test plugin factories and contracts
- Check a contract before a build.
assertContractfinds invalid declarations and schema problems. - Run a server factory with controlled ports.
createServerHarnessuses the real loader graph and commit rules. - Replace one service in a test. A named service double gives typed calls without a broad runtime fake.
- Mount one declared surface.
renderSurfacesupplies owner props, service doubles, routing, settings, and winner changes. - Check cleanup and reload behavior. The harnesses stop generations and make old service handles stale.
- Call a host role without a daemon.
createHostHarnesschecks schemas, signals, cancellation, and retained leases.
Composition, forks, and distribution
- Change a first-party screen. Fork the plugin that wins its surface contract.
- Replace a backend service. Keep the parent contract ID so current consumers follow the winner.
- Test a change safely. Install a fork and flip one winner without removal of the parent.
- Publish a plugin. Distribute a verified artifact through npm, Git, or a marketplace.
- Recover from a bad override. Start safe mode and restore a default winner.
Migrating from the 1.x API
- Move a 1.x plugin. Replace its package manifest, broad SDK access, and app slots.
- Plan a gradual move. Keep 1.x plugins active while other plugins use 2.0.
- Find a new API home. Use the coverage matrix for each old member.
- Choose a first plugin. Use the rebuild stories to compare real plugin shapes.
- Review a port. Check the manifest, contract edges, surface claims, and tier factories.
Typed contracts and contract.json
- Publish a typed service or surface. Put its public shape in
src/contracts.tsand declare it inbb.plugin.jsonc. - Use one contract from two package copies. The kernel matches the contract ID and version, not a JavaScript object.
- Review the exact public shape. Read the generated
dist/contract.jsonfile. - Change a stable contract. Use semantic version rules and keep a deprecation period.
- Try an unsettled contract. Mark it as
experimentaland expect source changes after any release. - Find an install failure. Compare claims and service edges with the declarations in
contract.json.
Cross-plugin claims
- Add content to another plugin's UI. Claim its typed
listsurface. - Keep domain data separate from presentation. Put data in a headless plugin and claims in a UI plugin.
- Follow the active surface graph. Let a claim become inactive when the declaring winner omits its child surface.
- Compose a replaceable surface. Render the current global winner instead of importing one implementation.
- Handle an owner change. Keep independent claims active when only one graph path changes.
Module exports and the import map
- Share a concrete React component. Export a stable component that another plugin can place in its own UI.
- Share a pure app helper. Export code that needs React or another app-only module.
- Publish exact code. Use an export when a winner change must not select different code.
- Keep one React instance. Let the host import map supply React to all app modules.
- Understand app reloads. Treat each changed ESM bundle as a new generation.