npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uiid/bertrand

v0.13.0

Published

Multi-session workflow manager for Claude Code.

Downloads

675

Readme

bertrand

Multi-session workflow manager for Claude Code. Tracks concurrent Claude Code sessions, captures their timelines into a local database, and surfaces them in a dashboard for review.

Status: TypeScript rebuild. The previous Go release (v0.9.1) is still on Homebrew but no longer developed against this branch.

How it works

Bertrand wraps Claude Code with two pieces:

  1. A system-prompt contract that tells the agent to call AskUserQuestion every turn with concrete, actionable options.
  2. Claude Code hooks that observe tool calls and lifecycle events (PreToolUse, PostToolUse, UserPromptSubmit, Stop, PermissionRequest) and write structured events into a local SQLite database.

The agent never knows bertrand exists. Hooks fire, scripts call bertrand update, rows land in the DB, the dashboard reads them.

When a session calls AskUserQuestion, bertrand marks it waiting, sets a Wave Terminal badge, and sends a notification. When you answer, the badge clears and the session moves to active.

Prerequisites

  • Claude Code--append-system-prompt and hooks support required.
  • Bun ≥ 1.3 — runtime for bertrand and the dashboard.
  • Wave Terminal — optional, but the only supported terminal for badges/notifications. Without it, bertrand still tracks state; you just don't get focus management.

Install

bun i -g @uiid/bertrand    # or: npm i -g @uiid/bertrand

The first run of bertrand auto-runs init; you can also invoke bertrand init explicitly.

From source

git clone https://github.com/uiid-systems/bertrand.git
cd bertrand
bun install
bun run src/index.ts init

Invoke via bun run src/index.ts <command> while developing.

Setup

bertrand init

This:

  1. Creates ~/.bertrand/ with config.json and bertrand.db.
  2. Installs hook scripts to ~/.bertrand/hooks/.
  3. Registers them in ~/.claude/settings.json.
  4. Writes shell completions to ~/.bertrand/completions/.

Re-run init whenever bertrand updates — hook scripts are versioned and may need refreshing.

Usage

Launch a new session

bertrand

Opens an Ink TUI to type a session name (e.g. bertrand/fix-recap-render, frontend/ENG-142-auth). Slashes nest the session under group folders. Claude Code launches with the bertrand contract applied.

Resume

bertrand <group/session>

Shows a picker: start a fresh Claude conversation, or resume one of the prior conversations on this session. Either way, bertrand re-injects the session timeline and any sibling-session context.

List

bertrand list

Interactive picker showing all sessions with status badges.

Other commands

| Command | Purpose | |---|---| | bertrand log <session> | Print the timeline event log for a session. | | bertrand stats <session> | Print materialized stats (duration, work/wait split, lines changed). | | bertrand archive <name> | Archive or unarchive a session. | | bertrand serve | Start the dashboard HTTP API on :5200. | | bertrand backfill-stats | Re-compute stats for older sessions after schema changes. | | bertrand update | Hook-facing event writer. Internal — don't call directly. |

Dashboard

A Vite + React + TanStack Router app at dashboard/. Renders timelines (assistant text, thinking, code diffs, permissions, Q&A pairs, context snapshots), engagement stats, and a session sidebar.

Run both the API and the dev server:

cd dashboard
bun run dev

This spawns bertrand serve (API on :5200) and vite (dashboard on :5199). Visit http://localhost:5199. The dashboard proxies /api to :5200.

The dashboard is currently dev-mode only. There's no production build/serve path yet.

Session states

| Status | Meaning | |---|---| | active | Agent is generating a response. | | waiting | Agent called AskUserQuestion, blocked on user input. | | paused | Session ended (Claude Code exited). | | archived | Manually archived; hidden from default views. |

Focus management (Wave)

When a session enters waiting, hooks call wsh badge to mark the block tab and wsh notify to send a notification. When it returns to active, the badge clears.

Other terminals fall back to no-ops; the session state is still tracked.

Architecture

Claude Code hook  →  ~/.bertrand/hooks/*.sh  →  `bertrand update --event …`  →  SQLite (events table)
                                                                                       ↓
                                                                             /api/events/:sessionId
                                                                                       ↓
                                                                              dashboard timeline

Key tables (src/db/schema.ts):

  • groups — nestable session containers.
  • sessions — named workspaces, status-tracked.
  • conversations — Claude conversations within a session (claude_id UUIDs).
  • events — every hook firing and lifecycle moment (session.waiting, session.answered, tool.applied, context.snapshot, session.recap, etc.). Free-form meta JSON column.
  • session_stats — materialized stats, refreshed at session end.
  • worktree_associations — tracked worktree branches per session.

Stats are computed live for active/waiting sessions and read from the materialized row otherwise — see src/server/index.ts.

File layout

Repo

src/
  cli/         # Command router and command handlers
  contract/    # System-prompt contract (AskUserQuestion loop, sibling context)
  db/          # Drizzle schema, migrations, query functions
  engine/      # Session lifecycle (launch, resume, finalize)
  hooks/       # Hook script generation (bash templates)
  lib/         # Timing FSM, diff stats, engagement, formatting, tests
  server/      # Bun HTTP server (/api/*)
  terminal/    # Terminal adapters (Wave, Noop)
  tui/         # Ink-based TUI screens
dashboard/
  src/
    api/         # Typed TanStack Query hooks
    components/  # Timeline content renderers, sidebar, markdown
    lib/         # Event categories, transforms, formatting
    routes/      # TanStack Router pages
schema/        # Drizzle migration SQL

Runtime

~/.bertrand/
  config.json                     # Terminal + bertrand settings
  bertrand.db                     # SQLite (sessions, events, stats)
  hooks/
    on-waiting.sh                 # PreToolUse AskUserQuestion → session.waiting
    on-answered.sh                # PostToolUse AskUserQuestion → session.answered
    on-active.sh                  # PreToolUse catch-all → session.active
    on-permission-wait.sh         # PermissionRequest → permission.request
    on-permission-done.sh         # PostToolUse catch-all → permission.resolve
    on-user-prompt.sh             # UserPromptSubmit → user.prompt
    on-done.sh                    # Stop → session.paused
  completions/                    # Shell completion scripts

Development

bun run typecheck       # Type-check src/
bun test                # Run backend tests
bun run db:generate     # Generate Drizzle migration after schema change
bun run db:migrate      # Apply migrations to ~/.bertrand/bertrand.db

The dashboard has its own tsc -b typecheck — run from dashboard/.

Releasing

Releases are driven by release-please from conventional commits on main.

  1. Land commits in conventional format (feat:, fix:, refactor:, etc.). Hidden types — chore, docs, test, ci — don't trigger a release.
  2. The Release Please workflow opens or updates a release PR with the next version + CHANGELOG.md entries.
  3. Merging the release PR creates the git tag and a GitHub Release.
  4. The same workflow then publishes to npm with provenance (typecheck + tests run first).

The .release-please-manifest.json file tracks the last released version; release-please updates it automatically. Publishing relies on the NPM_TOKEN repo secret (npm Automation token).

License

MIT