Bubo — agentic AI code review for GitLab MRs and GitHub PRs, with the LLM of your choice.
Project description
Bubo 🦉
Agentic AI code review — with the LLM of your choice.
Self-hosted · bring-your-own-LLM · GitLab + GitHub · inline findings only · governance, provenance & audit · OpenTelemetry metrics
Bubo 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.
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
Rendered docs live at → mountainowl.github.io/bubo
— the canonical reference. The docs/*.md files below are its source; this README
is a teaser.
👉 New here? The Recipes (docs/recipes.md) are copy-paste setups for GitLab and GitHub, using Codex (the bundled default) or Claude as the review agent.
What a review looks like
Findings are posted inline in a fixed shape — Issue / Impact / Evidence /
Fix / Confidence:
Issue: HS256 JWT fallback is skipped when Cognito URL construction fails.
Impact: Valid local/shared-secret JWT requests return 500 instead of authenticating.
Evidence: The changed interceptor rethrows InvalidAwsUrlException before fallback runs.
Fix: Treat Cognito validation construction failures as failed Cognito auth when fallback is allowed.
Confidence: 0.94
Found nothing? Bubo posts one short change-level acknowledgement
(Automated review ran — no issues found.) so a clean MR/PR reads differently
from one the reviewer never touched. It's on by default, dedup'd by bot author +
exact body, and configurable under [agents] (see the
configuration reference).
🎭 Give it a mood
A finding only helps if someone reads it. The shape above is terse — the
default, and the right call when you want pure signal. But a robotic comment gets
skimmed; one that sounds like a teammate gets fixed. So Bubo lets you pick the
voice. One knob:
[review]
tone = "collaborative" # terse · collaborative · socratic · formal · casual
Below is one real finding — a cookie-deletion bug Bubo caught on a public PR
— wearing four moods. Same bug, same evidence, same 0.99 confidence underneath.
Only the words change:
🤝 collaborative — for teams who want a teammate, not a linter Heads up — this removes by name only, so if the jar has
sidfora.exampleandb.example, onepopitem()returns one pair but deletes both cookies. Probably worth clearing the specific cookie using its domain/path/name.
🤔 socratic — for mentoring and review-as-teaching What happens here when the jar has the same cookie name for two domains?
del self[name]goes throughremove_cookie_by_namewithout domain/path, so this removes every matching cookie while returning only one pair — should we clear the selected cookie by domain/path/name instead?
🏛️ formal — for regulated shops and a clean audit trail When multiple domains contain the same cookie name, this deletes by name only and removes every matching cookie while returning a single pair. Recommend clearing the specific cookie selected by
popitemusing its domain, path, and name.
😎 casual — for startups who keep it light Quick one — this deletes by name only, so same-name cookies on other domains/paths get cleared too. Grab the Cookie from the iterator and clear that exact domain/path/name.
The catch? There isn't one. Mood changes only the words a developer reads.
Severity, evidence, confidence, the dedup fingerprint, and every row in your
governance/audit dataset stay identical across tones — so you tune the voice for
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
More sanitized review examples are in docs/examples/README.md.
60-second quickstart
Install the prereqs (uv, Python 3.14+, Git, plus the CLI for your SCM and a Codex agent — see prerequisites), then:
uv tool install bubo # from PyPI (or: pip install bubo)
# or track the main branch:
# uv tool install git+https://github.com/mountainowl/bubo
bubo init # idempotent; --dry-run to preview
# Edit ~/.local/share/bubo/config/env.toml:
# [gitlab].token, [agents].llm_model, [agents].llm_api_key,
# [agents].llm_api_key_env, and at least one [[projects]] entry.
bubo doctor # verify before first poll
bubo-poller # one poll cycle; exits at the end
The first cycle runs with [review].dry_run = true (the default) — findings are
planned, no comments posted. Flip to false once a real review looks right. Full
walkthrough in the Recipes and
install and configure; poller flags and the
bundled MCP server are in run.
Prefer a container? A multi-arch image is published to GHCR each release:
docker pull ghcr.io/mountainowl/bubo
docker run --rm ghcr.io/mountainowl/bubo bubo report # or bubo init / bubo-poller
The image ships bubo + git; the review-agent CLI (Codex/Claude) is BYO — derive
your own image (FROM ghcr.io/mountainowl/bubo) or mount it in.
Further reading
These render on the docs site and as plain Markdown in the repo:
| Doc | What's in it |
|---|---|
| Prerequisites | macOS / Linux runtime, per-provider tools, credentials, install verification. |
| Install and configure | uv tool install, bubo init, the minimum config/env.toml, GitLab and GitHub bot setup. |
| Run | One-off review, the poller, the bundled bubo-mcp MCP server, and upstream wrappers. |
| Configuration reference | Every [scm] / [gitlab] / [github] / [review] / [poller] / [agents] / [telemetry] / [[projects]] setting and its default. |
| Operate | Remote deploy, scheduling under cron or systemd, --sync-outcomes grading, one-shot backfill. |
| Telemetry | Emitted llm_review.* metrics, ready-made dashboard queries, cardinality discipline. |
Status
- GitLab & GitHub posting via polling — production path, at outcome-metric
parity. Set
[scm].provider = "github"(orBUBO_PROVIDER=github). - MCP server (
bubo-mcp) — read-only metrics + triggered reviews; stdio or HTTP. - Codex or Claude — Bubo runs the review through a wrapper around your agent CLI. Codex ships pre-wired as the bundled default; Claude works the same way once you point the wrapper at it.
- Webhook-driven triggering — not yet; polling is the only path.
Review execution sits outside CI/CD by design. Run it as a poller beside your existing pipelines.
Security
config/env.tomlis gitignored and holds tokens. Do not print or commit real values from it.- Review-agent stdout is redacted (
GITLAB_TOKEN=,OPENAI_API_KEY=,glpat-…,sk-…, and credentialed Git URLs) before it touches reports, logs, or the database error column. - The reviewer subprocess runs under a strict env allowlist — host secrets aren't
passed wholesale into the LLM agent. Releases are cosign-signed with an SBOM.
Report vulnerabilities per
SECURITY.md.
Bot avatar
Upload assets/bubo.png as the GitLab (or future GitHub) bot avatar.
Community
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bubo-0.21.3.tar.gz.
File metadata
- Download URL: bubo-0.21.3.tar.gz
- Upload date:
- Size: 345.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f30bbc70bd7a20ea6bcf7f8f4ffb1e674b1c6f2828aefa4047593ed7e38e01c
|
|
| MD5 |
dc42ec464f52a2bef2935d8f6e7aa72b
|
|
| BLAKE2b-256 |
edf5679e1600d7d12e039e36efec29d260e6730ef5ce7ef3a3a5040931a293bd
|
Provenance
The following attestation bundles were made for bubo-0.21.3.tar.gz:
Publisher:
publish-pypi.yml on mountainowl/bubo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bubo-0.21.3.tar.gz -
Subject digest:
6f30bbc70bd7a20ea6bcf7f8f4ffb1e674b1c6f2828aefa4047593ed7e38e01c - Sigstore transparency entry: 1844776316
- Sigstore integration time:
-
Permalink:
mountainowl/bubo@c1ee87eeb82fcc21ad977c97788bd0d43f0033fb -
Branch / Tag:
refs/tags/v0.21.3 - Owner: https://github.com/mountainowl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@c1ee87eeb82fcc21ad977c97788bd0d43f0033fb -
Trigger Event:
push
-
Statement type:
File details
Details for the file bubo-0.21.3-py3-none-any.whl.
File metadata
- Download URL: bubo-0.21.3-py3-none-any.whl
- Upload date:
- Size: 160.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
864bda3b5ac2af2cdf180c279fa6716c322be9738914650745d60b13b80f4207
|
|
| MD5 |
eb93fe90185928bc36abcae51499657e
|
|
| BLAKE2b-256 |
1c8e6e19edda99ec7f730933e8695f69f9017972eeacc0cbec2ca407a406ca70
|
Provenance
The following attestation bundles were made for bubo-0.21.3-py3-none-any.whl:
Publisher:
publish-pypi.yml on mountainowl/bubo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bubo-0.21.3-py3-none-any.whl -
Subject digest:
864bda3b5ac2af2cdf180c279fa6716c322be9738914650745d60b13b80f4207 - Sigstore transparency entry: 1844776472
- Sigstore integration time:
-
Permalink:
mountainowl/bubo@c1ee87eeb82fcc21ad977c97788bd0d43f0033fb -
Branch / Tag:
refs/tags/v0.21.3 - Owner: https://github.com/mountainowl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@c1ee87eeb82fcc21ad977c97788bd0d43f0033fb -
Trigger Event:
push
-
Statement type: