CLI
The bub command is provided by the application layer and requires the app optional dependency (install). This page lists only built-in commands; plugins can add commands through register_cli_commands, so consult bub --help and the plugin documentation for the full set.
Global options
Section titled “Global options”bub [OPTIONS] COMMAND [ARGS]...| Option | Type | Default | Description |
|---|---|---|---|
--workspace, -w |
path | current working directory | Sets BubFramework.workspace |
--version, -V |
flag | — | Show the installed Bub version and exit |
--help |
flag | — | Show help |
--workspace affects tools’ working directory, skill discovery, and relative-path resolution. It does not change BUB_HOME or the location of the configuration file.
Running bub without a command runs BubFramework.default_command with its defaults. The builtin plugin sets it to chat, so bub and bub -w PATH start the chat REPL. A plugin can choose another command by setting default_command in its register_cli_commands hook; the builtin only fills it when no plugin has.
Before loading the CLI, the command checks for the app optional dependency. If it is missing, the process prints the missing package and the install command, then exits without reaching the Typer help page.
bub run
Section titled “bub run”Runs a complete Turn for one message: resolves the session, builds the prompt, calls the model and tools, saves state, and generates a reply, then prints TurnResult.outbounds to stdout. It does not start a channel manager.
bub run [OPTIONS] MESSAGE| Parameter | Type | Default | Description |
|---|---|---|---|
MESSAGE |
text | required | Message body; executes a command when it starts with the command prefix |
--channel |
text | cli |
Inbound channel name |
--chat-id |
text | local |
Inbound chat ID |
--sender-id |
text | human |
Written to context.sender_id |
--session-id |
text | <channel>:<chat-id> |
Explicitly overrides the session ID |
Output format:
[channel:chat_id]<content>Examples:
bub run "Summarize this repository."bub run --channel web --chat-id demo "Continue the previous topic."bub run --session-id shared:demo ",tape.info"Because it does not start a ChannelManager:
- It does not start channels or background services such as
mcp.lifecycleandacp-server. - It does not automatically send model replies to a chat platform.
- The resource scopes for
provide_tape_store,provide_lifespan, andprovide_steering_inboxstill run. - The model stage uses the non-streaming
HookRuntime.run_model.
When the model returns no text, run prints the inbound content or prompt text as a fallback and logs a run_model stage error; the process itself usually still exits with code 0. To make scripted decisions based on failure, inspect stderr and the actual output rather than relying on the exit code alone.
bub chat
Section titled “bub chat”Starts an interactive REPL. It always enables the cli channel and attaches enabled Lifecycle channels.
bub chat [OPTIONS] [INITIAL_PROMPT]| Parameter | Type | Default | Description |
|---|---|---|---|
INITIAL_PROMPT |
text | — | Submitted immediately after the REPL starts |
--chat-id |
text | local |
CLI chat ID |
--session-id |
text | cli_session |
Session ID |
chat forces streaming output and ignores BUB_STREAM_OUTPUT. The attached Lifecycle lets MCP tools and ACP background services work during the chat.
Without a cli channel, the command prints an error and exits with code 1. This happens when a plugin replaces or removes the CLI channel.
bub chatbub chat "What changed in this repository?"bub chat --session-id shared:demoInside the REPL, comma-prefixed commands are controlled by BUB_COMMAND_PREFIX, which defaults to ,. Type ,help to see the current commands.
bub gateway
Section titled “bub gateway”Starts the channel manager and keeps enabled channels running.
bub gateway [OPTIONS]| Option | Type | Default | Description |
|---|---|---|---|
--enable-channel |
text, repeatable | empty; reads BUB_ENABLED_CHANNELS |
Include or exclude channels |
--install |
flag | off | Install and start a background service for the current user |
--uninstall |
flag | off | Stop and uninstall the background service |
--install and --uninstall are mutually exclusive; passing both exits with an argument error.
Channel selection
Section titled “Channel selection”BUB_ENABLED_CHANNELS is comma-separated; --enable-channel may be repeated, and command-line values take precedence.
enabled_channels |
Result |
|---|---|
all |
Every enabled channel that is not an Interface, minus !name exclusions |
| Explicit ordinary channels | The listed channels that are enabled and not excluded |
| Explicit ordinary channels + Lifecycle | Select the ordinary channels first, then attach every enabled Lifecycle |
| Only Lifecycle | Start only those Lifecycle channels |
Only !name |
No inclusions, so startup fails |
Interfaceis a user-facing surface that must be selected explicitly, such ascli.Lifecycleis a background service, such asmcp.lifecycleoragent-plugins.mcp.- When ordinary channels are selected explicitly, Lifecycle channels are attached automatically by default; use
!nameto exclude one. - If no channel can be enabled, it prints
No channels are enabled...and exits with code1.
bub gatewaybub gateway --enable-channel telegrambub gateway --enable-channel telegram --enable-channel discordbub gateway --enable-channel telegram --enable-channel '!mcp.lifecycle'Quote exclusions so the shell does not treat ! as history expansion.
Background service
Section titled “Background service”| Platform | Installation method | Location or behavior |
|---|---|---|
| Linux | systemd user unit | ${XDG_CONFIG_HOME:-~/.config}/systemd/user/bub-gateway.service |
| Windows | Scheduled task | Task named Bub Gateway, started when the current user logs in |
| macOS | --install unsupported |
Deploy with launchd, tmux, or a container |
The Linux unit uses Restart=on-failure, RestartSec=5s, and WantedBy=default.target. Installation runs daemon-reload, enable, and restart.
- The service captures the current Python interpreter, workspace, and explicit
--enable-channelarguments. - After changing the virtual environment, moving the workspace, or changing channel arguments, run
bub gateway --installagain. - To keep it running after logout on Linux, run
loginctl enable-linger "$USER". - Reinstalling replaces the existing unit or task and restarts it.
--uninstallcounts as successful even when the service does not exist.
macOS has no built-in service installation path; Deployment describes a container approach.
bub onboard
Section titled “bub onboard”Interactively generates configuration: it collects model and plugin options and writes them to BubFramework.config_file, which defaults to ~/.bub/config.yml. It does not start channels.
bub onboard [OPTIONS] [PLUGIN_NAME]| Parameter | Type | Default | Description |
|---|---|---|---|
PLUGIN_NAME |
text | none | Run only that plugin’s onboard_config |
Without a plugin name, it runs the built-in model configuration first, then the configuration of every other registered plugin. With a plugin name, it runs only the selected implementation and skips the gateway service installation prompt.
| Exit code | Meaning |
|---|---|
0 |
Saved, or the user declined gateway installation |
1 |
Configuration validation, write, plugin resolution, or gateway installation failed |
Common write behavior:
| Choice or action | Result |
|---|---|
| OpenAI-compatible service or local endpoint | Writes openai:<model>, api_base, and sets client_args.api_format: chat |
| Official OpenAI API selected but the address is not the official one | Resets the address to https://api.openai.com/v1 |
| Base URL left blank | Keeps the provider’s default address and does not write an explicit empty value |
| Base URL entered explicitly | Written even when it equals the default |
| API key left blank | Keeps the key for the current address, or continues using an environment variable |
| Base URL changed | Clears any key entered earlier |
| Codex selected | Skips model discovery and asks for a model ID manually |
The connection check calls only the model list endpoint, not a generation request, with a 10-second timeout. It does not verify that the model supports tool calls.
An onboard_config implementation receives the current configuration plus modifications from earlier hooks and returns a dictionary that is merged back; returning a non-dictionary raises TypeError. After all implementations finish, the result passes through configure.validate before being written to disk.
bub install
Section titled “bub install”Installs plugins into the uv project managed by Bub; with no arguments, it syncs that project. It does not start channels.
bub install [OPTIONS] [SPECS]...| Parameter | Type | Default | Description |
|---|---|---|---|
SPECS |
positional | empty | Packages or Git sources to install |
--project |
path | $BUB_HOME/bub-project or ~/.bub/bub-project |
Plugin project directory; also available as BUB_PROJECT |
Package spec resolution:
| Form | Resolves to |
|---|---|
package |
PyPI package name |
package@ref |
The packages/package subdirectory of bub-contrib, pinned to ref |
owner/repo |
https://github.com/owner/repo.git |
https://github.com/owner/repo.git@ref |
A prefixed Git URL that preserves the ref |
git@... or https://... |
Prefixed with git+ |
To pin any GitHub repository, use the full URL:
bub install https://github.com/owner/repo.git@v1.2.0Do not write owner/repo@v1.2.0: the current parser accepts only the owner/repo shorthand without a ref. Bub’s own repository name is also not the same as its package name; when installing a plugin, use the spec given in the plugin’s README.
Examples:
bub install bub-mcp@mainbub install bub-discordbub install https://github.com/owner/repo.git@v1.2.0bub installThe first invocation creates bub-project:
uv init --bare --name bub-project --appuv add --active --no-sync --extra app <source of the current Bub installation>The current Bub source may be an editable path, a local path, a VCS URL, or a PyPI name; source installs and PyPI installs therefore produce different project declarations.
Subsequent uv calls:
| Command | uv invocation |
|---|---|
bub install (no specs) |
uv sync --active --inexact |
bub install <specs> |
uv add --active <requirements> |
bub uninstall <packages> |
uv remove --active <packages> |
bub update |
uv sync --active --upgrade --inexact |
bub update <packages> |
uv sync --active --inexact --upgrade-package ... |
Prerequisites:
uvis onPATH, in the current Python scripts directory, or in~/.local/bin;- Bub is running inside a virtual environment (
sys.prefix != sys.base_prefix); - An explicitly passed
--projectdirectory must exist.
If these are not met, the command exits with code 1. When uv fails, its exit code is returned.
bub uninstall
Section titled “bub uninstall”Uninstalls one or more plugins from the managed project. It does not start channels. Names must match the project declarations.
bub uninstall [OPTIONS] PACKAGES...bub uninstall bub-discordIt modifies only bub-project, not the system Python or other packages in the environment where Bub itself runs. install, uninstall, and update all accept --project, and BUB_PROJECT can specify the same directory.
bub update
Section titled “bub update”Upgrades plugins in the managed project; without package names it upgrades every dependency, and with names it upgrades only those packages. It does not start channels.
bub update [OPTIONS] [PACKAGES]...bub updatebub update bub-mcpUpgrading does not automatically migrate plugin configuration or Tape data.
After upgrading across versions, restart bub chat or bub gateway and check whether bub hooks still reports entry-point loading failures.
bub login codex
Section titled “bub login codex”Configures ChatGPT plan credentials through the Codex CLI. It does not start channels.
bub login codex [OPTIONS]| Option | Type | Default | Description |
|---|---|---|---|
--executable |
text | codex |
Codex CLI executable |
--device-auth |
flag | off | Use the device authorization flow |
--codex-home |
path | $CODEX_HOME or ~/.codex |
Credential directory |
On success it prints:
login: okaccount_id: <id>Use BUB_MODEL=codex:<model> for ChatGPT plan access.Matching configuration:
export BUB_MODEL=codex:<model>Do not set BUB_API_KEY at the same time. On failure it prints login: failed: ... and exits with code 1. Credentials are usually written to $CODEX_HOME/auth.json or ~/.codex/auth.json.
bub plugins
Section titled “bub plugins”Prints each discovered plugin with the package and version that provide it.
bub plugins [OPTIONS] [NAMES]...| Option | Type | Default | Description |
|---|---|---|---|
NAMES |
text | all plugins | Plugin names registered in the bub entry-point group |
--version-only |
flag | off | Print only each plugin’s version, one per line |
Output is one line per plugin: name distribution version. The builtin hooks are listed as builtin, provided by bub. Plugins that failed to load or initialize are listed with (failed: <error>). An unknown name exits with code 1. With --version-only, each line holds only the version (- when the plugin has no package metadata), for example bub plugins my-plugin --version-only.
bub hooks
Section titled “bub hooks”Prints the hook implementation map. This is a hidden command that does not appear in bub --help and does not start channels.
bub hooksEach line has this format:
<hook>: <implementation>, <implementation>Implementations are printed in pluggy registration order, with hook names sorted alphabetically. When there are no implementations it prints (no hook implementations).
This order is not execution order. Bub reverses pluggy’s internal list and then applies tryfirst / trylast; see Priority and dispatch for diagnosing the actual order.
Exit codes
Section titled “Exit codes”| Scenario | Exit code |
|---|---|
| Command succeeds | 0 |
| Typer argument error | 2 |
app optional dependency missing |
1; the process exits before Typer |
| No channels can be enabled, plugin failure, configuration failure | 1 |
uv failure in bub install / uninstall / update |
uv’s exit code |
Model errors in bub run may only be logged and return fallback text, so do not rely on the exit code alone to determine whether the model call succeeded.