Started 2026 · Active Go · Bubble Tea · TUI
Beads is an issue tracker that lives in
your repo instead of a web app. Issues are files under version control, and
everything happens through the bd CLI, which makes it a natural fit for coding
agents: an agent can create, claim and close work without anyone building it an
API.
That works beautifully right up until you have fifty issues with a dependency graph and want to know what’s actually going on. There’s no UI, because not having a UI was the point. Loom is the window.

Read-only is the whole idea
Loom never writes to the Beads database, and never will.
The division is that bd is the agent’s tool for managing work and Loom is the
human’s tool for understanding it. Once you accept that, an enormous amount of
difficulty evaporates. No write path means no lock contention with the agent
that’s actively mutating the database, no merge semantics to reimplement, no
schema coupling, and no chance that my dashboard corrupts my tracker while I’m
looking at it.
It also means I never had to answer “should Loom let you close an issue?”, which is the question that would have turned a weekend into a project.
Shelling out on purpose
Loom talks to Beads by running the bd CLI and reading its JSON output. It does
not link the database.
That’s slower than reading Dolt directly, and it’s the decision I’d defend hardest. There was an earlier third-party Beads viewer that linked the storage layer, and it broke when Beads migrated from SQLite to Dolt. The CLI is the stable, documented, versioned interface. The storage engine is an implementation detail that its authors are entitled to change.
So there’s exactly one place in Loom that knows what bd output looks like. If
the JSON shape changes, that’s the only thing I have to touch.
The concurrency model falls out of the same choice. The agent is writing to an embedded database while I’m reading from it, so the reader is the one that has to yield. When Loom finds the database locked it waits its turn instead of telling me something went wrong.
Errors get translated into sentences a person can act on rather than passed through. “No beads project found. Run ‘bd init’ to initialize.” beats an exit status.
What it shows
A dashboard for the shape of the project: status counts, priority distribution, what’s ready to pick up, what’s blocked.

A sortable, filterable issue table. The filter stays small on purpose: any
key:value token filters a field, and anything else is free text matched against
title and ID. So status:open priority:1 auth does what you’d hope.

And a dependency tree, which is the view I actually built the thing for. Everything is keyboard-driven and Vim-flavoured.
Under it
Bubble Tea, Bubbles and Lipgloss, and nothing else. No CLI framework, no JSON library, no database driver.
Two behaviours I made sure of. Typing “q” in a filter box doesn’t quit the program, which is the classic TUI bug, because a view holding a text prompt takes the keyboard away from the global shortcuts while it has focus. And when the dependency graph contains a cycle, Loom names the issues in it rather than reporting that a cycle exists somewhere and leaving me to find it.
Loom tracks its own backlog in Beads, so the screenshots on this page are Loom rendering Loom’s issues.
Getting it
|
|
You’ll need bd installed and a project initialized. Run loom --watch if you
want it to poll while you work.
It went from first commit to a tagged 1.0 in four days, which says more about Bubble Tea and a clear spec than it does about me. It’s had dependency bumps and nothing else since April, and that’s the right amount of activity for a tool that does one thing. I keep it open in a split while agents work, which was the entire brief.