A terminal UI to see what your Claude Code workflows actually do —
the blueprints, the live runs, and the chat history behind them.
Claude Code's Workflow tool lets an agent fan
out subagents from a script — agent() / parallel() / pipeline() / phase(). Those scripts
live in .claude/workflows/*.js, every execution is journaled under ~/.claude/projects/, and
every conversation is a .jsonl transcript. cwviz reads all three and shows them in one TUI.
Tab cycles three modes:
| Mode | What it shows | Source |
|---|---|---|
| Workflows | The blueprint — phase tree, every agent() call with its agentType / schema / model, parallel/pipeline fan-out, loops. |
.claude/workflows/*.js, parsed with yuku — never executed |
| Runs | Live + historical executions — per-agent state (✓ done · ● running · ✗ error · ◌ queued), the tool each agent is on, tokens, tool calls, duration, result previews. A workflow running right now shows up ● LIVE with its agents reconstructed from their live subagent transcripts. Auto-refreshes. |
wf_*.json journals + live subagents/ tail |
| Sessions | The chat history — full transcript: prompts, replies, tool calls, results, collapsed thinking. | ~/.claude/projects/**/*.jsonl transcripts (lazy-loaded) |
![]() Workflows — static graph |
![]() Runs — live + historical |
![]() Sessions — chat history |
A workflow script tells you what should happen. The run journal tells you what did — which agent stalled, where the tokens went, what each one concluded. cwviz puts the blueprint next to its executions next to the conversation that started them, so you can actually read your own orchestration instead of grepping JSON.
Needs Bun. Then:
git clone git@github.com:umgbhalla/cwviz.git
cd cwviz
bun install
bun link # registers the `cwviz` command on your PATHRun it in any folder that has a .claude/workflows/ directory:
cd ~/some/repo
cwviz # TUI — Tab toggles Workflows ⇄ Runs ⇄ SessionsHeadless modes (no TUI — pipe-friendly):
cwviz --list [dir] # one line per workflow script
cwviz --runs [n] # newest n run journals
cwviz --json [dir] # full static analysis as JSONdir defaults to the current folder and is scanned recursively (skipping node_modules, .git,
worktrees, .deploy). Runs and Sessions are read from ~/.claude/projects (all repos).
Scope it with CWVIZ_PROJECTS — a comma-separated allowlist matched against any path segment
of a workflow's repo or a run/session's project. Useful for privacy or a focused view:
CWVIZ_PROJECTS=ax2,bun cwviz # only the ax2 and bun repos in every modeTab switch mode · ↑↓ / j k select · / or Ctrl-K search · esc clear search · PgUp / PgDn scroll detail · q quit
Mouse (on by default): click a tab to switch mode, click a row to select it, scroll-wheel over the list or detail to scroll.
- Workflows —
yuku-analyzerparses each script into an ESTree AST (new Analyzer().addFile(...).ast), then awalk()visitor pulls out themetablock and every orchestration call. Statically-resolvable strings (literals, no-hole templates,+concats) are read directly; template holes show as·; dynamic fan counts (arr.map(...)) are reported as dynamic, never guessed. No code is run. - Runs — each journal carries a
workflowProgressevent stream (workflow_phase+workflow_agentwithstate,lastToolName,tokens,toolCalls,durationMs,resultPreview). cwviz rebuilds the phase→agent tree from it. In-flight runs are detected from start-writtenworkflows/scripts/<name>-<runId>.jsthat have no journal yet; the list auto-refreshes every 1.5s so a running workflow appears and updates live. - Sessions — transcripts are large, so the list is built from a cheap head-read (title +
metadata) and the full transcript is parsed only when you select a session. The selected
session's
.jsonlisfs.watched — Claude Code appends it per message, so the transcript re-renders in real time and an active session is marked● LIVE. No polling.
The exact on-disk layout, line schemas, and write cadence — reverse-engineered from the Claude
Code binary with bun-demincer and verified empirically
— are documented in docs/INFRA.md.
src/
model.ts analyze.ts discover.ts Workflows: model, yuku analyser, file finder
runs-model.ts runs.ts Runs: model + journal/orphan discovery
sessions-model.ts sessions.ts Sessions: model + transcript parser
ui.ts opentui scene (3 modes, live polling)
index.ts CLI entry
*.test.ts parser + headless-render checks
bun run check # tsc --noEmit
bun test # analyser + run parser + session parser + headless renderThe render test mounts the real scene on opentui's test renderer and asserts against the captured cell grid — no PTY needed.
- opentui — the native (Zig) terminal UI core that powers the interface
- yuku — the Zig JS/TS toolchain whose analyzer parses the workflow scripts
- terminal-control (
termctrl) — drove the real TUI in a PTY to capture every screenshot and the demo above - bun-demincer — extracted the Claude Code binary to map its on-disk infra (see
docs/INFRA.md) - termcast — reference for opentui app patterns
- Bun — runtime; runs the TypeScript directly, no build step
The demo corpus is real: workflows and runs from Bun and a private agent project, browsed live.
MIT © Umang Bhalla



