Dreambase Panel
python
A boxed multi-line panel with auto-sizing borders: a context section with a gradient bar and status word, a stats section (tokens, cost, duration, lines changed), and a repo section (model, git branch with file counts, clock, vim mode), in a terracotta Claude-brand palette. Source: github.com/kyleledbetter/claudecode-statusline
Preview
Clean repo
╭─ CONTEXT ────────────────────────────────────────────╮
│ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 17:27 │
╰──────────────────────────────────────────────────────╯
New session
╭─ CONTEXT ──────────────────────────────────────╮
│ ░░░░░░░░░░░░░░░░░░░░░░ 0% of 200K · GOOD │
├─ STATS ────────────────────────────────────────┤
│ ↓ 0 ↑ 0 │ $0.00 │ ⏱ 10m 12s │
├─ REPO ─────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 17:27 │
╰────────────────────────────────────────────────╯
Dirty branch
╭─ CONTEXT ────────────────────────────────────────────╮
│ ██████████▌░░░░░░░░░░░ 48% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 96.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Sonnet 4.6 · feat/auth ~1 ?1 · 17:27 I │
╰──────────────────────────────────────────────────────╯
Near-full
╭─ CONTEXT ─────────────────────────────────────────────╮
│ ████████████████████░░ 91% ⚠ of 200K · HIGH │
├─ STATS ───────────────────────────────────────────────┤
│ ↓ 182.0K ↑ 1.4K │ $4.12 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ────────────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 17:27 │
╰───────────────────────────────────────────────────────╯
1M context
╭─ CONTEXT ─────────────────────────────────────────────╮
│ ██████████████░░░░░░░░ 64% ⚠ of 1M · OK │
├─ STATS ───────────────────────────────────────────────┤
│ ↓ 640.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ────────────────────────────────────────────────┤
│ ◆ Fable 5 · main · 17:27 │
╰───────────────────────────────────────────────────────╯
Post-compact
╭─ CONTEXT ──────────────────────────────────────╮
│ ░░░░░░░░░░░░░░░░░░░░░░ 0% of 200K · GOOD │
├─ STATS ────────────────────────────────────────┤
│ ↓ 0 ↑ 0 │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ─────────────────────────────────────────┤
│ ◆ Haiku 4.5 · main · 17:27 N │
╰────────────────────────────────────────────────╯
Worktree
╭─ CONTEXT ────────────────────────────────────────────╮
│ ████████▏░░░░░░░░░░░░░ 37% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 74.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · 17:27 │
╰──────────────────────────────────────────────────────╯
Non-git
╭─ CONTEXT ────────────────────────────────────────────╮
│ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · 17:27 │
╰──────────────────────────────────────────────────────╯
Source
#!/usr/bin/env python3
"""Claude Code Status Line — Dreambase Edition
A branded mini status app with box borders and section headers.
"""
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", "")
# ── 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"
# ── Layout engine ─────────────────────────────────────────
MIN_IW = 46 # minimum inner content width
def vlen(s):
"""Visible length excluding ANSI escapes."""
return len(re.sub(r"\033\[[^m]*m", "", s))
def make_renderer(content_rows):
"""Compute box width from widest content, return render functions."""
iw = max(MIN_IW, max(vlen(c) for c in content_rows))
tw = iw + 4
def pad(s):
return s + " " * max(0, iw - vlen(s))
def border_top(label=""):
if label:
fill = tw - 5 - len(label)
return f"{RUST}╭─ {RUST_B}{B}{label}{R}{RUST} {'─' * fill}╮{R}"
return f"{RUST}╭{'─' * (tw - 2)}╮{R}"
def border_mid(label=""):
if label:
fill = tw - 5 - len(label)
return f"{RUST}├─ {RUST_B}{B}{label}{R}{RUST} {'─' * fill}┤{R}"
return f"{RUST}├{'─' * (tw - 2)}┤{R}"
def border_bot():
return f"{RUST}╰{'─' * (tw - 2)}╯{R}"
def row(content):
return f"{RUST}│{R} {pad(content)} {RUST}│{R}"
return border_top, border_mid, border_bot, row
# ── 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
# ── Progress bar ──────────────────────────────────────────
BAR_W = 22
BLOCKS = " ▏▎▍▌▋▊▉█"
if pct >= 95:
bar_clr = RED
status_label = f"{RED}{B}CRITICAL{R}"
elif pct >= 85:
bar_clr = ORG
status_label = f"{ORG}HIGH{R}"
elif pct >= 70:
bar_clr = YEL
status_label = f"{YEL}MODERATE{R}"
elif pct >= 50:
bar_clr = LIME
status_label = f"{LIME}OK{R}"
else:
bar_clr = GRN
status_label = f"{GRN}GOOD{R}"
eighths = pct * BAR_W * 8 // 100
full = eighths // 8
partial = eighths % 8
empty = BAR_W - full - (1 if partial else 0)
bar = "█" * full
if partial:
bar += BLOCKS[partial]
bar += "░" * empty
ctx_label = "1M" if ctx_size >= 1_000_000 else "200K"
warn = f" {RED}{B}⚠{R}" if exceeds_200k else ""
# ── Git info (cached for perf) ────────────────────────────
CACHE = "/tmp/claude-sl-git"
def git_info():
try:
if time.time() - os.path.getmtime(CACHE) < 5:
with open(CACHE) as f:
p = f.read().strip().split("|")
if len(p) == 4:
return p
except (OSError, ValueError):
pass
try:
br = subprocess.run(
["git", "-C", project_dir, "branch", "--show-current"],
capture_output=True, text=True, timeout=2,
).stdout.strip()
if not br:
return ["", "0", "0", "0"]
st = len(subprocess.run(
["git", "-C", project_dir, "diff", "--cached", "--numstat"],
capture_output=True, text=True, timeout=2,
).stdout.strip().splitlines())
mo = len(subprocess.run(
["git", "-C", project_dir, "diff", "--numstat"],
capture_output=True, text=True, timeout=2,
).stdout.strip().splitlines())
ut = len(subprocess.run(
["git", "-C", project_dir, "ls-files", "--others", "--exclude-standard"],
capture_output=True, text=True, timeout=2,
).stdout.strip().splitlines())
result = [br, str(st), str(mo), str(ut)]
with open(CACHE, "w") as f:
f.write("|".join(result))
return result
except Exception:
return ["", "0", "0", "0"]
branch, staged, modified, untracked = git_info()
staged, modified, untracked = int(staged), int(modified), int(untracked)
# ── 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)
clock = time.strftime("%H:%M")
# Git indicators
git_ind = ""
if staged:
git_ind += f" {GRN}+{staged}{R}"
if modified:
git_ind += f" {YEL}~{modified}{R}"
if untracked:
git_ind += f" {GRY}?{untracked}{R}"
# Lines changed
lines_s = ""
if lines_added or lines_removed:
lines_s = f"{GRN}+{lines_added}{R}{D}/{R}{RED}-{lines_removed}{R}"
# 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 ─────────────────────────────────
ctx_content = (
f"{bar_clr}{bar}{R} {WHT}{B}{pct}%{R}{warn}"
f" {D}of{R} {RUST}{ctx_label}{R} {D}·{R} {status_label}"
)
stats_content = (
f"{BLU}↓{R} {in_t} {PUR}↑{R} {out_t}"
f" {RUST_D}│{R} {YEL}{cost_s}{R}"
f" {RUST_D}│{R} {GRY}⏱ {dur_s}{R}"
+ (f" {RUST_D}│{R} {lines_s}" if lines_s else "")
)
repo_content = f"{m_icon} {RUST_B}{B}{model}{R}"
if branch:
repo_content += f" {RUST_D}·{R} {PUR}{truncate(branch)}{R}{git_ind}"
repo_content += f" {RUST_D}·{R} {GRY}{clock}{R}"
repo_content += vim_s
# ── Render (box auto-sizes to widest row) ─────────────────
border_top, border_mid, border_bot, row = make_renderer(
[ctx_content, stats_content, repo_content]
)
print(border_top("CONTEXT"))
print(row(ctx_content))
print(border_mid("STATS"))
print(row(stats_content))
print(border_mid("REPO"))
print(row(repo_content))
print(border_bot())