╭─ CONTEXT ────────────────────────────────────────────╮
│ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 16:21 │
╰──────────────────────────────────────────────────────╯
Terracotta
by
Single-line status line in a terracotta Claude-brand palette: model, effort, cwd, git branch and diff stats, MCP connection count, context bar with token usage, cache hit rate, cost, and session duration. Adapted from @kyleledbetter's Dreambase Panel.
Before you install
- This version was reviewed before it was listed on Oct 29, 2026
- Doesn't ask to connect to the internet
- The (+128,-34) beside the branch counts lines Claude changed this session, not your uncommitted git diff.
- A yellow ⚠ follows the context bar once a response passes 200k tokens, even in a 1M context window.
- Outside a git repo the branch segment is left out, and a named session shows at the end of the line.
Preview
◆ Opus 5.5 │ Effort: high │ cwd: ~ / app │ ⎇ main (+128,-34) │ MCP: 0 │ Context: [▰▰▱▱▱▱▱▱▱▱] 44k/200k (22%) │ ↓ 44.0K ↑ 1.4K ⛁ 2.0K (4%) │ $0.41 │ ⏱ 10m 12s
◆ Opus 5.5 │ Effort: high │ cwd: ~ / app │ ⎇ main (+0,-0) │ MCP: 0 │ Context: [▱▱▱▱▱▱▱▱▱▱] 0/200k (0%) │ ↓ 0 ↑ 0 │ $0.00 │ ⏱ 10m 12s
◆ Sonnet 5.5 I │ Effort: medium │ cwd: ~ / app │ ⎇ feat/auth (+128,-34) │ MCP: 0 │ Context: [▰▰▰▰▱▱▱▱▱▱] 96k/200k (48%) │ ↓ 96.0K ↑ 1.4K ⛁ 2.0K (2%) │ $0.41 │ ⏱ 10m 12s
◆ Opus 5.5 │ Effort: max │ cwd: ~ / app │ ⎇ main (+128,-34) │ MCP: 0 │ Context: [▰▰▰▰▰▰▰▰▰▱] 182k/200k (91%) ⚠ │ ↓ 182.0K ↑ 1.4K ⛁ 2.0K (1%) │ $4.12 │ ⏱ 10m 12s
◆ Fable 5.1 │ Effort: xhigh │ cwd: ~ / app │ ⎇ main (+128,-34) │ MCP: 0 │ Context: [▰▰▰▰▰▰▱▱▱▱] 640k/1M (64%) ⚠ │ ↓ 640.0K ↑ 1.4K ⛁ 2.0K (0%) │ $0.41 │ ⏱ 10m 12s
◆ Haiku 4.5 N │ cwd: ~ / app │ ⎇ main (+128,-34) │ MCP: 0 │ Context: [▱▱▱▱▱▱▱▱▱▱] 0/200k (0%) │ ↓ 0 ↑ 0 │ $0.41 │ ⏱ 10m 12s
◆ Opus 5.5 │ Effort: low │ cwd: ~ / .wt / feature │ MCP: 0 │ Context: [▰▰▰▱▱▱▱▱▱▱] 74k/200k (37%) │ ↓ 74.0K ↑ 1.4K ⛁ 2.0K (2%) │ $0.41 │ ⏱ 10m 12s
◆ Opus 5.5 │ Effort: high │ cwd: ~ / scratch │ MCP: 0 │ Context: [▰▰▱▱▱▱▱▱▱▱] 44k/200k (22%) │ ↓ 44.0K ↑ 1.4K ⛁ 2.0K (4%) │ $0.41 │ ⏱ 10m 12s │ Session: refactor-pass
What it does
One terracotta-colored line with your model, effort, folder, git branch, MCP server count, a context bar, token and cache stats, cost and session time.
- Written in
- Python
- Network access
- None requested
- Works in
- Terminal
How it works
- Reads the model, effort, folders, cost, line counts and context usage from the stdin JSON.
- Runs git branch --show-current in the project folder, caching it for 5 seconds.
- Counts MCP servers listed in ~/.claude.json and .mcp.json, without connecting to them.
- The 10-block context bar turns lime at 50%, yellow at 70%, orange at 85% and red at 95%.
Source
#!/usr/bin/env python3
"""Claude Code Status Line — Dreambase Flat
Single-line adaptation of @kyleledbetter's Dreambase Panel: the same
context bar, stats, and repo sections in a terracotta Claude-brand
palette, rendered on one line instead of a boxed three-row panel.
"""
import json
import os
import re
import subprocess
import sys
import time
data = json.load(sys.stdin)
# ── Extract data ──────────────────────────────────────────
model = data.get("model", {}).get("display_name", "—")
model_id = data.get("model", {}).get("id", "")
project_dir = data.get("workspace", {}).get("project_dir", ".")
current_dir = data.get("workspace", {}).get("current_dir", ".")
pct = int(float(data.get("context_window", {}).get("used_percentage") or 0))
ctx_size = int(data.get("context_window", {}).get("context_window_size") or 200000)
input_tokens = int(data.get("context_window", {}).get("total_input_tokens") or 0)
output_tokens = int(data.get("context_window", {}).get("total_output_tokens") or 0)
cost_usd = float(data.get("cost", {}).get("total_cost_usd") or 0)
duration_ms = int(data.get("cost", {}).get("total_duration_ms") or 0)
lines_added = int(data.get("cost", {}).get("total_lines_added") or 0)
lines_removed = int(data.get("cost", {}).get("total_lines_removed") or 0)
exceeds_200k = data.get("exceeds_200k_tokens", False)
vim_mode = data.get("vim", {}).get("mode", "")
effort_level = (
data.get("thinking", {}).get("level")
or data.get("effort", {}).get("level")
or os.environ.get("CLAUDE_EFFORT", "")
)
session_id = data.get("session_id", "")
current = data.get("context_window", {}).get("current_usage") or {}
cache_read = int(current.get("cache_read_input_tokens") or 0)
cache_write = int(current.get("cache_creation_input_tokens") or 0)
fresh_input = int(current.get("input_tokens") or 0)
# ── ANSI Colors ───────────────────────────────────────────
R = "\033[0m"
B = "\033[1m"
D = "\033[2m"
# Claude brand rust / terracotta
RUST = "\033[38;5;173m"
RUST_B = "\033[38;5;209m"
RUST_D = "\033[38;5;131m"
RUST_BG = "\033[48;5;52m"
# Functional
GRN = "\033[38;5;34m"
LIME = "\033[38;5;118m"
YEL = "\033[38;5;220m"
ORG = "\033[38;5;208m"
RED = "\033[38;5;196m"
CYN = "\033[38;5;81m"
BLU = "\033[38;5;69m"
PUR = "\033[38;5;141m"
WHT = "\033[38;5;255m"
GRY = "\033[38;5;243m"
# ── Formatters ────────────────────────────────────────────
def fmt_tok(n):
if n >= 1_000_000:
return f"{n / 1_000_000:.1f}M"
if n >= 1_000:
return f"{n / 1_000:.1f}K"
return str(n)
def fmt_dur(ms):
s = ms // 1000
h, rem = divmod(s, 3600)
m, sec = divmod(rem, 60)
if h:
return f"{h}h {m}m"
if m:
return f"{m}m {sec}s"
return f"{sec}s"
def truncate(s, maxlen=18):
return s[: maxlen - 1] + "…" if len(s) > maxlen else s
def fmt_cwd(path, maxlen=40):
home = os.path.expanduser("~")
if path == home or path.startswith(home + os.sep):
path = "~" + path[len(home):]
root = path.startswith("/")
segs = [p for p in path.split("/") if p]
def width(s):
return len(" / ".join(s)) + (1 if root else 0)
# Fish-style: shorten leading segments to their first char until it fits; last segment stays full
for i in range(len(segs) - 1):
if width(segs) <= maxlen:
break
segs[i] = segs[i][:1]
return ("/" if root else ""), segs
# ── Context bar ───────────────────────────────────────────
BAR_W = 10
if pct >= 95:
bar_clr = RED
elif pct >= 85:
bar_clr = ORG
elif pct >= 70:
bar_clr = YEL
elif pct >= 50:
bar_clr = LIME
else:
bar_clr = GRN
filled = pct * BAR_W // 100
bar = "▰" * filled + "▱" * (BAR_W - filled)
# Tokens currently in context: fresh input + cache read + cache write (falls back to the percentage)
cache_total = cache_read + cache_write + fresh_input
used_tokens = cache_total if cache_total else pct * ctx_size // 100
def fmt_ctx(n):
if n >= 1_000_000:
return f"{n / 1_000_000:g}M"
if n >= 1_000:
return f"{round(n / 1_000)}k"
return str(n)
ctx_label = fmt_ctx(ctx_size)
warn = f" {YEL}{B}⚠{R}" if exceeds_200k else ""
# ── MCP servers (user + project config) ───────────────────
def mcp_count():
names = set()
try:
with open(os.path.expanduser("~/.claude.json")) as f:
claude_json = json.load(f)
names.update((claude_json.get("mcpServers") or {}).keys())
project = claude_json.get("projects", {}).get(project_dir, {})
names.update((project.get("mcpServers") or {}).keys())
except (OSError, ValueError):
pass
try:
with open(os.path.join(project_dir, ".mcp.json")) as f:
names.update((json.load(f).get("mcpServers") or {}).keys())
except (OSError, ValueError):
pass
return len(names)
# ── Session name (from running-session registry) ──────────
def session_name():
if data.get("session_name"):
return data["session_name"]
sessions_dir = os.path.expanduser("~/.claude/sessions")
try:
for fname in os.listdir(sessions_dir):
if not fname.endswith(".json"):
continue
try:
with open(os.path.join(sessions_dir, fname)) as f:
entry = json.load(f)
except (OSError, ValueError):
continue
if session_id and entry.get("sessionId") == session_id:
return entry.get("name", "")
except OSError:
pass
return ""
# ── Git branch (cached for perf) ──────────────────────────
CACHE = "/tmp/claude-sl-git"
def git_branch():
try:
if time.time() - os.path.getmtime(CACHE) < 5:
with open(CACHE) as f:
cached_dir, cached_branch = f.read().split("\n", 1)
if cached_dir == project_dir:
return cached_branch.strip()
except (OSError, ValueError):
pass
try:
br = subprocess.run(
["git", "-C", project_dir, "branch", "--show-current"],
capture_output=True, text=True, timeout=2,
).stdout.strip()
with open(CACHE, "w") as f:
f.write(f"{project_dir}\n{br}")
return br
except Exception:
return ""
branch = git_branch()
# ── Derived values ────────────────────────────────────────
in_t = fmt_tok(input_tokens)
out_t = fmt_tok(output_tokens)
cost_s = f"${cost_usd:.2f}"
dur_s = fmt_dur(duration_ms)
# Model diamond icon
if "opus" in model_id:
m_icon = f"{RUST_B}◆{R}"
elif "sonnet" in model_id:
m_icon = f"{BLU}◆{R}"
elif "haiku" in model_id:
m_icon = f"{GRN}◆{R}"
else:
m_icon = f"{GRY}◆{R}"
# Vim mode
vim_s = ""
if vim_mode == "NORMAL":
vim_s = f" {RUST_BG}{WHT}{B} N {R}"
elif vim_mode == "INSERT":
vim_s = f" {RUST_BG}{WHT}{B} I {R}"
# ── Build content strings ─────────────────────────────────
model_content = f"{m_icon} {RUST_B}{B}{model}{R}{vim_s}"
effort_content = ""
if effort_level:
effort_content = f"{GRY}Effort:{R} {PUR}{effort_level}{R}"
session_content = ""
name = session_name()
if name:
session_content = f"{GRY}Session:{R} {PUR}{name}{R}"
CWD_SEP = f"{R} {GRY}/{R} {PUR}"
cwd_prefix, cwd_segs = fmt_cwd(current_dir)
cwd_content = f"{GRY}cwd:{R} {PUR}{cwd_prefix}{CWD_SEP.join(cwd_segs)}{R}"
branch_content = ""
if branch:
branch_content = (
f"{GRY}⎇{R} {PUR}{truncate(branch)}{R}"
f" {D}({R}{GRN}+{lines_added}{R}{D},{R}{RED}-{lines_removed}{R}{D}){R}"
)
ctx_content = (
f"{GRY}Context:{R} [{bar_clr}{bar}{R}] {WHT}{B}{fmt_ctx(used_tokens)}{R}"
f"{GRY}/{R}{RUST}{ctx_label}{R} {D}({pct}%){R}{warn}"
)
cache_s = ""
if cache_total:
hit_pct = cache_read * 100 // cache_total
cache_s = f" {CYN}⛁{R} {fmt_tok(cache_read)} {GRY}({hit_pct}%){R}"
stats_content = (
f"{BLU}↓{R} {in_t} {PUR}↑{R} {out_t}{cache_s}"
f" {RUST_D}│{R} {YEL}{cost_s}{R}"
f" {RUST_D}│{R} {GRY}⏱ {dur_s}{R}"
)
# ── Render (single line) ──────────────────────────────────
sep = f" {RUST}│{R} "
segments = [model_content]
if effort_content:
segments.append(effort_content)
segments.append(cwd_content)
if branch_content:
segments.append(branch_content)
mcp_content = f"{GRY}MCP:{R} {PUR}{mcp_count()}{R}"
segments += [mcp_content, ctx_content, stats_content]
left = sep.join(segments)
# Right-align session name: pad with spaces to terminal width (visible chars only)
ANSI_RE = re.compile(r"\033\[[0-9;]*m")
def visible_len(s):
return len(ANSI_RE.sub("", s))
def term_width():
for stream in (sys.stderr, sys.stdin, sys.stdout):
try:
return os.get_terminal_size(stream.fileno()).columns
except (OSError, ValueError):
pass
return int(os.environ.get("COLUMNS", 120))
if session_content:
gap = term_width() - visible_len(left) - visible_len(session_content) - 2
if gap >= 2:
print(f"{left}{' ' * gap}{session_content}")
else:
print(f"{left}{sep}{session_content}")
else:
print(left)
More status lines
████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │ 5h 26% ↻2h06m │ 7d 7% ↻2d0h │ ◆ Opus 5.5 · main · 03:53
◆ Opus 4.8 ▏ ctx ▓▓░░░░░░░░ 22% ▏ high ▏ 10m
main ▏ week ▓░░░░░░░░░ 7% ▏ 2d0h ▏ $0.41 ▏ +128/-34Opus 4.8 | user | ~/app | main | ██░░░░░░░░ 22% | 37000↑ 1400↓ 2000c | $0.4100
Opus 4.8 [high] ~/app main
16:20:55 | ⛁ ██░░░░░░░░ 22% | 5h ●◔○○○ 26% ↻2h7m | 7d ◔○○○○○○ 7% ↻2d1h │ $0.41 ⏱ 10m
Opus 4.8 │ main │ app │ ▓▓░░░░░░░░ 44k/200k │ $0.41
Related mods
Shows branch safety, context headroom, and account usage in a compact session telemetry strip

One line above the prompt: context tokens with their weather, a bar per recent prompt, your 5-hour and 7-day limits against the time elapsed, the prompt cache with its time left, the session cost with the last prompt's share in dollars and in points of the 5-hour limit, and the subagents running. Labels in English, French or Russian. After Token Weather (Anthropic, Apache-2.0) and the ideas of usage-meter (HolyGrail) and prompt-cache-control (Daniel San).

A cockpit for Claude Code: plan bars and subagent strips, context, usage limits with reset countdowns and burn rates, spend, the model route and a pet. Six bar styles, three collapsed looks. CLI and Desktop.

Flightdeck: a live agent dashboard for Claude Code. Main model vitals, an on-call architect, every permission check, subagent cards and swimlanes, a turn receipt and a session log, all from real session events
