Open source · MIT · Python 3.11+
Vibe Board spends the hours you are not awake for. Point it at any repository: it turns a plan into tickets, launches one agent per ticket into its own git worktree, tests every branch before it merges, and waits out rate limits by itself instead of stopping.
The problem
You hit the limit at 4pm. The window reopens at 9pm, and by then you are not there. The next morning you relaunch by hand and re-explain the project to a fresh agent whose context was cut.
The subscription is paid for whether or not anyone is awake to use it. The gap between what you bought and what you can actually consume is not a model problem — it is an orchestration problem, and it is the one this project exists to close.
Press Finish the project and the board works the backlog until nothing is launchable — then tells you honestly whether that means done, blocked, or waiting on your review.
A limit is not a failure — it is a known future timestamp. When every account is cooling, the supervisor sleeps until the earliest reset and resumes by itself. That single behaviour is the largest recovery of otherwise-idle plan hours the board makes.
Every transition is written to .vibe/runtime/autopilot.json
before it is acted on, so a supervisor killed by a closing lid is
resumable — and "what was it doing" is a file read. It also holds the
machine awake while a run is in flight, and lets it sleep again the
moment it goes idle.
Retries are bounded, waiting is bounded, and a board with nothing launchable ends the run rather than spinning. With no test command configured nothing is merged at all — every branch parks for review, and autopilot says so before it starts rather than after.
Desktop notification, webhook, or an appended log — any combination, per project. The webhook is the one that reaches someone who has walked away, which is the case autopilot exists for; the log is the only one that leaves a record.
What you get
Everything below is a screenshot of the running board, not a mockup.
One agent per ticket, each in its own worktree and branch. Auto mode runs to completion; duo mode parks and waits for you. Runs survive a crash, because every run is a file on disk.
Cost, tokens, duration and outcome for every run — and a warehouse that merges every project into one local SQLite file, so "what did last week cost across all of them" is a query, not ten dashboards. Reported costs are used verbatim and labelled as reported; the rest is labelled an estimate.
Every agent branch with its diff and its commits. The suite runs on that branch's own worktree, and merging refuses rather than forces — no conflicted mess left in your index.
Rate limit, permission denial, crash, or a real test failure — classified, not dumped, with the ones the board can safely fix marked as such. Agent blockers arrive as a queue you can act on, not as text buried in a log.
Brief, spec, architecture, sprints, tickets, agent roles, protocol. From a document, or from one sentence. The transformation is a versioned prompt library, so the same input regenerates the same structure. No source document? The intake stage is told so, and raises open questions instead of inventing answers.
A worktree, a branch and its own port range per slot. Advisory locks serialise the genuinely shared files, so two agents never believe they own the same code.
Plug in token-compression tools per project. The board verifies they actually engaged before crediting them with anything — an addon that could not engage is not recorded on the run it did not touch.
Run the suite from the board, and script Playwright walkthroughs that drive the running app, record a video and assert on what they see. The spec is the recorded claim that a ticket works; the result is whether it still does. Requirement coverage is tracked from the ticket IDs.
The three ideas behind it
Board, tickets, run records, locks. No database, no daemon that must have been running. Kill the server mid-run and a fresh process reads the files back and knows exactly what was in flight, what stalled, and what is waiting on a human.
Python 3.11 and the standard library. It boots on a bare machine. The UI is one HTML file — no CDN, no bundler, not a single network request to render the page you are looking at.
A compression tool that failed to engage is not credited on the run it did
not touch. A board whose remaining tickets are all blocked is reported as
blocked, not done. Only done means done.
How parallelism stays safe
Every ticket declares a role, a
lane, and the
files it owns. A lane is one
agent's serial queue; files_owned is an exclusive claim.
Containment counts, not just equality — a ticket owning
api/app/core/ collides with one owning
api/app/core/config.py. This is the check that stops the most
expensive failure a parallel fleet has: two agents editing the same file,
one silently losing its work at merge time.
# non-zero exit on a real conflict vibe-board check-overlap myproject S1 S2 # a lane is one agent's serial queue # files_owned is an exclusive claim # containment counts, not just equality
When an agent cannot proceed without touching something it does not own, it writes a blocker rather than widening its own scope — and the board shows those blockers as a queue, not as buried text in a log.
Accounts and models
No API key, no separate billing relationship. Runs bill to whichever coding CLI is already signed in — and if you keep more than one login, the board routes around whichever one is tired.
Two directories can share one login. Scheduling two concurrent runs onto
them is scheduling two runs onto a single quota pool — and you find out
when both stall. The board groups by the login's own
accountUuid, read from each config directory, never by
directory path.
A run can be pinned to a specific account, so work continues on a second plan when the first hits its limit. Accounts can be detected, added by hand, enabled, disabled, or marked as cooling down.
No provider exposes remaining quota. The board does not pretend otherwise — it tracks what it can actually observe, and says a limit was hit rather than predicting one.
dev here is one account reached by two directories and two
launchers — grouped, so the board never schedules two runs onto it
believing they are independent.
vibe-board accounts detect vibe-board accounts disable claude2 vibe-board accounts set-status claude1 cooling_down
| Driver profile | Drives |
|---|---|
claude | Claude Code CLI against Anthropic |
claude-compatible | Same binary, another compatible endpoint |
kimi | Kimi CLI, cost estimated |
generic | Any other CLI, via an argv template |
Kimi's credentials file is detected but never read — that is exactly the kind of file this tool should not be pulling into anyone's context.
The orchestrator
Press Briefing and the orchestrator answers from the board, the run records and the autopilot state as they are at that moment — not from a transcript of everything you have ever said to it. That keeps the answer accurate, and keeps it cheap: the same price on the two-hundredth question as on the first.
Per-ticket chats are scoped to their ticket and thrown away when it closes. No conversation on the board grows without bound.
Chat cannot launch anything. The orchestrator runs in plan permission mode: it reads, reasons and recommends, but never edits files or dispatches agents as a side effect of a reply. Launching stays an explicit action. A chat box that can silently mutate the project is a far larger trust surface than one that only talks.
An agent starting on a repository spends its first several turns finding out where things are. Cut that run mid-ticket and the orientation dies with the context — the next attempt buys it again. On a fixed-price plan that re-derivation is often the majority of a short run.
When a ticket closes, what it established goes to
.vibe/skills/: the files it actually changed, the commands
that worked, the notes it left. Later agents whose own
files_owned overlap get those notes in their opening
prompt.
It is extracted from the run's own record, not generated. An accelerator costing a model call per ticket would fail exactly when the quota is exhausted — which is precisely when resuming matters most.
Safe by construction
An agent that can run commands can run any command. A git worktree
isolates which files it edits — it
does not stop curl, rm, or reading
~/.ssh. So run the fleet in a container, where the agent sees your
repository and nothing else.
printf 'VIBE_UID=%s\nVIBE_GID=%s\nPROJECT_DIR=%s\n' \
"$(id -u)" "$(id -g)" ~/code/my-project > .env
docker compose build
docker compose run --rm board add /work/project --name project
docker compose up # → http://127.0.0.1:8000
Unprivileged user holding your uid, all capabilities dropped,
no-new-privileges. Files the agent writes belong to you, not root.
There is a middle setting, too. By default an agent may write files in its
worktree but not run commands; bypassPermissions lets it run
anything. Between them, allowed_tools takes an allowlist — so an
agent can run its tests and its git commands, and nothing else:
[runtime] permission_mode = "acceptEdits" allowed_tools = "Bash(git*) Bash(pytest*) Bash(npm test*)"
Two things the container deliberately does not do. It does not restrict network access — the agent must reach a model API, so it can reach other things too. And it mounts your coding-CLI credentials, because a run has to authenticate as somebody. Both are stated plainly in the security policy rather than left for you to discover.
60 seconds
You need Python 3.11+, git, and a logged-in coding CLI on your PATH —
by default claude. No API key: runs
bill to whichever plan that CLI is already signed into.
git clone https://github.com/troumba-ai/vibe-board && cd vibe-board
alias vibe-board="PYTHONPATH=$PWD/src python3 -m vibeboard"
vibe-board add ~/code/my-project --name myproject
vibe-board init myproject --description "What I'm building, in one sentence."
vibe-board promote myproject
vibe-board serve --project myproject # → http://127.0.0.1:8000
Already have sprint files? Skip init and point the config at them.
No install step, because the core is the standard library and nothing else —
a pip install release is coming, and until it lands the clone above
is the supported path.
The integration surface
A target repository carries a single .vibe/config.toml: where
its board lives, how it names worktrees and ports, which lock keys are its
hot set, which driver and permission mode to use. Written by
vibe-board add and safe to edit by hand — the board preserves
your comments.
Ticket ids do not have to look like S1-T1; the pattern is
yours. Secrets are referenced as ${VAR} from the board's own
environment, so the committed file never holds one.
The project registry lives outside your repositories, at
~/.vibe/projects.toml. Nothing about the board is global
state you have to keep in sync.
vibe-board add · list · remove projects vibe-board serve · status the board vibe-board agent start · list · stop runs vibe-board autopilot start · status · stop vibe-board init · promote idea → plan vibe-board check-overlap before a wave vibe-board accounts list · detect · enable · disable · set-status · add · remove vibe-board addons list · enable · disable · doctor · savings vibe-board telemetry sync · report · query vibe-board demo list · run
Where it is going
Shipped: project registry, agent runtime, initializer, proof runs, telemetry warehouse, multi-account routing, compression addons, container image, and autopilot — unattended completion with quota waiting, skill reuse and notifications.
Machine-readable depends_on, a derived board, per-ticket threads,
and wave computation from the real dependency graph.
Which model runs which ticket, as a named policy: routing by lane, a frontier model gating a cheaper one, escalation on repeated failure.
Cost preview before dispatch, ticket replay from a pinned commit, scope violations enforced as a merge gate.
Expose the board's operations as MCP tools, so an orchestrating agent drives the fleet directly.
Answer "does this tool pay off on my code" from measured runs rather than vendor claims.
Demos run and record today, but their specs are written by the agent that built the ticket, or by hand. Deriving a walkthrough from the ticket and the shipped UI — and keeping it honest as the UI moves — is not built.
The local loop is bounded by one machine being awake and one plan's quota. Neither is a property of the design, and removing them is infrastructure — not a feature withheld from the open-source version.