CLAUDE.md · git:20260915.009ffba · 2026-09-15 · sha256 27be11b52abaa0b2

CLAUDE.md git:20260915.009ffbaA

Immutable. This exact content is served forever at /api/v1/blob/27be11b52abaa0b2.

# flowpane

A Claude Code plugin that replaces the default Workflow progress list with a
live drawing of the run: a phase-by-phase graph, a stack, or a timeline in the
pane beside the transcript, with each agent's state, timing, token spend, tool
calls and prompt.

It draws into a terminal grid. Everything it knows comes from files a running
workflow writes, and everything it says has to fit in cells.

## Commands

```bash
claude plugin test .          # the test suite — 99 tests across 11 files
bunx tsc --noEmit             # typecheck (hooks/ and types/ only; see tsconfig.json)
```

Use `claude plugin test .`, not `bun test`. The tests import
`claude-code/testing`, which the plugin test runner supplies and a bare `bun
test` does not; bare `bun test` fails to resolve it.

The developer tools under `dev/` all take a run and draw it without a session:

```bash
bun dev/preview.ts [--animate|--journal <dir>|--settings|--menu theme|--about|--plain]
bun dev/shot.ts [--journal <dir>] [--port 8731]   # the pane in a browser, live, at any size
bun dev/stress.ts                                  # wide fan-outs and a narrow body
bun dev/lines.ts                                   # every line of every run at every size
bun dev/audit.ts                                   # every run, layout and size: nothing throws, every cell legible
bun dev/checkpic.ts <dir>                          # the bands cover the canvas, every node has a Button
bun dev/contrast.ts                                # every theme's roles against its ground, its edges against its states
bun dev/edges.ts <dir>                             # the graph derived for a run, and how it was derived
bun dev/recover.ts <sessionId> [home]              # what a session's runs rebuild to
bun dev/dryrun.ts <workflow>                       # a workflow's control flow, stubbed, no agents spent
bun dev/checkmeta.ts                               # the version the pane shows is the one the manifest ships
```

`dev/load.ts` is not a command — it is the run-building library the others share.
`dev/drive.py` gives Claude Code a pty and captures a real session (`python3`,
not `python`).

Real runs to draw live under
`~/.claude/projects/<project-slug>/<sessionId>/subagents/workflows/wf_*`.

Before calling any of this done: `bunx tsc --noEmit`, `claude plugin test .`,
`bun dev/lines.ts`, `bun dev/audit.ts`.

## Layout of the repo

| Path | What it holds |
| --- | --- |
| `hooks/register.ts` | The plugin itself: every hook, the journal tail, the run state |
| `hooks/journal.ts` | Reading a run off disk — journal, run file, agent transcripts |
| `hooks/shape.ts` | The run's shape: phases, passes, what fed what |
| `hooks/tree.ts` | Rows and segments for the timeline |
| `hooks/layout.ts` | Where every band, card and column goes, at a given size |
| `hooks/paint.ts` | Everything drawn: cards, wires, header, dialogs, settings |
| `hooks/canvas.ts` | The grid: cells, colour mixing, line drawing, joints |
| `hooks/theme.ts` | Twelve themes, each ten colours, and the thirteen roles derived |
| `hooks/press.ts` | Keys and clicks |
| `hooks/about.ts` | What the pane says about itself: the dialog and `/wf about` |
| `tests/` | What the pane draws, read back off the canvas |
| `dev/` | The tools above |
| `docs/` | The design record: every decision, and what it replaced |
| `README.md` | What the plugin is, installing it, and the way in to `docs/` |

## Constraints the platform imposes

These are not preferences. Breaking one produces a pane that looks right in a
test and wrong in a terminal.

- **A `Button` carries no colour and no background.** `ButtonProps` has `key`,
  `label`, `hotkey`, `action`, `plain` and `hover` — no colour among them. A
  state mark, a rule or a fill drawn inside a Button's cells comes out in the
  label's own plain text. So every coloured cell stays outside every hotspot: the mark sits beside the
  name with a cell of air between them, and a card is framed in code points
  rather than filled.
- **Every dialog is modal.** There is no layering below one. Anything that has
  to be readable while a dialog is open is drawn into the pane, not under it.
- **A `Raster` is blitted, an element tree is re-rendered.** `ui.blit` repaints
  a mounted Raster at 120 ms a frame; a redraw through `ui.render` costs a
  render and runs at 320. Animation belongs in the Raster.
- **The pane's size is given, not asked for.** Every drawing decision takes the
  width it is handed. Nothing may assume a minimum beyond what `layout.ts`
  already guards.

## Rules the drawing keeps

- **Colour says one of two things: what state a node is in, or that a line is a
  wire.** Structure is grey. The one border allowed to carry a state is a
  card's own frame, which encloses exactly one agent — see `frameOf`.
- **No line crosses a word.** `canvas.line()` refuses to overwrite a port, and
  `dev/lines.ts` checks the whole corpus: every line lands on a blank cell,
  every arrowhead has a stem, every arm reaches something.
- **A measurement keeps its unit where the unit fits.** Token counts run down a
  ladder — `∑ 15.1k tkns`, `∑ 15.1k`, `15.1k` — and a lane picks one spelling
  for all of its cards, so two cards are never compared in two units.
- **The header gives way in a fixed order.** Each group has a `drop` rank and
  its own `short` ladder; the highest rank gives way first.
- **A finished run stops moving.** A run that is not `running` is painted at
  `tick = -1`, which freezes every animation: no spinner, no sliding label, no
  travelling light.

## Conventions

- **Comments say why, not what.** Most of what is hard here is a decision that
  replaced something worse, and the comment is where the replaced thing is
  recorded. `paint.ts` is long because of this and that is the intent.
- **Prose written to a file is normal English** — comments, `docs/`, commit
  messages. No abbreviations invented for the file, no notes to the reader
  about the session that produced it.
- **`docs/` is the design record, and it is kept current.** A change that
  alters what a reader sees changes the page under `docs/` that covers it, in
  the same pass — `docs/README.md` says which page that is. The root `README.md`
  is the introduction: what the plugin is, how to install it, and links in.
- **Tests read the canvas.** They paint a run and assert on the cells, so a
  test says what a person would see. Keep the fixture minimal and name the test
  as a sentence about the drawing.