Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
25ea89b
feat: rebuild bub as batteries-included hook-first framework
PsiACE Feb 13, 2026
95bdda1
feat: migrate builtin capabilities into runtime and cli skills
PsiACE Feb 14, 2026
36246fe
feat: bub the framework v2
PsiACE Feb 22, 2026
ba2284f
refactor: not plugin, just skill adapter
PsiACE Feb 22, 2026
378b030
refactor: redesign the interfaces of bub framework
frostming Feb 25, 2026
1be54ad
refactor: remove redundant timeout check in _run_tools_once method
frostming Feb 26, 2026
2557c93
Refactor code structure and remove redundant sections for improved re…
frostming Mar 3, 2026
59889d9
refactor: remove plugin installation logic and enhance tool handling …
frostming Mar 3, 2026
2ea65fb
refactor: add default tape context to engine for improved message han…
frostming Mar 3, 2026
d6d2e54
refactor: enhance output formatting in CLI and improve logging in too…
frostming Mar 3, 2026
8014450
refactor: enhance tape info output with additional details and improv…
frostming Mar 3, 2026
8e486e4
refactor: implement channel architecture with message handling and Te…
frostming Mar 4, 2026
61a125f
feat: add Telegram skill for message handling and editing via Bot API
frostming Mar 4, 2026
a1870a7
feat: add CLI channel for interactive chat sessions and enhance messa…
frostming Mar 4, 2026
c89e00a
fix: add JSON import and enhance logging in TelegramChannel message b…
frostming Mar 4, 2026
8802998
feat: implement ForkTapeStore for enhanced tape management and persis…
frostming Mar 4, 2026
aa5d39b
Add skill-creator and skill-installer components
frostming Mar 4, 2026
37ce5b0
feat: update system prompt to include detailed response instructions …
frostming Mar 4, 2026
0413bec
feat: refactor hook implementation to use lifespan context management…
frostming Mar 4, 2026
f422632
feat: add requirement installation step in entrypoint script
frostming Mar 4, 2026
5797a9d
fix: simplify message template assignment in BufferedMessageHandler
frostming Mar 4, 2026
7940e8d
fix: remove unused import of json from pydantic in tape.py
frostming Mar 4, 2026
d2c55fb
fix: replace import of BaseModel from anthropic with pydantic in tool…
frostming Mar 4, 2026
0396eb8
fix: remove apscheduler dependency from project files
frostming Mar 4, 2026
9f6a8f1
fix: trim whitespace from skill content read operations
frostming Mar 4, 2026
683f302
feat: add GitHub CLI skill documentation for repository, issue, pull …
frostming Mar 4, 2026
0ebaac1
fix: update channel name retrieval in dispatch method to use output_c…
frostming Mar 4, 2026
9893512
feat: add skill_load to the list of built-in tools
frostming Mar 4, 2026
6fd7fda
fix: update docstring for skill_load function to clarify skill loadin…
frostming Mar 4, 2026
a4e21ab
fix: update docstring for skill_load function to clarify skill locati…
frostming Mar 4, 2026
1cd1580
refactor: remove Discord and Telegram skill implementations and relat…
frostming Mar 5, 2026
d43cbe2
refactor: update __all__ to include ChannelMessage and remove unused …
frostming Mar 5, 2026
112b04f
refactor: update documentation for architecture, channels, CLI, deplo…
frostming Mar 5, 2026
92fccc0
refactor: reorganize documentation structure and add new channel and …
frostming Mar 5, 2026
abf9f2b
refactor: update execution policy for multi-line messages and add her…
frostming Mar 5, 2026
1f76067
feat: Introduce Agent class for prompt processing and refactor CLI fr…
frostming Mar 6, 2026
f0d51bd
refactor: change subprocess execution method in bash tool to use crea…
frostming Mar 6, 2026
f628835
refactor: update _builtin_skills_root to return a list of paths from …
frostming Mar 6, 2026
36fb752
refactor: enhance message handling by adding prettify method for cont…
frostming Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,6 @@ cython_debug/

# Reference directory - ignore all reference projects
reference/

# Local legacy backups created during framework migrations
backup/
136 changes: 65 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,103 +1,97 @@
# Bub

[![Release](https://img.shields.io/github/v/release/bubbuild/bub)](https://github.com/bubbuild/bub/releases)
[![Build status](https://img.shields.io/github/actions/workflow/status/bubbuild/bub/main.yml?branch=main)](https://github.com/bubbuild/bub/actions/workflows/main.yml?query=branch%3Amain)
[![Commit activity](https://img.shields.io/github/commit-activity/m/bubbuild/bub)](https://github.com/bubbuild/bub/graphs/commit-activity)
[![License](https://img.shields.io/github/license/bubbuild/bub)](LICENSE)
Bub is a hook-first AI framework built on `pluggy`: the core stays small and orchestrates turns, while builtins and plugins provide behavior.

> Bub it. Build it.
## Current Implementation

Bub is a collaborative agent for shared delivery workflows, evolving into a framework that helps other agents operate with the same collaboration model.
It is not a personal-assistant shell: it is designed for shared environments where work must be inspectable, handoff-friendly, and operationally reliable.

> Documentation: <https://bub.build>

Built on [Republic](https://github.com/bubbuild/republic), Bub treats context as explicit assembly from verifiable interaction history, rather than opaque inherited state.
This aligns with [Socialized Evaluation](https://psiace.me/posts/im-and-socialized-evaluation/): systems are judged by how well teams can inspect, review, and continue work together.

## What Bub Provides

- Multi-operator collaboration in shared delivery environments.
- Explicit command boundaries for predictable execution.
- Verifiable history (`tape`, `anchor`, `handoff`) for audit and continuity.
- Channel-neutral behavior across CLI and message channels.
- Extensible tools and skills with a unified operator-facing workflow.
- CLI bootstrap: `src/bub/__main__.py` (Typer app)
- Turn orchestrator: `src/bub/framework.py`
- Hook contract: `src/bub/hookspecs.py`
- Builtin hooks/runtime: `src/bub/builtin/hook_impl.py` + `src/bub/builtin/engine.py`
- Skill discovery and validation: `src/bub/skills.py`

## Quick Start

```bash
git clone https://github.com/bubbuild/bub.git
cd bub
uv sync
cp env.example .env
uv run bub --help
```

Minimal `.env`:

```bash
BUB_MODEL=openrouter:qwen/qwen3-coder-next
LLM_API_KEY=your_key_here
# Runtime off: falls back to model_output=prompt
BUB_RUNTIME_ENABLED=0 uv run bub run "hello"
```

Start interactive CLI:

```bash
uv run bub
# Internal command mode (line starts with ',')
BUB_RUNTIME_ENABLED=0 uv run bub run ",help"
```

## Interaction Model

- `hello`: natural language routed to model.
- `,help`: internal command.
- `,git status`: shell command.
- `, ls -la`: shell command (space after comma is optional).

Common commands:

```text
,help
,tools
,tool.describe name=fs.read
,skills.list
,skills.describe name=friendly-python
,handoff name=phase-1 summary="bootstrap done"
,anchors
,tape.info
,tape.search query=error
,tape.reset archive=true
,quit
```bash
# Model runtime (hosted providers usually require a key)
BUB_API_KEY=your_key uv run bub run "Summarize this repository"
```

## Channel Runtime (Optional)
## CLI Commands

Telegram:
- `bub run MESSAGE`: execute one inbound turn and print outbound messages
- `bub hooks`: print hook-to-plugin bindings
- `bub install PLUGIN_SPEC`: install plugin from PyPI or `owner/repo` (GitHub shorthand)

```bash
BUB_TELEGRAM_ENABLED=true
BUB_TELEGRAM_TOKEN=123456:token
BUB_TELEGRAM_ALLOW_FROM='["123456789","your_username"]'
uv run bub message
```
## Runtime Behavior

Discord:
- Regular text input: uses `run_model`; if runtime is unavailable, output falls back to the prompt text
- Comma commands: `,help`, `,tools`, `,fs.read ...`, etc.
- Unknown comma commands: executed as `bash -lc` in workspace
- Session event log: `.bub/runtime/<session-hash>.jsonl`
- `AGENTS.md`: if present in workspace, appended to runtime system prompt

```bash
BUB_DISCORD_ENABLED=true
BUB_DISCORD_TOKEN=discord_bot_token
BUB_DISCORD_ALLOW_FROM='["123456789012345678","your_discord_name"]'
BUB_DISCORD_ALLOW_CHANNELS='["123456789012345678"]'
uv run bub message
## Skills

- Discovery roots with deterministic override:
1. `<workspace>/.agent/skills`
2. `~/.agent/skills`
3. `src/bub_skills`
- Each skill directory must include `SKILL.md`
- Supported frontmatter fields:
- required: `name`, `description`
- optional: `license`, `compatibility`, `metadata`, `allowed-tools`

## Plugin Development

Plugins are loaded from Python entry points in `group="bub"`:

```toml
[project.entry-points."bub"]
my_plugin = "my_package.my_plugin"
```

## Development
Implement hooks with `@hookimpl` following `BubHookSpecs`.

## Runtime Environment Variables

- `BUB_RUNTIME_ENABLED`: `auto` (default), `1`, `0`
- `BUB_MODEL`: default `openrouter:qwen/qwen3-coder-next`
- `BUB_API_KEY`: runtime provider key
- `BUB_API_BASE`: optional provider base URL
- `BUB_RUNTIME_MAX_STEPS`: default `8`
- `BUB_RUNTIME_MAX_TOKENS`: default `1024`
- `BUB_RUNTIME_MODEL_TIMEOUT_SECONDS`: default `90`

## Documentation

- `docs/index.md`: overview
- `docs/architecture.md`: lifecycle, precedence, and failure isolation
- `docs/skills.md`: skill discovery and frontmatter constraints
- `docs/cli.md`: CLI usage and comma command mode
- `docs/features.md`: implemented capabilities and limits

## Development Checks

```bash
uv run ruff check .
uv run mypy
uv run mypy src
uv run pytest -q
just docs-test
```

## License

[Apache 2.0](./LICENSE)
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
volumes:
- ${BUB_WORKSPACE_PATH:-.}:/workspace
- ${BUB_HOME:-${HOME}/.bub}:/data
- ${BUB_AGENT_HOME:-${HOME}/.agent}:/root/.agent
- ${BUB_AGENT_HOME:-${HOME}/.agents}:/root/.agents
stdin_open: true
tty: true
restart: unless-stopped
105 changes: 58 additions & 47 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,60 @@
# Architecture

This page is for developers and advanced users who need to understand why Bub behavior is deterministic and how to extend it safely.

## Core Principles

1. One session, one append-only tape.
2. Same routing rules for user input and assistant output.
3. Command execution and model reasoning are explicit layers.
4. Phase transitions are represented by `anchor/handoff`, not hidden state jumps.
5. Human and agent operators follow the same collaboration boundaries.

## Runtime Topology

```text
input -> InputRouter -> AgentLoop -> ModelRunner -> InputRouter(assistant output) -> ...
\-> direct command response
```

Key modules:

- `src/bub/core/router.py`: command detection, execution, and failure context wrapping.
- `src/bub/core/agent_loop.py`: turn orchestration and stop conditions.
- `src/bub/core/model_runner.py`: bounded model loop and user-driven skill-hint activation.
- `src/bub/tape/service.py`: tape read/write, anchor/handoff, reset, and search.
- `src/bub/tools/*`: unified registry and progressive tool view.

## Single Turn Flow

1. `InputRouter.route_user` checks whether input starts with `,`.
2. If command succeeds, return output directly.
3. If command fails, generate a `<command ...>` block for model context.
4. `ModelRunner` gets assistant output.
5. `route_assistant` applies the same command parsing/execution rules.
6. Loop ends on plain final text, explicit quit, or `max_steps`.

## Tape, Anchor, Handoff

- Tape is workspace-level JSONL for replay and audit.
- `handoff` writes an anchor with optional `summary` and `next_steps`.
- `anchors` lists phase boundaries.
- `tape.reset` clears active context (optionally archiving first).

## Tools and Skills

- Built-in tools and skills live in one registry.
- System prompt starts with compact tool descriptions.
- Full tool schema is expanded on demand (`tool.describe` or explicit selection).
- `$name` hints progressively expand tool/skill details from either user input or model output.
## Core Components

- `BubFramework`: creates the plugin manager, loads plugins, and runs `process_inbound()`.
- `BubHookSpecs`: defines all hook contracts (`src/bub/hookspecs.py`).
- `HookRuntime`: executes hooks with sync/async compatibility helpers (`src/bub/hook_runtime.py`).
- `Agent`: builtin model-and-tools runtime (`src/bub/builtin/agent.py`).
- `ChannelManager`: starts channels, buffers inbound messages, and routes outbound messages (`src/bub/channels/manager.py`).

## Turn Lifecycle

`BubFramework.process_inbound()` currently executes in this order:

1. Resolve session via `resolve_session(message)` (fallback to `channel:chat_id` if empty).
2. Initialize state with `_runtime_workspace` from `BubFramework.workspace`.
3. Merge all `load_state(message, session_id)` dicts.
4. Build prompt via `build_prompt(message, session_id, state)` (fallback to inbound `content` if empty).
5. Execute `run_model(prompt, session_id, state)`.
6. Always execute `save_state(...)` in a `finally` block.
7. Render outbound batches via `render_outbound(...)`, then flatten them.
8. If no outbound exists, emit one fallback outbound.
9. Dispatch each outbound via `dispatch_outbound(message)`.

## Hook Priority Semantics

- Registration order:
1. Builtin plugin `builtin`
2. External entry points (`group="bub"`)
- Execution order:
1. `HookRuntime` reverses pluggy implementation order, so later-registered plugins run first.
2. `call_first` returns the first non-`None` value.
3. `call_many` collects every implementation return value (including `None`).
- Merge/override details:
1. `load_state` is reversed again before merge so high-priority plugins win on key collisions.
2. `provide_channels` is collected by `BubFramework.get_channels()`, and the first channel name wins, so high-priority plugins can override builtin channel names.

## Error Behavior

- For normal hooks, `HookRuntime` does not swallow implementation errors.
- `process_inbound()` catches top-level exceptions, notifies `on_error(stage="turn", ...)`, then re-raises.
- `on_error` itself is observer-safe: one failing observer does not block the others.
- In sync calls (`call_first_sync`/`call_many_sync`), awaitable return values are skipped with a warning.

## Builtin Runtime Notes

Builtin `BuiltinImpl` behavior includes:

- `build_prompt`: supports comma command mode; non-command text may include `context_str`.
- `run_model`: delegates to `Agent.run()`.
- `system_prompt`: combines a default prompt with workspace `AGENTS.md`.
- `register_cli_commands`: installs `run`, `gateway`, `chat`, plus hidden compatibility/diagnostic commands.
- `provide_channels`: returns `telegram` and `cli` channel adapters.
- `provide_tape_store`: returns a file-backed tape store under `~/.bub/tapes`.

## Boundaries

- `Envelope` stays intentionally weakly typed (`Any` + accessor helpers).
- There is no globally enforced schema for cross-plugin `state`.
- Runtime behavior in this document is aligned with current source code.
Binary file removed docs/assets/images/baby-bub.png
Binary file not shown.
34 changes: 0 additions & 34 deletions docs/channels.md

This file was deleted.

47 changes: 47 additions & 0 deletions docs/channels/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Channels

Bub uses channel adapters to run the same agent pipeline across different I/O endpoints.

## Builtin Channels

- `cli`: local interactive terminal channel (`uv run bub chat`)
- `telegram`: Telegram bot channel (`uv run bub gateway`)

See [Telegram](telegram.md) for channel-specific configuration and runtime behavior.

## Run Modes

Local interactive mode:

```bash
uv run bub chat
```

Channel listener mode (all non-`cli` channels by default):

```bash
uv run bub gateway
```

Enable only Telegram:

```bash
uv run bub gateway --enable-channel telegram
```

## Session Semantics

- `run` command default session id: `<channel>:<chat_id>`
- Telegram channel session id: `telegram:<chat_id>`
- `chat` command default session id: `cli_session` (override with `--session-id`)

## Debounce Behavior

- `cli` does not debounce; each input is processed immediately.
- Other channels can debounce and batch inbound messages per session.
- Comma commands (`,` prefix) always bypass debounce and execute immediately.

## About Discord

Core Bub does not currently include a builtin Discord adapter.
If you need Discord, implement it in an external plugin via `provide_channels`.
Loading
Loading