Skip to content

Package a plugin

Use a plugin to give the Bub app behavior you want to reuse or share. A Python package declares an entry point in the bub group; Bub imports that target and registers its hook implementations. You can also reuse an existing plugin in your own service while keeping your host’s entry point.

For a complete first example, follow Write your first plugin. This guide covers the choices you make when packaging a plugin of your own.

Portable Agent Plugins use a different format: plugin.json, skills, and MCP servers. The Agent Plugins integration loads those packages through a Python plugin.

A plugin needs the application dependencies described in Install Bub. Declare bub[app]>=0.6.0 in the package dependencies for this guide. See Upgrade to 0.6.0 when updating an existing plugin.

[project.entry-points."bub"]
myplugin = "bub_myplugin.plugin"

The key myplugin identifies the plugin in diagnostics. The value names the Python object to import.

Choose one target form:

Target Registration Use it when
bub_myplugin.plugin Register the module Hooks are module-level functions
bub_myplugin.plugin:plugin Register the instance Hooks share state on an object
bub_myplugin.plugin:MyPlugin Call the class or factory with BubFramework, then register its result Hooks need the framework

For the module form, define hooks at module level:

from bub import hookimpl


@hookimpl
def build_prompt(message, session_id, state):
    return "Describe this workspace."

For the instance form, create the object in the target module:

from bub import hookimpl


class MyPlugin:
    @hookimpl
    def build_prompt(self, message, session_id, state):
        return "Describe this workspace."


plugin = MyPlugin()

Every callable target receives the framework, including classes. Accept it in the constructor or factory:

from bub import BubFramework, hookimpl


class MyPlugin:
    def __init__(self, framework: BubFramework) -> None:
        self.framework = framework

    @hookimpl
    def build_prompt(self, message, session_id, state):
        return f"Describe the workspace at {self.framework.workspace}."

These examples replace the prompt. Choose the hook for your actual task from Change application behavior.

If the package defines @tool functions, import their module from the entry-point module before an agent is created:

from . import tools  # noqa: F401

The application takes a snapshot of registered tools when it creates a BubAgent. See Add application tools for registration, deferred loading, and code mode.

Install into the application’s environment

Section titled “Install into the application’s environment”

An editable install lets you change Python source without rebuilding the package. Use the same environment that runs Bub.

For a uv project, run from the plugin directory:

uv sync
uv run bub hooks

To install into an existing activated application environment, use uv pip install -e . and run bub hooks there.

bub hooks confirms registration. Check priority and return values by running the task your hook should handle. Bub registers builtin hooks before entry-point plugins. Hooks at equal priority generally run in reverse registration order; tryfirst and trylast affect that order. The dispatch rules explain which implementation runs first.

If the plugin is missing, check its entry-point group, the active environment, and startup logs for an import failure. Changes to package metadata require reinstalling the package.

Build and install a wheel into a separate environment before publishing it. Confirm that the entry point loads and that a representative task uses your hook. If you bundle skills or several plugins, follow Package a Bub application.