Skip to content

Distribution

This page shows how to package Bub as the runtime kernel of your own product. The case study throughout is visual-base — a distribution that pins bub, ships its own visual-base CLI, and bundles two plugins (bub_kimi and bub_eye) plus a skills package.

  • A plugin or two ready to publish (see Plugins).
  • A pyproject.toml-based build backend (hatchling, uv_build, or pdm-backend).

A distribution is, first, a Python package whose dependencies include Bub. Pin a tight range so a new minor release of Bub does not silently change behavior for your users:

[project]
name = "visual-base"
version = "0.2.0"
dependencies = [
    "bub>=0.3.6,<0.4",
    "loguru>=0.7",
]

Bub follows the stability promise — kernel hardened, plugins loose. The >=0.3.6,<0.4 range opts into patch-level fixes within the 0.3 minor line.

The cleanest way to brand a Bub-based product is a thin CLI that defers to Bub’s own Typer app. Visual-base’s cli.py is the reference pattern:

# src/visual_base/cli.py
from __future__ import annotations

import os
import sys

from visual_base.settings import VisualBaseSettings


def _has_explicit_workspace_flag(argv: list[str]) -> bool:
    return any(
        arg == "--workspace" or arg == "-w" or arg.startswith("--workspace=")
        for arg in argv
    )


def main() -> None:
    if not _has_explicit_workspace_flag(sys.argv[1:]):
        workspace = VisualBaseSettings().resolve_workspace()
        workspace.mkdir(parents=True, exist_ok=True)
        os.chdir(workspace)

    from bub.__main__ import app

    app()

Two points worth calling out:

  • The product resolves a sticky workspace before importing Bub, then chdirs into it. BubFramework snapshots Path.cwd() as its workspace at construction time, so the chdir must happen first.
  • After that, the product hands off to bub.__main__.app — the same Typer app the bare bub command uses. Every plugin contributing register_cli_commands lights up automatically.

Wire the CLI to a script entry point:

[project.scripts]
visual-base = "visual_base.cli:main"

End users now run visual-base chat instead of bub chat, but get the full pipeline.

3. Ship multiple plugins from one distribution

Section titled “3. Ship multiple plugins from one distribution”

Nothing forces a one-plugin-per-package layout. A distribution can host several plugin modules under one wheel and register each one through its own entry-point key. From visual-base’s pyproject.toml:

[project.entry-points.bub]
kimi = "bub_kimi.plugin"
eye = "bub_eye.plugin:EyeImpl"

[tool.hatch.build.targets.wheel]
packages = ["src/visual_base", "src/bub_kimi", "src/bub_eye", "src/skills"]

bub_kimi is a module-shaped plugin (a run_model hook that delegates to the Kimi CLI). bub_eye is a callable-class plugin that holds a framework reference (a background channel that records the screen). One uv pip install visual-base registers both with Bub.

Listing src/skills in [tool.hatch.build.targets.wheel] packages puts the skills directory on Bub’s builtin discovery root. See Skills → Ship skills with your package for the full mechanics.

If your product hosts Bub inside its own process — an HTTP API, a worker, a notebook — skip the CLI and drive BubFramework directly. An envelope is the dict (or message object) that carries one inbound message through the pipeline.

import asyncio

from bub import BubFramework


async def run_one_turn() -> None:
    framework = BubFramework()
    framework.load_hooks()
    inbound = {
        "session_id": "embedded:demo",
        "channel": "embedded",
        "chat_id": "demo",
        "content": "hello from a host process",
    }
    async with framework.running():
        result = await framework.process_inbound(inbound)
    for outbound in result.outbounds:
        print(outbound["content"])


asyncio.run(run_one_turn())

framework.running() is the async context that resolves provide_tape_store and any other lifecycle-bound hooks. Open it once around the lifetime of your service, not per turn, when running in long-lived processes.

  • Plugins — package layout for the plugins you bundle
  • Skills — ship skills inside the distribution wheel
  • Deploy — run your distribution in production
  • Settings reference — environment variables consumed by the kernel