A read-only tmux side panel for Claude Code and the Codex CLI, inspired by oh-my-opencode's sidebar. It shows the things that otherwise scroll out of view — context usage, git state, the live todo list, and a diff of the file Claude just edited — in a pane that's always visible.
┌────────────────────────────┬────────────────────────────────┐
│ claude │ Context │
│ │ 66,518 tok · 33% · $0.46 │
│ > build the thing │ ████████░░░░░░░░░░░░░░ │
│ │ │
│ ⏺ Read(app.ts) │ ▼ Git │
│ ⏺ Edit(app.ts) │ main ↑2 · 3 files │
│ │ M src/app.ts +42 -7 │
│ │ ?? notes.md │
│ │ │
│ │ ▼ Worktrees │
│ │ • main ~/proj │
│ │ • feat/auth ~/proj-auth │
│ │ │
│ │ ▼ Touched (session) │
│ │ ✎ src/app.ts ×4 │
│ │ + tests/app.test.ts │
│ │ │
│ │ ▼ MCP │
│ │ • firebase │
│ │ │
│ │ ▼ Todo │
│ │ [✓] Explore context │
│ │ [»] Implement parser │
│ │ [ ] Write tests │
│ │ │
│ │ ▼ Diff · app.ts │
│ │ @@ -12,3 +12,4 @@ │
│ │ - const x = load() │
│ │ + const x = loadSync() │
│ │ │
│ │ ~/proj · Opus 4.8 │
└────────────────────────────┴────────────────────────────────┘
| Panel | Shows | Source | Freshness |
|---|---|---|---|
| Context | tokens used, % of window, $ spent, model | statusline pump | every Claude repaint |
| Agents | subagents spawned this session, with live status | session transcript | every message |
| Jobs | background shell jobs this session, with live status | session transcript | every message |
| Git | branch, ahead/behind, per-file +/− | git status / diff --numstat |
2 s poll |
| Worktrees | all worktrees of the repo | git worktree list |
2 s poll |
| Touched | files this session edited, with edit counts | session transcript | every message |
| MCP | configured MCP server names | Claude Code config | at startup |
| Todo | live task list with status checkboxes | transcript TaskCreate/TaskUpdate |
every message |
| Diff | compacted git diff HEAD of the last-touched file |
git + transcript | 2 s poll |
Read-only by design: no keybindings, no mouse, no scrolling. It's a thing you glance at.
Claude Code has no sidebar extension point, so the panel lives next to the process:
cc-test (wrapper) ──► tmux session
├─ pane 0: claude ──► statusline.sh ──► pump JSON
└─ pane 1: python3 -m sidebar.main
├─ reads pump JSON (context, cost, model — authoritative,
│ survives compaction)
├─ tails session JSONL (todos, touched files — incremental,
│ byte-offset, handles truncation)
└─ polls git (2 s cadence, 1 s total deadline)
The key trick: Claude Code invokes your statusline command on every UI refresh and pipes it a JSON blob with authoritative context accounting. A small guarded block tees that JSON to a per-session file, and the sidebar watches it. No polling of Claude itself, no patching its bundle, no recomputing tokens from the transcript (which would drift after compaction).
The renderer is a pure function — render(model, width, height) -> list[str] — with
East-Asian-width-aware clipping (CJK content doesn't corrupt the pane), golden-frame tests,
and honest shrink accounting (flexible sections collapse to +N more and then disappear;
a section header is never stranded without content).
- Claude Code with a
statusLinecommand configured - tmux ≥ 3.2
- Python 3.10+ (stdlib only — zero dependencies)
- git
- Codex CLI ≥ 0.144 for cx-test (optional)
git clone https://github.com/nathan1658/claude-sidebar.git ~/claude-sidebar
ln -s ~/claude-sidebar/bin/cc-test ~/.local/bin/cc-test # or anywhere on your PATH
ln -s ~/claude-sidebar/bin/cx-test ~/.local/bin/cx-test # optional, for Codex CLIThen add the pump to your statusline script, immediately after it reads stdin
(e.g. after input=$(cat)):
# --- claude-sidebar pump (no-op unless launched via cc-test) ---
if [ -n "${CLAUDE_SIDEBAR_ID:-}" ]; then
_sb_dir="$HOME/.claude/sidebar"
mkdir -p "$_sb_dir"
printf '%s' "$input" > "$_sb_dir/$CLAUDE_SIDEBAR_ID.json.tmp" \
&& mv -f "$_sb_dir/$CLAUDE_SIDEBAR_ID.json.tmp" "$_sb_dir/$CLAUDE_SIDEBAR_ID.json"
fi
# --- end claude-sidebar pump ---The block is a strict no-op for every session not launched through the wrapper: plain
claude runs are byte-for-byte unaffected. If you don't have a statusline script yet, set
one up first (/statusline in Claude Code, or see the
statusline docs).
cd your-project
cc-test # instead of `claude` — extra args are forwardedNotes:
- Session lifetime == wrapper lifetime. Exiting Claude, detaching (
prefix d), or a setup failure all tear the tmux session down. There is no detach-and-keep-running. - Run it from a plain terminal, not from inside an existing tmux session.
- The pane is fixed at 34 columns; it adapts down to compact layouts if you shrink it.
- Color is on by default (diff +/−, git status, activity dots); disable with
--no-colororNO_COLOR=1. - If the pane shows
⚠ pump silentor⚠ no transcriptafter 30 s, the statusline pump or transcript feed never showed up — check your setup.
cd your-project
cx-test # instead of `codex` — extra args are forwardedcx-test works like cc-test, but wraps codex instead of claude. No statusline pump to
set up — the sidebar tails the Codex rollout JSONL directly, so there's nothing to add to
your config. It also shows a rate-limits line under the context bar, showing whichever rate
windows Codex reports (e.g. 5h 54% · wk 11%); the Jobs panel is unused (Codex has no
background-job concept). If the pane shows ⚠ no codex session found after 30 s, no rollout
matching this project's cwd showed up.
python3 -m unittest discover -s tests -v # 126 tests, no dependencies
python3 tests/gen_golden.py # regenerate golden frames after render changes
CLAUDE_SIDEBAR_ID=debug python3 -m sidebar.main --once # render one frame and exitDesign docs live in docs/superpowers/ — the spec and the task-by-task implementation plan
this was built from.
- MCP panel lists configured servers, not live connection status
- No LSP panel (Claude Code has no LSP subsystem to report on)
- Sessions launched outside
cc-test(including IDE/cmux-hosted ones) get no sidebar