Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 21 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@
[![Docs](https://img.shields.io/badge/docs-mountainowl.github.io%2Fbubo-4f62ad)](https://mountainowl.github.io/bubo/)
[![License: MIT](https://img.shields.io/badge/license-MIT-111827)](https://github.com/mountainowl/bubo/blob/main/LICENSE)

Bubo reviews your GitLab MRs and GitHub PRs with **the LLM you choose** (Codex,
Claude, or any model your CLI drives) and posts only the findings worth acting on
as inline threads — no chatbot noise, no praise, no summaries. Like the owl it's
named for, it stays silent until it has something worth saying.
[Bubo](https://en.wikipedia.org/wiki/Bubo_(genus)) is the genus of the great
horned and eagle owls — patient night hunters that sit silent, see in the dark,
and strike only when sure. Code review, same idea: Bubo reviews your GitLab MRs
and GitHub PRs with **the LLM you choose** (Codex, Claude, or any model your CLI
drives) and posts only the findings worth acting on as inline threads — no
chatbot noise, no praise, no summaries.

![Bubo hero](https://raw.githubusercontent.com/mountainowl/bubo/main/docs/images/bubo-hero.png)
## Features at a glance

| | |
|---|---|
| 🧠 **Bring your own LLM** | Codex, Claude, or any model your CLI drives — no vendor lock-in. |
| 🔒 **Self-hosted** | Code, diffs, and review data stay on your infrastructure. |
| 🔀 **GitLab + GitHub** | MRs and PRs, one config, identical behavior on both. |
| 🎯 **Signal over noise** | Only actionable inline findings; one "all good" ack on a clean change. |
| 🎭 **Moods** | Pick the review voice — terse / collaborative / socratic / formal / casual. |
| 📉 **Learns your taste** | Suppresses finding-classes your team repeatedly disputes. |
| ✅ **Verify before posting** | Optional "is this real?" passes drop findings that don't hold up. |
| 🛡️ **Governance-ready** | AI-code provenance, rigor modulation, auditable on-prem report. |
| 🔌 **MCP + CI** | Built-in `bubo-mcp` server + a GitHub Action to review PRs in CI. |

## 📖 Documentation

Expand Down Expand Up @@ -90,14 +104,8 @@ your humans without touching the data your compliance reports run on. Ships as
`terse`; opt in when you're ready, set it once, leave it to the operator.
→ [tone reference](https://github.com/mountainowl/bubo/blob/main/docs/configuration.md#review-comment-tone-moods)

Real (sanitized) inline findings on GitLab MRs:

![Sanitized inline finding — data primer](https://raw.githubusercontent.com/mountainowl/bubo/main/docs/images/gitlab-mr-review-data-primer.png)

![Sanitized inline finding — exception handler](https://raw.githubusercontent.com/mountainowl/bubo/main/docs/images/gitlab-mr-review-exception-handler.png)

More sanitized examples are in [docs/examples/README.md](https://github.com/mountainowl/bubo/blob/main/docs/examples/README.md).
Demo GIF: [docs/media/bubo-demo.gif](https://github.com/mountainowl/bubo/blob/main/docs/media/bubo-demo.gif).
More sanitized review examples are in
[docs/examples/README.md](https://github.com/mountainowl/bubo/blob/main/docs/examples/README.md).

## 60-second quickstart

Expand Down
72 changes: 46 additions & 26 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,57 @@

**Agentic AI code review — with the LLM of your choice.**

Bubo reviews your GitLab MRs and GitHub PRs, posts only the findings worth
acting on, and runs on the LLM *you* pick — self-hosted, so nothing leaves your
infrastructure. Like the owl it's named for, it stays quiet until it has
something worth saying: no chatbot chatter, no praise, no summaries.
[Bubo](https://en.wikipedia.org/wiki/Bubo_(genus)) is the genus of the great
horned and eagle owls — patient night hunters that sit silent, see in the dark,
and strike only when they're sure. Code review, same idea: Bubo watches your
diffs, stays quiet through the noise, and speaks only when it finds something
worth the author's time. It runs on the LLM *you* pick, self-hosted, so nothing
leaves your infrastructure — no chatbot chatter, no praise, no summaries.

![Bubo hero](images/bubo-hero.png)
<div class="grid cards" markdown>

[Copy-paste recipes :material-silverware-fork-knife:](recipes.md){ .md-button .md-button--primary }
[60-second quickstart :material-rocket-launch:](install-and-configure.md){ .md-button }
[Source on GitHub :material-github:](https://github.com/mountainowl/bubo){ .md-button }
- :material-silverware-fork-knife:{ .lg .middle } **Copy-paste recipes**

---

GitLab & GitHub setups you can paste and run.

[:octicons-arrow-right-24: Recipes](recipes.md)

- :material-rocket-launch:{ .lg .middle } **60-second quickstart**

---

Install, configure, first review — fast.

[:octicons-arrow-right-24: Get started](install-and-configure.md)

- :material-github:{ .lg .middle } **Source on GitHub**

---

MIT-licensed, self-hostable, BYO-LLM.

[:octicons-arrow-right-24: View the repo](https://github.com/mountainowl/bubo)

</div>

---

## Features

- **Bring your own LLM.** Codex, Claude, or any model your CLI can drive. No
vendor lock-in.
- **Self-hosted.** Code, diffs, and review data stay on your infrastructure.
- **GitLab MRs and GitHub PRs**, one config, same behavior on both.
- **Signal over noise.** Only actionable inline findings — Issue / Impact /
Evidence / Fix / Confidence. A clean change gets one "all good" acknowledgement.
- **Give it a mood.** Pick the review voice — `terse`, `collaborative`,
`socratic`, `formal`, or `casual` — without touching the underlying data.
- **Learns your team's taste.** Tracks which findings get accepted vs. disputed
and can suppress the finding-classes you keep rejecting.
- **Verify before posting.** Optional independent "is this real?" passes drop
findings that don't hold up — point them at a second model for real diversity.
- **Built for governance.** Opt-in AI-code provenance, review-rigor modulation,
and an auditable on-prem report (accept rate, ROI, noise trend, latency,
policy decisions) via CLI and MCP.
- **Observability in.** OpenTelemetry metrics; cosign-signed releases with SBOMs.
## Features at a glance

| | |
|---|---|
| 🧠 **Bring your own LLM** | Codex, Claude, or any model your CLI drives — no vendor lock-in. |
| 🔒 **Self-hosted** | Code, diffs, and review data stay on your infrastructure. |
| 🔀 **GitLab + GitHub** | MRs and PRs, one config, identical behavior on both. |
| 🎯 **Signal over noise** | Only actionable inline findings (Issue / Impact / Evidence / Fix / Confidence); one "all good" ack on a clean change. |
| 🎭 **Moods** | Pick the review voice — `terse` / `collaborative` / `socratic` / `formal` / `casual` — without touching the data. |
| 📉 **Learns your taste** | Suppresses finding-classes your team repeatedly disputes. |
| ✅ **Verify before posting** | Optional "is this real?" passes drop findings that don't hold up — point them at a second model for real diversity. |
| 🛡️ **Governance-ready** | AI-code provenance, review-rigor modulation, and an auditable on-prem report (accept rate, ROI, noise trend, latency, policy decisions). |
| 📊 **Observability** | OpenTelemetry metrics; cosign-signed releases with SBOMs. |
| 🔌 **MCP + CI** | Built-in `bubo-mcp` server, and a GitHub Action to review PRs in CI. |

## What it does

Expand Down
111 changes: 111 additions & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# MCP server (`bubo-mcp`)

Bubo ships its own Model Context Protocol server so chat clients (Codex, Claude
Desktop, …) can inspect review state and trigger reviews on demand. Launch it
with the `bubo-mcp` console script (or `bin/bubo mcp`). Two interfaces, three
deployment patterns.

## Two interfaces

**Metrics — read-only, against SQLite:**

| Tool | Returns |
|---|---|
| `health` | `{status, last_status, last_updated_at, age_seconds}` — same semantics as `bubo-poller --health`. |
| `list_recent_reviews` | Recent `reviewed_mrs` rows newest-first, with optional `status` / `project` / `limit` filters. |
| `get_review` | One review row by `(project, iid[, sha])`; resolves to the latest SHA when unspecified. |
| `get_findings` | Per-finding rows (file, line, severity, category, confidence, posted body, discussion id). |
| `get_finding_outcomes` | Resolution state populated by `--sync-outcomes` (resolved / disputed / merged_unresolved / …). |
| `get_metrics` | Aggregated counts + token / cost sums for a `since_hours` window, optionally filtered by project. |

**Review — trigger a one-shot review:**

| Tool | Args | Returns |
|---|---|---|
| `review_change` | `url=…` **or** `provider={gitlab,github,auto}` + `project=…` + `number=…`; optional `timeout_seconds`. | `{provider, project, number, exit_code, duration_seconds, findings, raw_output}`. |

`provider="auto"` (the default) infers the provider from the URL when one is
given, otherwise falls back to `[scm].provider` in `config/env.toml`.
`review_change` blocks until the underlying `reviewer_command` subprocess
completes — set the client-side `tool_timeout_sec` accordingly. **MCP-triggered
reviews return findings inline; they do not write to `reviewed_mrs`**, so they
won't show up in the metrics tools. Use the poller for state-tracked reviews.

## Three deployment patterns

The server supports two transports — stdio (default) and HTTP+SSE with
bearer-token auth — selected via `[mcp_server].transport` in `config/env.toml`.
Pick the pattern that matches where Codex and the reviewer live.

**Pattern 1 — same host (laptop runs Codex *and* the reviewer):**

```toml
# ~/.codex/config.toml
[mcp_servers.bubo]
command = "/absolute/path/to/bubo/bin/bubo"
args = ["mcp"]
startup_timeout_sec = 20
tool_timeout_sec = 1800 # ≥ [review].timeout_seconds for review_change
```

No reviewer-side config change needed — `[mcp_server].transport` defaults to
`"stdio"`. `~/` expansion works, so a `~/bubo/...` path holds across machines
that install to the same per-user location.

**Pattern 2 — remote via SSH (laptop runs Codex; server runs the reviewer + holds the SQLite):**

```toml
# ~/.codex/config.toml
[mcp_servers.bubo]
command = "ssh"
args = [
"-T", # no pty; keeps stdout clean for MCP framing
"-o", "ServerAliveInterval=30", # keeps long review_change calls alive across NAT
"bubo.example.com", # ssh_config Host alias, or user@host
"/opt/bubo/bin/bubo mcp", # absolute path on the server
]
startup_timeout_sec = 30
tool_timeout_sec = 1800
```

Operator prerequisites: key-based SSH (an interactive password breaks the stdio
loop), the install root readable by the SSH user, and no MOTD/banner noise on
stdout (set `PrintMotd no` server-side or `LogLevel QUIET` client-side). No new
code — SSH multiplexes the MCP stdio over the wire.

**Pattern 3 — remote over HTTP+bearer (multi-tenant or org-wide):**

On the reviewer host, set `config/env.toml`:

```toml
[mcp_server]
transport = "http"
host = "0.0.0.0" # or a specific interface
port = 8765
bearer_token = "${BUBO_MCP_TOKEN}" # generate: openssl rand -hex 32
```

Then run `bin/bubo mcp` (e.g. under a systemd unit). On the client:

```toml
# ~/.codex/config.toml — exact key for HTTP MCP servers varies across
# Codex versions; check `codex --help` if these names look wrong.
[mcp_servers.bubo]
url = "https://reviewer.example.com/mcp"
bearer_token = "..." # matches BUBO_MCP_TOKEN
startup_timeout_sec = 30
tool_timeout_sec = 1800
```

The server enforces `Authorization: Bearer <token>` on every request and returns
401 otherwise. **The server does not terminate TLS** — bind to `127.0.0.1` and
front it with nginx/caddy, expose it only over a VPN, or accept that the token
crosses the network in clear text.

## Upstream wrappers

The dispatcher also fronts the **upstream** MCP servers — `bin/bubo mcp-upstream
gitlab` and `bin/bubo mcp-upstream github` — which locate the third-party GitLab
/ GitHub MCP server on `PATH` and exec it with `config/env.toml` tokens injected.
The poster path uses these to create inline review threads. Point Codex at them
directly for a chat-driven session with the same MCP surface the reviewer uses.
113 changes: 4 additions & 109 deletions docs/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,116 +20,11 @@ deliberately no daemon mode. Each invocation processes up to
`max_merge_requests_per_poll` MRs and exits. See [operate.md](operate.md)
for ready-to-copy cron/systemd templates.

## MCP interface (`bubo-mcp`)
## MCP server (`bubo-mcp`)

Bubo ships its own MCP server with **two interfaces**: a metrics side for
inspecting review state, and a review side for triggering a fresh review by
URL or `(provider, project, number)`.

**Metrics interface — read-only, against SQLite:**

| Tool | Returns |
|---|---|
| `health` | `{status, last_status, last_updated_at, age_seconds}` — same semantics as `bubo-poller --health`. |
| `list_recent_reviews` | Recent `reviewed_mrs` rows newest-first, with optional `status` / `project` / `limit` filters. |
| `get_review` | One review row by `(project, iid[, sha])`; resolves to the latest SHA when unspecified. |
| `get_findings` | Per-finding rows (file, line, severity, category, confidence, posted body, discussion id). |
| `get_finding_outcomes` | Resolution state populated by `--sync-outcomes` (resolved / disputed / merged_unresolved / …). |
| `get_metrics` | Aggregated counts + token / cost sums for a `since_hours` window, optionally filtered by project. |

**Review interface — trigger a one-shot review:**

| Tool | Args | Returns |
|---|---|---|
| `review_change` | `url=…` **or** `provider={gitlab,github,auto}` + `project=…` + `number=…`; optional `timeout_seconds`. | `{provider, project, number, exit_code, duration_seconds, findings, raw_output}`. |

`provider="auto"` (the default) infers the provider from the URL when one is
given, otherwise falls back to `[scm].provider` in `config/env.toml`.
`review_change` blocks until the underlying `reviewer_command` subprocess
completes — set the client-side `tool_timeout_sec` accordingly. **MCP-triggered
reviews return findings inline; they do not write to `reviewed_mrs`**, so they
won't show up in the metrics tools. Use the poller for state-tracked reviews.

### Three deployment patterns

The server supports two transports — stdio (default) and HTTP+SSE with
bearer-token auth — selected via `[mcp_server].transport` in
`config/env.toml`. Pick the pattern that matches where Codex and the
reviewer live.

**Pattern 1 — same host (laptop runs Codex *and* the reviewer):**

```toml
# ~/.codex/config.toml
[mcp_servers.bubo]
command = "/absolute/path/to/bubo/bin/bubo"
args = ["mcp"]
startup_timeout_sec = 20
tool_timeout_sec = 1800 # ≥ [review].timeout_seconds for review_change
```

No reviewer-side config change needed — `[mcp_server].transport` defaults
to `"stdio"`. `~/` expansion works, so a `~/bubo/...` path holds across
machines that install to the same per-user location.

**Pattern 2 — remote via SSH (laptop runs Codex; server runs the reviewer + holds the SQLite):**

```toml
# ~/.codex/config.toml
[mcp_servers.bubo]
command = "ssh"
args = [
"-T", # no pty; keeps stdout clean for MCP framing
"-o", "ServerAliveInterval=30", # keeps long review_change calls alive across NAT
"bubo.example.com", # ssh_config Host alias, or user@host
"/opt/bubo/bin/bubo mcp", # absolute path on the server
]
startup_timeout_sec = 30
tool_timeout_sec = 1800
```

Operator prerequisites: key-based SSH (an interactive password breaks the
stdio loop), the install root readable by the SSH user, and no MOTD/banner
noise on stdout (set `PrintMotd no` server-side or `LogLevel QUIET`
client-side). No new code — SSH multiplexes the MCP stdio over the wire.

**Pattern 3 — remote over HTTP+bearer (multi-tenant or org-wide):**

On the reviewer host, set `config/env.toml`:

```toml
[mcp_server]
transport = "http"
host = "0.0.0.0" # or a specific interface
port = 8765
bearer_token = "${BUBO_MCP_TOKEN}" # generate: openssl rand -hex 32
```

Then run `bin/bubo mcp` (e.g. under a systemd unit). On the client:

```toml
# ~/.codex/config.toml — exact key for HTTP MCP servers varies across
# Codex versions; check `codex --help` if these names look wrong.
[mcp_servers.bubo]
url = "https://reviewer.example.com/mcp"
bearer_token = "..." # matches BUBO_MCP_TOKEN
startup_timeout_sec = 30
tool_timeout_sec = 1800
```

The server enforces `Authorization: Bearer <token>` on every request and
returns 401 otherwise. **The server does not terminate TLS** — bind to
`127.0.0.1` and front it with nginx/caddy, expose it only over a VPN, or
accept that the token crosses the network in clear text.

### Upstream wrappers

The dispatcher also fronts the **upstream** MCP servers — `bin/bubo
mcp-upstream gitlab` and `bin/bubo mcp-upstream github` — which locate the
third-party GitLab / GitHub MCP server on `PATH` and exec it with
`config/env.toml` tokens injected. The poster path uses these to create
inline review threads. Point Codex at them directly for a chat-driven session
with the same MCP surface the reviewer uses.
Bubo ships an MCP server with read-only metrics tools and a one-shot
`review_change` trigger, over stdio or HTTP. Full setup and the three deployment
patterns are on the **[MCP server](mcp.md)** page.

## How the GitHub provider talks to GitHub

Expand Down
3 changes: 2 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ nav:
- Prerequisites: prerequisites.md
- Install and configure: install-and-configure.md
- Run: run.md
- MCP server: mcp.md
- GitHub Action (CI review): github-action.md
- Configuration reference: configuration.md
- Operate: operate.md
- Telemetry: telemetry.md
- Metrics & telemetry: telemetry.md
- Examples: examples/README.md
- References: references.md

Expand Down
Loading