claude-code-weaverbird

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: MIT

README

weaverbird

A statusline multiplexer for Claude Code. Claude Code exposes exactly one statusLine.command. weaverbird is that command. It reads the session JSON once, gathers structured records from many independent provider programs that do not know about each other, and weaves them into one status bar, owning color, ordering, separators, width degradation, and staleness itself.

Providers emit structured data and a semantic class, never pre-styled ANSI, so a bar assembled from tools that were never coordinated still reads as one coherent, themeable system.

The problem it solves

Claude Code allows a single statusline command, so tools that each want to be on the bar cannot coexist. The author has three that do: bloodhound (subscription quota), usage-gov (pacing alarm), and pigeon (inter-session messages). Only one can own the slot, and the other two go dark. weaverbird owns the slot once and composes all of them, plus anything else you drop in.

How it works

A provider publishes two things, the way a REST service publishes an OpenAPI document and endpoints:

  • a spec: a small capability document declaring the widgets it offers and their static metadata (id, priority, icon, thresholds, cache policy);
  • value records: one JSON line per widget with the current value, produced on demand.

weaverbird reads the spec once, then fetches values per render, in parallel, with a per-provider timeout and a last-known-good cache, and it only re-invokes a provider when that provider's declared cache policy says something changed. It maps class to color from one theme, lays the widgets out, and degrades gracefully when the terminal is narrow.

Vocabulary: provider (a program or file that supplies data), widget (one addressable unit, identified by an id like bloodhound.week), spec (a provider's capability document), value record (one widget's current value).

Install

weaverbird is a single Go binary.

make install                 # builds and installs the weaverbird binary
weaverbird install           # claims the statusLine slot in ~/.claude/settings.json (backs it up first)

weaverbird install also creates ~/.claude/weaverbird/providers/ and sets a refreshInterval so that time-based and alarm providers stay live while a session idles. Run weaverbird doctor at any time to confirm the slot is still owned by weaverbird and to see the discovered providers and their health.

Adding a provider

Drop a file into ~/.claude/weaverbird/providers/. That is the entire registration; no shared config is ever edited. An entry is one of:

  • a descriptor NN-name.provider.json that declares how to fetch the spec and values (an exec command you choose, a frozen file, or an inline document);
  • a bare executable that reads the session JSON on stdin and prints value records (the specless shortcut, for a quick script);
  • a *.ndjson file that some daemon writes on its own cadence.

Disable one with weaverbird disable <name> (it appends .disabled); re-enable with enable.

The full contract is SPEC.md. A cookbook of every form in shell, Python, and Go is EXAMPLES.md. Machine-readable JSON Schemas are in schema/.

Writing a provider in Go

Import the provider helper package (pure standard library, no cobra required). It carries the wire types and lifts the whole subcommand into one call:

import wb "github.com/PeterSR/claude-code-weaverbird/provider"

var spec = wb.Spec{V: 1, Provider: "bloodhound", Icon: "🩸", Widgets: []wb.Widget{
    {ID: "bloodhound.week", Title: "Weekly quota", Priority: 20,
     States: map[string]float64{"warn": 70, "danger": 90}, Cache: &wb.Cache{TTLSec: 10}},
}}

func value(s wb.Session, requested []string) ([]wb.Value, error) {
    // reuse your tool's existing data; return one record per widget, omit to stay silent
}

// inside your tool's `weaverbird` subcommand:
//   return wb.Dispatch(args, os.Stdin, os.Stdout, spec, value)

See EXAMPLES.md section 9 for the full subcommand.

The bundled providers

providers/ in this repo holds ready descriptors for the author's three tools, bloodhound, usage-gov, and pigeon. Each of those tools grows an additive weaverbird subcommand built on the provider library, so their spec and values come from the same structured data they already compute. weaverbird depends on none of them: it is equally happy with just one, or with providers you write.

Theming

Color comes from a theme: a name plus a light and a dark palette, each mapping a widget's class to a color. Two themes ship in the binary: vanilla (the default, the classic 16-color ANSI mapping, byte-identical to weaverbird's original look) and teal (a truecolor theme with distinct light and dark palettes). Drop your own at ~/.claude/weaverbird/themes/<name>.json:

{
  "name": "sunset",
  "light": { "warn": "#e2892a", "danger": "#d1483a" },
  "dark":  { "warn": "#f0972f", "danger": "#e2564a" },
  "separator": " | ",
  "provider_separator": " -- "
}

light/dark map class to a hex color, a 256-color index, or a named ANSI color; a user theme of the same name overrides a built-in. separator and provider_separator are both optional, the plain text rendered, dim, between widgets: separator joins two adjacent widgets from the same provider (a "run"), defaulting to two spaces; provider_separator joins two adjacent widgets whose provider differs (a run boundary), defaulting to a middle dot (" · "). The two default independently and neither falls back to the other, so a theme (both built-ins included) that sets neither still gets both defaults, and a theme may override either one, both, or neither. Both are theme-level only, there is no config.json override for either, and whichever one applies at a given gap counts against the width-degradation cascade like everything else on the bar.

Which theme and appearance are active is a pointer in ~/.claude/weaverbird/config.json (WEAVERBIRD_THEME/WEAVERBIRD_APPEARANCE override it for one process), changed with weaverbird theme use <name> and weaverbird theme appearance <auto|light|dark>, never by hand-editing colors. auto appearance resolves to light or dark from, in order: the env override, the config pointer, Claude Code's own ~/.claude/settings.json theme, $COLORFGBG, else dark. weaverbird theme list and weaverbird theme show [name] inspect the catalog. Full detail, including the exact appearance resolution order and the color value formats, is SPEC.md section 6.1.

To see a theme applied without waiting on a real Claude Code session, use weaverbird preview (below), which renders through the exact same theme and layout engine as the real bar.

Layout

By default weaverbird renders every enabled provider's widgets, in provider order, except any a provider marked "default": false: an opt-in, group-only widget that exists but does not clutter the bar unasked (handy for a chatty or niche widget most people never want to see). For coarse control over what goes on the bar and in what order, including pulling an opt-in widget in, a provider can declare named groups (curated bundles of its own widgets, e.g. bloodhound.quota), and you can point ~/.claude/weaverbird/config.json's layout at an ordered list of tokens:

{ "layout": ["bloodhound", "usage-gov.pace", "pigeon"] }

Each token is, in precedence order, a group id, a widget id, or a provider name (that provider's own implicit default group, every widget it declares, opt-in ones excluded). When layout is set, it is strict: only the widgets it resolves to render, in that order; anything not referenced is left off the bar, opt-in or not. An opt-in widget is still selectable, overriding its own default, either by naming its widget id directly or through a group that includes it. When layout is absent, the default view above applies. Manage it with weaverbird layout show (prints the resolved layout, by row, plus opt-in widgets available to add, what a strict layout omits, and any unknown token), weaverbird layout set <token...>, and weaverbird layout clear. Full detail, including the exact token precedence, is SPEC.md section 6.2 (and section 3.2 for opt-in widgets).

Commands

weaverbird                        read the session JSON on stdin and print the bar (the statusLine command)
weaverbird install                claim the statusLine slot (backs up settings first)
weaverbird doctor                 verify the slot is owned; list providers, widgets, and health
weaverbird list                   list discovered providers and their widgets
weaverbird enable <name>          re-enable a provider
weaverbird disable <name>         disable a provider without deleting it
weaverbird preview                render the bar in a normal terminal, outside a Claude Code session
weaverbird theme list             list built-in and user themes, marking the active one
weaverbird theme show [name]      print a theme's light and dark palettes
weaverbird theme use <name>       set the active theme
weaverbird theme appearance <auto|light|dark>   set the appearance pointer
weaverbird layout show            print the resolved layout: what renders, what's omitted, unknown tokens
weaverbird layout set <token...>  set the active layout
weaverbird layout clear           remove the layout, returning to the default (every widget)
weaverbird version                print the version

weaverbird preview runs the real render pipeline outside a session: by default it synthesizes a session payload from your environment (cwd, $CLAUDE_CODE_SESSION_ID or a stable placeholder, your real terminal width) and drives your actual providers, so you see your actual current bar. --input FILE (or - for stdin) replays a captured statusline payload instead. --demo bypasses the real providers and renders a fixed, clearly-synthetic widget set that exercises every class, the icon dedup, and the width cascade, so a theme can be fully evaluated even when real provider quota is low. --width, --theme, --appearance, and --layout (comma-separated tokens) override those settings for that one render only, without touching config.json.

Prior art

weaverbird's contract deliberately recombines established conventions rather than inventing a format: the class and states model from Waybar, text/short width degradation and min_width from the i3bar protocol, the declaration-versus-render split and the caching and truncation rules from linesmith, the structured drop-in record from snackdriven/claude-statusline, manifest discovery and graceful degradation from betmoar/cc-status-plugin, and the ok/warning/critical threshold convention from Nagios and Grafana. SPEC.md credits each in detail.

Scope

This is built to solve one real problem well, the author's three tools sharing one bar, and to be useful with a single provider and nobody else's. It is not framed as a standard. If the contract is clean enough that others find it worth implementing, that is earned later, not declared here.

Directories

Path Synopsis
cmd
weaverbird command
Command weaverbird is a statusline multiplexer for Claude Code: it reads Claude Code's session JSON once from stdin, fans it out to independent provider programs, and weaves the widgets they emit into one themed, width-aware status bar.
Command weaverbird is a statusline multiplexer for Claude Code: it reads Claude Code's session JSON once from stdin, fans it out to independent provider programs, and weaves the widgets they emit into one themed, width-aware status bar.
examples
quota command
Command quota is a tiny, complete weaverbird provider built on the public provider helper library.
Command quota is a tiny, complete weaverbird provider built on the public provider helper library.
internal
cache
Package cache implements weaverbird's session-keyed on-disk state: the cached spec per provider (fetched at most once per session) and the per-widget value cache that drives both the cache-policy gate (do not re-invoke within ttl_sec, or until invalidate.file changes) and the last-known-good fallback for a failing provider (SPEC.md section 5).
Package cache implements weaverbird's session-keyed on-disk state: the cached spec per provider (fetched at most once per session) and the per-widget value cache that drives both the cache-policy gate (do not re-invoke within ttl_sec, or until invalidate.file changes) and the last-known-good fallback for a failing provider (SPEC.md section 5).
ccinput
Package ccinput reads and leniently parses the JSON Claude Code pipes into its statusLine.command on stdin.
Package ccinput reads and leniently parses the JSON Claude Code pipes into its statusLine.command on stdin.
engine
Package engine is weaverbird's hot path: for every enabled discovered provider it resolves the spec (cached, at most fetched once per session), applies the per-widget cache-policy gate, invokes the value source only when something is due, and merges the result into layout-ready widgets with class and icon already resolved.
Package engine is weaverbird's hot path: for every enabled discovered provider it resolves the spec (cached, at most fetched once per session), applies the per-widget cache-policy gate, invokes the value source only when something is due, and merges the result into layout-ready widgets with class and icon already resolved.
grouplayout
Package grouplayout resolves the user's optional layout (SPEC.md section 6.2): an ordered list of tokens, read from config.json, each a provider-declared group id, a widget id, or a provider name (that provider's own implicit default group, SPEC.md section 3), that together decide which widgets render and in what order.
Package grouplayout resolves the user's optional layout (SPEC.md section 6.2): an ordered list of tokens, read from config.json, each a provider-declared group id, a widget id, or a provider name (that provider's own implicit default group, SPEC.md section 3), that together decide which widgets render and in what order.
layout
Package layout owns ordering, rows, icon placement, the width-degradation cascade, OSC 8 href wrapping, and joining widgets into the final bar text (SPEC.md section 6).
Package layout owns ordering, rows, icon placement, the width-degradation cascade, OSC 8 href wrapping, and joining widgets into the final bar text (SPEC.md section 6).
registry
Package registry finds providers in the drop-in providers directory, classifies each entry (an explicit descriptor, a bare executable, or a bare records file), resolves its spec and value sources, and orders the result: the stable left-to-right base position for the bar (DESIGN section 5, SPEC.md section 2).
Package registry finds providers in the drop-in providers directory, classifies each entry (an explicit descriptor, a bare executable, or a bare records file), resolves its spec and value sources, and orders the result: the stable left-to-right base position for the bar (DESIGN section 5, SPEC.md section 2).
theme
Package theme owns class -> color: the theme catalog (the built-in vanilla and teal themes plus any user theme dropped into the themes directory), the light/dark appearance resolution, NO_COLOR handling, and the separator between widgets (SPEC.md section 6).
Package theme owns class -> color: the theme catalog (the built-in vanilla and teal themes plus any user theme dropped into the themes directory), the light/dark appearance resolution, NO_COLOR handling, and the separator between widgets (SPEC.md section 6).
Package provider is the public contract library for weaverbird provider authors.
Package provider is the public contract library for weaverbird provider authors.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL