Two lines in ice cyan and apricot: a per-session emoji, your computer's name, and the project with its git branch; then the model and context use. A label in an optional ~/.config/starfield-terminal/config.json replaces the computer name. Python standard library only, no network calls.

In Claude Desktop? This status line shows there once you also install statusline-anywhere.

Before you install

  • This version was reviewed before it was listed on Nov 2, 2026
  • Doesn't ask to connect to the internet
  • Shows your computer's name, from scutil on macOS or the hostname elsewhere.
  • Set status_label in ~/.config/starfield-terminal/config.json to show your own label instead.
  • Writes no files and makes no network calls. The blank third line is a spacer on purpose.
Not showing after you paste? Fix it

Preview

Clean repo
☕ e2b │ PROJECT app · GIT main MODEL Opus 5.5 │ CTX 22% ⠀
New session
☕ e2b │ PROJECT app · GIT main MODEL Opus 5.5 │ CTX -- ⠀
Dirty branch
☕ e2b │ PROJECT app · GIT feat/auth MODEL Sonnet 5.5 │ CTX 48% ⠀
Near-full
☕ e2b │ PROJECT app · GIT main MODEL Opus 5.5 │ CTX 91% ⠀
1M context
☕ e2b │ PROJECT app · GIT main MODEL Fable 5.1 │ CTX 64% ⠀
Post-compact
☕ e2b │ PROJECT app · GIT main MODEL Haiku 4.5 │ CTX -- ⠀
Worktree
☕ e2b │ PROJECT feature · GIT worktree-featu… MODEL Opus 5.5 │ CTX 37% ⠀
Non-git
☕ e2b │ PROJECT scratch MODEL Opus 5.5 │ CTX 22% ⠀

What it does

A two-line Claude Code status line theme in ice cyan and apricot. It shows a session emoji, your computer's name, the project and git branch, then the model and context use.

Written in
Python
Network access
None requested
Works in
Terminal

How it works

  1. Reads the folder, model name and context percentage from stdin, and COLUMNS for width.
  2. Picks one of six emoji from a hash of the session ID, so each session keeps its own.
  3. Runs git branch --show-current and cuts long names short, as in worktree-featu….
  4. Shows CTX -- before the first prompt and drops the GIT part outside a repo.

Source

#!/usr/bin/env python3
"""Starfield Terminal: Claude Code statusline; JSON on stdin. No network calls."""
import hashlib
import json
import os
from pathlib import Path
import socket
import subprocess
import sys
import unicodedata

# Palette: ice cyan, apricot, silver white, cool slate
CYAN = '\033[38;2;143;229;239m'
APRICOT = '\033[38;2;245;191;138m'
WHITE = '\033[38;2;237;241;245m'
MUTED = '\033[38;2;135;152;171m'
RESET = '\033[0m'
MARKS = ['🥐', '🍳', '☕', '🚀', '✨', '🔮']


def width(s):
    total = 0
    for ch in s:
        if unicodedata.combining(ch) or unicodedata.category(ch) in ('Cf', 'Mn', 'Me'):
            continue
        total += 2 if (unicodedata.east_asian_width(ch) in ('W', 'F') or ord(ch) >= 0x1F300) else 1
    return total


def shorten(s, limit):
    s = str(s).replace('\n', ' ').replace('\r', ' ').strip()
    if width(s) <= limit:
        return s
    out = ''
    for ch in s:
        if width(out + ch) > max(0, limit - 1):
            break
        out += ch
    return out + '…'


def machine_label():
    """Fallback identity when config.json has no status_label.

    macOS reports the friendly ComputerName from System Settings; elsewhere the
    hostname without its domain is used. Returned verbatim — a machine called
    'MacBook Pro' should read like that, not like a shouting brand.
    """
    host = ''
    if sys.platform == 'darwin':
        try:
            proc = subprocess.run(
                ['scutil', '--get', 'ComputerName'],
                stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
                text=True, timeout=0.5, check=False
            )
            host = proc.stdout.strip()
        except Exception:
            host = ''
    if not host:
        try:
            host = socket.gethostname().split('.')[0].strip()
        except OSError:
            return 'STARFIELD'
    return host or 'STARFIELD'


try:
    d = json.load(sys.stdin)
except Exception:
    d = {}

try:
    config_file = Path.home() / '.config' / 'starfield-terminal' / 'config.json'
    config = json.loads(config_file.read_text(encoding='utf-8')) if config_file.exists() else {}
    if not isinstance(config, dict):
        config = {}
except (OSError, ValueError):
    config = {}

session = str(d.get('session_id') or 'default')
mark = MARKS[int(hashlib.sha256(session.encode()).hexdigest(), 16) % len(MARKS)]
work = d.get('workspace') or {}
current_dir = work.get('current_dir') or d.get('cwd') or str(Path.home())
project_name = Path(current_dir).name or '~'
if Path(current_dir).expanduser() == Path.home():
    project_name = '~'

model_data = d.get('model') or {}
model_raw = str(model_data.get('display_name') or model_data.get('id') or '--')
# Provider names in parenthesis add length but not useful model information.
model_raw = model_raw.replace('(OpenRouter)', '').replace(' (OpenRouter)', '')

# An unrecognized OpenRouter model may be assigned an assumed 200k context.
# Do not imply precision for custom gateway context windows.
model_id = str(model_data.get('id') or '').lower()
is_gateway = 'openrouter' in model_id or 'openrouter' in str(model_data.get('display_name') or '').lower() or model_id.startswith('ccs-')
ctx_data = d.get('context_window') or {}
pct = ctx_data.get('used_percentage')
ctx_text = '--'
if isinstance(pct, (int, float)) and not isinstance(pct, bool) and 0 <= pct <= 100:
    # Custom gateway model window sizes can be assumed by Claude Code.
    # The tilde explicitly marks provider-dependent percentages as estimates.
    ctx_text = f'~{pct:.0f}%' if is_gateway else f'{pct:.0f}%'

try:
    proc = subprocess.run(
        ['git', '-C', str(current_dir), 'branch', '--show-current'],
        stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
        text=True, timeout=0.75, check=False
    )
    branch = proc.stdout.strip()
except Exception:
    branch = ''

try:
    cols = int(os.environ.get('COLUMNS', '88'))
except ValueError:
    cols = 88
cols = max(35, cols)

# Line 1: identity + current project + git branch. Limit all values.
branch = shorten(branch, 15)
status_label = shorten(config.get('status_label') or machine_label(), 24)
identity = f'{mark}  {status_label}'
project_label = 'PROJECT'
git_suffix = f'  ·  GIT {branch}' if branch else ''
max_project = max(8, cols - width(identity) - width(project_label) - width(git_suffix) - 8)
project = shorten(project_name, min(26, max_project))
if width(identity) + width(project_label) + width(project) + width(git_suffix) + 6 > cols:
    git_suffix = ''
line1 = (f'{WHITE}{mark}{RESET}  {CYAN}{status_label}{RESET}'
         f'  {MUTED}│ PROJECT{RESET}  {WHITE}{project}{RESET}'
         + (f'{MUTED}{git_suffix}{RESET}' if git_suffix else ''))

# Line 2: model, then context; keep right-hand label legible on narrow splits.
model_limit = max(8, min(38, cols - 25))
model = shorten(model_raw, model_limit)
line2 = (f'{MUTED}MODEL{RESET}  {WHITE}{model}{RESET}'
         f'  {MUTED}│  CTX{RESET}  {APRICOT}{ctx_text}{RESET}')

print(line1)
print(line2)
# U+2800 BRAILLE PATTERN BLANK renders as nothing but is not whitespace, so the
# host keeps this spacer line instead of trimming it away.
print('⠀')

More status lines

  • 237 installs
╭─ CONTEXT ────────────────────────────────────────────╮ │ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │ ├─ STATS ──────────────────────────────────────────────┤ │ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │ ├─ REPO ───────────────────────────────────────────────┤ │ ◆ Opus 4.8 · main · 16:21 │ ╰──────────────────────────────────────────────────────╯
  • 4 installs
◆ Opus 4.8 ▏ ctx ▓▓░░░░░░░░ 22% ▏ high ▏ 10m main ▏ week ▓░░░░░░░░░ 7% ▏ 2d0h ▏ $0.41 ▏ +128/-34
◆ Opus 4.8 │ app/main ▰▰▰▰▱ 78% │ ↑44k ↓1.4k │ 5h: -- │ 7d: -- │ 10m12s
✧ ███████████▋ 73% 🗝 Ultima Weapon ✦ app ∙ main ♥ ████████████████████████ 100% 「SAVE POINT」 ✶ Limit Form ◉ 41
  • 104 installs
Opus 4.8 [high] ~/app main 16:20:55 | ⛁ ██░░░░░░░░ 22% | 5h ●◔○○○ 26% ↻2h7m | 7d ◔○○○○○○ 7% ↻2d1h │ $0.41 ⏱ 10m
  • 81 installs
████▊░░░░░░░░░░░░░░░░░ 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

Related mods

Deck in Claude Desktop

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.

Context Bar in Claude Desktop

Your context window as a stacked bar above the prompt, a color per category, with token counts and the compaction point. /context-bar shows or hides it.