Skip to content
v0.3.0MIT

Agent's sidekick — deterministic lifecycle closure (task → agent → PR → gate → debt). Wedge: review gate + debt ledger with existing agents, task store, and PR/CI. Orchestration (fleet/convoy) is optional and progressive — not a replacement runtime.

bro 🤝

npm CI license node

Your agent's sidekick. Skills are instructions — bro is the brain.

Landing: broject.dev · Docs: broject.dev/docs

ICP: maintainers and small teams with existing agents, a task store, and PR/CI.
JTBD: deterministic lifecycle closure — take work from task → agent → PR → gate → debt, close it without gaps, and sweep what remains.

Wedge: portable task → agent → PR → gate → debt contract. Start with the review gate and debt ledger — no fleet, no model-gateway required. Orchestration (fleet/convoy/loop) is optional and progressive, not a replacement runtime.

npx @broject/bro act status             # review gate verdict
npx @broject/bro debt prs               # unprocessed merged PRs not yet swept into the ledger
npx @broject/bro debt collect           # sweep threads into the ledger
npx @broject/bro debt status            # what's left

What's the deal

Review bots dump comments on your PRs. You merge, the threads stay unresolved, the findings evaporate. bro harvests them into a local ledger (.agents/review-debt/) and slaps debt:* labels on the PRs it already swept — so nothing gets scanned twice and nothing hides.

PR merged → bro debt collect → findings land in .agents/review-debt/
                            → PR gets debt:collected (or debt:clean)
                            → you see exactly what's left: bro debt prs

What bro does now

Not a plugin — a system. The CLI is the mechanics; per-client plugins are transport; hooks put state back in front of the agent at every lifecycle boundary; skills carry the policy; connectors swap the backends (review host, task store, agent plane, events); guards nudge and the judge decides. bro runs bro's own repo — a bead becomes a convoy becomes a PR, act gates the merge, debt sweeps the threads, learn keeps the lesson.

  • Planning: next, spec, stack, query — pick the work, write the contract, line up the chain, ask the providers.
  • Orchestration: loop, convoy, fleet, notify/bus — run the backlog, pour the molecules, watch the workers. Non-blocking by design: long waits (bro act wait, bro watch --every) run in a background shell, not a subagent — zero tokens while they poll, and your agent keeps working.
  • Review: act, debt, judge, guard — the gate as code, the ledger, the verdicts, the nudges.
  • Self-reflection: drill, wtf/retrospect, learn — descend, vent, remember.
  • Plumbing: providers, status/serve, sweep, sync, setup/doctor/check/run/plan/cleanup/plugins — connect, host, dispose, move, wire.

Install

npx -y @broject/bro --help     # zero install
npm i -g @broject/bro          # or keep bro around: bro debt status

npm latest is 0.2.5 — this page uses the flat spellings the published CLI dispatches (bro debt status). The grouped surface (bro review debt status) lands in 0.3.0; flat names stay permanent aliases, and upcoming commands are marked 0.3.0.

Requires: node >= 22.18, gh authenticated, bd (beads) — it's a default store, so it's required unless you opt out. That's it. No tokens to babysit, no config files to confess to. (Zero-beads fallback: "stores": ["jsonl"] in bro.config.local.json.)

Install as an agent plugin

This repo is a plugin marketplace + registry — one bro plugin packaged per client:

ClientInstall
Devindevin plugins install ThePlenkov/bro (or ThePlenkov/bro#plugins/devin/bro)
Claude Code/plugin marketplace add ThePlenkov/bro → /plugin install bro@bro
Codexcodex plugin marketplace add ThePlenkov/bro → install bro
Cursor/add-plugin https://github.com/ThePlenkov/bro, then install bro
OpenCodeadd "plugin": ["@broject/bro"] to opencode.json, or bro plugins install opencode
Kilobro plugins install kilo
pibro plugins install pi
ompomp plugin install ./plugins/omp/bro, or bro plugins install omp

Every adapter ships the same skills and lifecycle hooks (session rehydration, review-gate stop, self-approve for bro/bd) wired through hooks/run.sh — local dist → bro on PATH → major-pinned npx, always fail-open. Cursor's adapter is plugins/cursor/bro; its hooks speak Cursor's schema (additional_context, one followup_message on stop). Cloud agents do not run sessionStart, so the first prompt rehydrates once.

The pi and omp adapters are generated from a shared core (packages/cli/src/adapter/): the core holds the bro mechanics, each client holds only its host's own vocabulary, and the build folds them into one standalone file. omp's plugin adds the mode bridges — Plan, Goal, Vibe, Prewalk and Advisor keep omp's own semantics, and bro supplies the durable state they lack (claims, specs, the judge, the debt ledger). See specs/bro-omp-plugin.md.

OpenCode, Kilo, and pi are native plugins the CLI installs itself — bro plugins install <client> writes the adapter module to the client's own plugin dir (bro plugins list prints the client × scope matrix). OpenCode loads JS/TS modules instead of a hook manifest, so bro ships as a native plugin (packages/cli/src/opencode.ts, published as the package's ./server export) that spawns its own CLI. This entry uses OpenCode's V1 plugin API and does not run on V2. Two consequences:

  • Install by package name only. OpenCode's loader reads exports["./server"] from the installed package and does not accept subpath specifiers — "@broject/bro/opencode" is not a valid entry. Pin a build with "plugin": [["@broject/bro", { "command": { "cmd": "/path/to/bro" } }]].
  • The stop gate re-prompts instead of blocking. OpenCode has no pre-stop hook; session.idle arrives after the turn is over. bro feeds the blocker back as a synthetic turn once per session — a second block is logged, not re-prompted. Same "gates, not loops" rule the other adapters get from stop_hook_active, and an aborted or errored turn never gates at all.

Commands

Full reference: broject.dev/docs.

Planning

CommandWhat bro does
bro next [--list] [--json] [--global]Claim the top ready bead and print the work order
bro spec check|drift|new|tree|initCheck spec coverage and drift, scaffold specs, and inspect their tree
bro stack push|list|sync (+ publish, merge in 0.3.0)Build, register, retarget, and land stacked bead→worktree→PR chains
bro query <plan.toml>Run a cross-provider GraphQL fan-out plan — one merged JSON answer
bro work enter|leave|list|pruneManage sibling worktrees and their recorded stack membership

Orchestration

CommandWhat bro does
bro loop [--max N] [--dry-run] [--stack NAME] [--label a,b]Claim, work, pass the gate, close, and repeat
bro convoy pour|status|next|claim|done|list|runRun a beads formula as a claimable molecule — run is the molecule queue
bro fleet [--json|--live]View molecules, steps, agents, worktrees, and PRs
bro agents status|up|down|pruneInspect, spawn, respawn, stop, or reap detached agents
bro watch [--once|--every N|--notify], bro watch install|uninstallRead-only heartbeat; --every is the session pulse (bro/pulse.lock, one per repo); install arms the want-marker (bro/pulse.json) — OS timers retired
bro notify <text>Drop an event into the session mailbox — addressed, typed, coalesced
bro bus serve|publish|subscribe|statusThe local event broker behind the events facade
bro drive [--once|--every N|--no-merge] (+ install|uninstall in 0.3.0)Apply the act gate to fleet PRs; merge only on green. install arms the merge path (bro/drive.json) — session checkpoints rearm a dead supervisor

Review

CommandWhat bro does
bro act status [PR]Exit gate as code — open threads, pending CI, SAST findings, mergeability; non-zero while blocked
bro act threads|resolve|reply|wait|mergeWork the threads; merge only when the gate is green
bro act rearm [--dry-run]Resurrect dead PR watchers — a pushed PR is never unwatched
bro debt collect|prs|status|next|set|sync|stats|trendHarvest unresolved threads on merged PRs into the ledger, then work them off
bro judge decide|stats|replayCalibrated decisions for agent loops — typed questions, typed answers with confidence
bro guard list|testInspect and dry-run declarative prompt guards — nudges, never gates

Self-reflection

CommandWhat bro does
bro drill down|up|current|tree|list|distillScoped descent frames — result and prevention required on the way up
bro wtf <complaint>Capture the complaint verbatim as a bead
bro retrospect record|status|schema|listTurn wtfs into retro records and prevention beads
bro learn add|list|show|forget|capture|probeStore lessons and surface them when their triggers match

Plumbing

CommandWhat bro does
providers configNamed model-plane registry (api/acp/cli kinds) consumed by judge and fleet — bro doctor reports it
bro status [--json|--deep]The compact live board — beads + fleet + drill + git in one read
bro serve [--port N]The fleet facade over loopback HTTP/JSON for thin clients
bro sweep status|distill|runGated disposal for closed beads — gate → archive → prune → flatten
bro sync [--pull]Push or restore bro artifacts on refs/bro/data
bro setup [--beads] [--skills] [--pack [NAME]]Configure the repo and optionally install beads, skills, or a capability pack
bro doctor [--json]Diagnose Node, git, auth, beads, hooks, providers, and configuration
bro check [--evaluate] [--json]Run the repo's Sverka workflow; --evaluate applies its policy gate
bro run <plan.toml> / bro plan validateExecute or check a validated plan
bro plugins [list|install|uninstall]Print the registry, or install client adapters (opencode, kilo, pi, omp)
bro task|store …Document verbs; --global targets the user-level store
bro cleanup [--remote] [--dry-run]Delete local branches whose PR merged and stale temp dirs

The pipeline (beads)

bro setup --beads drops debt-pipeline.formula.toml into .beads/formulas/:

bd mol pour debt-pipeline
#   collect → HUMAN GATE (triage) → fix → PR gate → sync

Every step is a bro command; the human gate is the point. bro collects and carries — the verdict is yours.

Config (optional)

bro.config.json in the repo root — committed project policy, merged over ~/.config/bro/config.json (global user) and under bro.config.local.json (gitignored machine overrides — bro setup writes there). Everything's optional:

{
  "stores": ["jsonl", "beads"],
  "personality": "terse",
  "debt": {
    "dir": ".agents/review-debt",
    "sources": ["review-threads", "dependabot", "secret-scanning"],
    "stale_days": 14
  },
  "connectors": { "reviews": "github", "tasks": "beads" },
  "sdd": { "mode": "remind", "dir": "specs" }
}

connectors pins which registered connector serves a facade when several could — e.g. a self-hosted GitHub Enterprise or GitLab instance. Remote-URL matching auto-detects github.com and gitlab.com; anything else (GHES, self-hosted GitLab, a future Jira connector) resolves through this map.

sdd opts the repo into spec-driven development: remind nudges via session/prompt hook context when a claimed bead lacks specs/<id>.md (or a spec: link), gate also lets the stop gate block once. Commit the section — it then applies to every agent in the repo.

For the loop, drive, agents, learn, sdd, check, drill, stack, and beads sections, see the configuration reference.

stores lists the backends debt writes to. jsonl is the evidence ledger (always written — drop it and bro adds it back). beads is on by default: the ledger alone is a log — the beads projection is the queue (bd ready -l debt, drill frames, bro next). A normal collect auto-runs bd init --stealth --skip-agents --skip-hooks when a repo is missing .beads (skipped by --dry-run, --list-only, and an empty ledger) — announced on stderr, never silent — and projects every record into bd. Stealth means local-only: .beads lands in .git/info/exclude, nothing is committed, rm -rf .beads undoes it. Opt out with an explicit "stores": ["jsonl"]. Requires bd installed; a missing bd fails the run after evidence is written.

The ledger dir is machine-local state too — bro adds it to .git/info/exclude on first write so harvest evidence can't be committed by accident.

debt.sources picks which collectors bro debt collect runs. The default is ["review-threads"] — the original merged-PR sweep. Opt-in sources feed the same ledger and beads projection (debt:<source> labels, thread_id is the source's stable key):

SourceFeeds on
review-threadsUnresolved review threads on merged PRs (default)
dependabotOpen Dependabot alerts — skipped when an open dependabot/* PR already covers the dependency
code-scanningOpen code-scanning alerts (rule + file ref)
secret-scanningOpen secret-scanning alerts — always blocking priority
stale-prsOpen PRs idle > debt.stale_days days or failing checks (WIP drafts don't count)
failed-ciLatest default-branch workflow run, if it failed
sonarcloudOpen SonarCloud issues + TO_REVIEW hotspots on new code (SONAR_TOKEN; project key from debt.sourceConfig.sonarcloud.project_key or sonar-project.properties). Skips findings a Sonar PR comment already covered
codacyCurrent repo issues from Codacy API v3 (CODACY_API_TOKEN or repo-scoped CODACY_PROJECT_TOKEN; debt.sourceConfig.codacy.{host,provider} for self-hosted/non-GitHub). Skips findings a Codacy PR comment already covered
codesceneFailed quality gates + per-file code-health declines from stored delta analyses (CODESCENE_TOKEN; project id from debt.sourceConfig.codescene.project_id or CODESCENE_PROJECT_ID). Skips findings a CodeScene PR comment already covered

Alert sources reconcile both ways: a finding that disappears upstream is marked done in the ledger on the next collect.

Labels bro manages

LabelMeaning
debt:collectedSwept, findings in the ledger
debt:cleanSwept, nothing found — bro won't look twice
debt:skippedYou told bro to chill. bro chills.

Philosophy

bro doesn't fix your code. bro doesn't write essays in your PRs. bro collects what's owed, keeps the books clean, and waits. bro got you.

Dev

git clone https://github.com/theplenkov/bro
cd bro && npm install
npm run build && npm test

Workspace packages live in packages/* (@broject/core, @broject/debt, @broject/act, the CLI itself). Skills in skills/ are thin wrappers — all mechanics are in the CLI. Nx inference comes from the published @nx-devkit/* plugins.

Releasing

CI-driven, human-gated. Actions → Release → Run workflow:

  • specifier — auto derives the bump from conventional commits since the last v* tag (feat: → minor, fixes → patch, BREAKING → major, 0.x shifted), or pick patch / minor / major / prerelease.
  • preid — set (e.g. beta) to cut vX.Y.Z-beta.N instead.
  • dryRun — prints the nx release plan, changes nothing.

The workflow runs nx release version on the main checkout, commits the bump to a release/vX.Y.Z branch, and opens a PR. Merge it → release-tag.yml cuts the v* tag + GitHub release on main and dispatches publish.yml, which ships to npm via OIDC trusted publishing. No tokens, no manual tags. release-tag.yml can also be run manually (Actions → Tag Release) to catch up a version that predates the pipeline.

MIT. PRs welcome — bro reviews them anyway.