A small collection of Python CLI scripts that augment
bd (the beads issue
tracker) and its Dolt-backed storage. No package, no build step —
each script lives at the repo root alongside a shared bdutils.py
helper and runs with python3 (or uv for the one script that
needs rich).
Clone it, then link the scripts onto your PATH:
git clone https://github.com/deg/beads-utils && cd beads-utils
make install # symlinks into ~/.local/bin
make install PREFIX=~/bin # ...or wherever you keep themThe symlinks point back at the clone, so the scripts still run from it — edit
one and the change takes effect immediately, with no reinstall. Keep the clone
around; make uninstall removes the links again.
Two things make install deliberately doesn't do: it won't put PREFIX on
your PATH (it says so if it isn't), and it won't set up tab completion —
run make completions for that line. Or skip installing entirely and just put
the clone itself on PATH; the scripts run in place either way.
| Script | What it does |
|---|---|
bd-export-csv |
Export the bead database to a flat CSV for spreadsheet review |
bd-dolt-check |
Verify the Dolt data behind a bd repo is actually committed and pushed to its git remote |
bd-dolt-diff |
Preview what a bd dolt push would send: issue-level diff between local and remote Dolt state |
bd-log |
Git-log-style timeline of bead and memory lifecycle events — created, changed, ended (color-coded, auto-paged) |
claude-session-find |
Substring search across ~/.claude/projects/*.jsonl to find old Claude Code sessions |
claude-session-list |
Git-log-style listing of Claude Code sessions (default: current project; -g for all) |
claude-session-rename |
Set a Claude Code session's title from the shell, like /rename, without starting the session |
bd-view |
Pretty-print a single bead with rendered Markdown |
claude-session-report |
Render a Claude Code session as a Markdown discussion transcript |
bd-complete |
Emit completion candidates (bead ids, session uuids) — the helper behind shell tab completion |
Run any script with --help for full usage. Per-script details and
conventions live in CLAUDE.md; see
CONTRIBUTING.md to contribute.
The scripts fall into two families: tools for looking at your beads, and tools for looking at your Claude Code sessions. Either way the theme is the same — the data is already on your disk; these make it pleasant to read.
When an issue carries a long description — design notes, acceptance
criteria, nested lists — bd show prints the raw Markdown as one long
wall of text. bd-view beads-utils-s4s (that's an issue id) renders it
instead: real headings, real code blocks, dependencies and comments
included, paged like git log. Field coverage is a superset of bd show's — labels, external refs, the metadata dict, parent and children
each shown as themselves — and anything bd grows later lands in a
trailing Other Fields section rather than silently disappearing.
Agentic coding changes what an issue tracker has to answer. A Claude
session can create, claim, and close half a dozen beads while your
attention was on the code, and afterwards you want to know what it
actually did. bd-log shows the lifecycle events — created, started,
closed — newest first, git-log style. bd-log --open narrows to beads
still open: the to-do list the session left behind (--no-deferred drops
the ones parked with bd defer, and --no-blocked the ones waiting on
something else to finish), and bd-log --id <id> --children narrows the other way — one bead, or one epic and
everything under it, from creation to close. --oneline collapses each
event to a single row when you want the shape of the week rather than
the detail of any one entry.
Beads are only half of what a session leaves behind. The other half is
what it learned — the memories bd remember accumulates, which shape
every future session and which nothing otherwise shows you the history
of. bd remember stores no timestamps, so bd-log reconstructs them
from the Dolt commits underneath and folds them into the same timeline:
--about=memories for that history on its own, --about=memories --only=end for the ones that were dropped. Events are a small grid —
--only picks the verb (create, change, end), --about picks
whether it happened to a bead or to a memory — and both default to
everything, so a plain bd-log now answers "what happened here" for
both. This one part needs the dolt CLI; without it the bead half still
works exactly as before.
Sometimes the right reading tool is a spreadsheet — sorting issues for a
triage meeting, or sharing the list with someone who doesn't live in a
terminal. bd-export-csv --sort=-priority,created_at flattens the whole
database to a CSV, presorted before the spreadsheet even opens.
The last pair guards against a quiet failure mode. Beads keeps its data
in Dolt and pushes it to your git remote under refs/dolt/data — a ref
GitHub's UI never shows, so the repo page looks identical whether or not
your issues actually made it to the remote. bd-dolt-check answers
"did they?" — on both counts that matter, since data still sitting in
the Dolt working set is in no commit and therefore in no push. It
reports what is uncommitted and compares the rest against the remote,
exiting non-zero on either (which also makes it a CI gate). bd-dolt-diff
answers the follow-up — what exactly would a bd dolt push send? —
with an issue-level diff: added and removed beads, field-by-field
changes, dependency and comment edits.
claude --resume offers a picker of recent sessions, but the picker is
cramped and can't be scripted. claude-session-list is the long-form
version: the sessions for the current project (or every project, with -g),
each with its full UUID ready to paste, a timestamp range with the
active span, prompt/reply counts, and the session title. --oneline
gives a compact table; claude-session-list -q | head -1 hands a
script the newest UUID.
That covers "which session was most recent"; claude-session-find
covers "which session was it where we discussed the pager?" It greps
the transcripts for a substring and lists the matching sessions with
snippets for context — or, with --oneline, just the sessions and their
hit counts. claude --resume $(claude-session-find -q pager | head -1)
drops you straight back into the conversation.
And when a session turns out to be worth keeping — a design discussion,
a long debugging hunt — claude-session-report renders it as a
Markdown transcript: your prompts, Claude's replies, and (with --all)
the tool calls and every other channel too. The result is a document you can
review at leisure, commit next to the code it produced, or hand to a
colleague who asks "how did you get Claude to do that?"
Titles are what make the picker and these scripts navigable, and
/rename only works from inside a running session. claude-session-rename
sets one from the shell — claude-session-rename <uuid> 'Pager design' —
so an old session can be labelled after the fact, or a batch of them from
a loop, without resuming each one. It writes exactly what /rename would,
and refuses a session that is currently running, where /rename itself is
the right tool.
There's still no package and no build step — but there is a Makefile, so you
don't have to remember any of the invocations. make help lists everything:
make test # run the test suite
make check # ruff, plus a --version smoke test of every script
make ci # everything CI runs
make dolt-check # verify this repo's own beads data is pushed
make screenshots # regenerate the terminal images above
make install # symlink the scripts onto your PATHDependencies resolve through uv into a throwaway environment, so nothing is
installed globally. CI runs these same targets, so a green make ci locally
means a green CI.
The suite in tests/ is fixtures all the way down: bd and dolt
are replaced by programmable fakes on PATH, and Claude session history by
synthetic .jsonl trees under a temporary HOME. Nothing touches a real beads
project or your session history.
Tab completion for zsh and bash lives in completions/.
bd-view completes bead ids (with titles shown in zsh),
claude-session-report completes session uuids/titles, the project-path
scripts complete directories, and every script completes its flags.
Options that take a value complete in either spelling (--id beads-1 or
--id=beads-1), and the comma-separated ones with a known vocabulary
(bd-log's --id, --only, --about and --status;
claude-session-list --sort) complete one element at a time, so
--only=create,ch finishes as --only=create,change.
Source the file for your shell from your rc file:
# ~/.zshrc (oh-my-zsh users: put this *after* `source $ZSH/oh-my-zsh.sh`)
source /path/to/beads-utils/completions/beads-utils.zsh
# ~/.bashrc
source /path/to/beads-utils/completions/beads-utils.bashThe scripts (including bd-complete, which feeds the dynamic candidates)
must be on your $PATH. The file is read when the shell starts, so after
pulling a release that adds a flag, start a new shell — an existing one
still holds the old definitions and will complete nothing for it.
Prefer the autoload convention instead? Drop
beads-utils.zsh into a directory on your $fpath, or beads-utils.bash
into your bash-completion.d.
Released under the MIT License.




