Skip to main content
statuslinesSource-backedReview first Safety Privacy

Python Rich Statusline - Statuslines

Feature-rich statusline using Python's Rich library for beautiful formatting, progress bars, and real-time token cost tracking

by JSONbored·added 2025-10-01·
Claude Code
HarnessClaude Code
Language:python
Review first review before installing

Open the source and read safety notes before installing.

Safety notes

  • Runs as a Claude Code statusline command on refresh and depends on the local Python/Rich environment being available.
  • Does not perform network calls or write files, but statusline failures may affect prompt/status rendering until dependencies are installed.

Privacy notes

  • Reads Claude Code statusline JSON from stdin, including model, workspace path, token usage, estimated cost, and Git branch when present.
  • Displays workspace path, Git branch, token usage, and cost information in the local terminal statusline.

Prerequisites

  • Claude Code CLI installed and configured
  • Python 3.7+ installed and available in PATH (python3 command)
  • Rich library installed (pip3 install rich or python3 -m pip install rich)
  • Terminal with UTF-8 encoding support (required for Unicode emojis: 🤖 📁 🎯 💰)
  • Terminal with truecolor support (24-bit color, recommended for best Rich rendering - set COLORTERM=truecolor)
  • Git command (optional, for Git branch display - statusline works without Git)

Schema details

Install type
config
Reading time
1 min
Difficulty score
3
Troubleshooting
Yes
Breaking changes
No
Runtime and command metadata
Script language
python
Script body
#!/usr/bin/env python3

import sys
import json
from rich.console import Console
from rich.text import Text

console = Console()

# Read JSON from stdin
try:
    data = json.load(sys.stdin)
except json.JSONDecodeError:
    console.print("[red]Error: Invalid JSON input[/red]")
    sys.exit(1)

# Extract session data
model = data.get('model', 'unknown')
workspace = data.get('workspace', {}).get('path', '~').replace(f"{os.path.expanduser('~')}", '~')
tokens = data.get('session', {}).get('totalTokens', 0)
cost = data.get('session', {}).get('estimatedCost', 0.0)
git_branch = data.get('git', {}).get('branch', None)

# Build status components
status = Text()

# Model indicator with emoji
status.append("🤖 ", style="bold")
status.append(f"{model}", style="bold cyan")
status.append(" │ ", style="dim")

# Directory
status.append("📁 ", style="bold")
status.append(f"{workspace}", style="yellow")

# Git branch if available
if git_branch:
    status.append(" │ ", style="dim")
    status.append(" ", style="bold")
    status.append(f"{git_branch}", style="magenta")

# Token usage
status.append(" │ ", style="dim")
status.append("🎯 ", style="bold")
status.append(f"{tokens:,}", style="green" if tokens < 100000 else "yellow")

# Cost with dynamic coloring
if cost > 0:
    status.append(" │ ", style="dim")
    status.append("💰 ", style="bold")
    cost_color = "green" if cost < 0.10 else "yellow" if cost < 1.0 else "red"
    status.append(f"${cost:.3f}", style=cost_color)

console.print(status)
Full copyable content
{
  "statusLine": {
    "type": "command",
    "command": "$CLAUDE_PROJECT_DIR/.claude/statuslines/python-rich-statusline.py",
    "refreshInterval": 1000
  }
}

About this resource

Features

  • Rich library for beautiful terminal formatting
  • Real-time token cost calculation and display
  • Progress bar showing session token usage
  • Git branch and status indicators
  • Emoji support for visual feedback
  • Dynamic color schemes based on cost thresholds
  • JSON parsing with error handling
  • Customizable Rich table layouts for multi-metric display

Use Cases

  • Track token costs in real-time during long sessions
  • Monitor git branch when working across multiple projects
  • Visual feedback for budget-conscious API usage
  • Enhanced aesthetics for presentation or streaming
  • Python developers leveraging Rich library ecosystem
  • Data visualization enthusiasts wanting rich terminal output

Requirements

  • Claude Code CLI installed and configured
  • Python 3.7+ installed and available in PATH (python3 command)
  • Rich library installed (pip3 install rich or python3 -m pip install rich)
  • Terminal with UTF-8 encoding support (required for Unicode emojis: 🤖 📁 🎯 💰)
  • Terminal with truecolor support (24-bit color, recommended for best Rich rendering - set COLORTERM=truecolor)
  • Git command (optional, for Git branch display - statusline works without Git)

Configuration

{
  "statusLine": {
    "type": "command",
    "command": "$CLAUDE_PROJECT_DIR/.claude/statuslines/python-rich-statusline.py",
    "refreshInterval": 1000
  }
}

Examples

Enhanced Python Rich Statusline with Progress Bar

Extended version with Rich progress bar showing token usage percentage

#!/usr/bin/env python3

import sys
import json
import os
from rich.console import Console
from rich.text import Text
from rich.progress import BarColumn, Progress

console = Console()

try:
    data = json.load(sys.stdin)
except json.JSONDecodeError:
    console.print("[red]Error: Invalid JSON input[/red]")
    sys.exit(1)

model = data.get('model', {}).get('display_name') or data.get('model', {}).get('id') or 'unknown'
workspace_path = data.get('workspace', {}).get('current_dir') or data.get('workspace', {}).get('path') or data.get('cwd', '~')
workspace = workspace_path.replace(os.path.expanduser('~'), '~')
workspace = os.path.basename(workspace)

tokens = data.get('cost', {}).get('total_lines_added') or data.get('session', {}).get('totalTokens', 0)
cost = data.get('cost', {}).get('total_cost_usd') or data.get('session', {}).get('estimatedCost', 0.0)

# Token limit (adjust based on model)
if 'sonnet' in model.lower() or 'opus' in model.lower():
    token_limit = 1000000
else:
    token_limit = 200000

token_percentage = min((tokens / token_limit) * 100, 100)

# Git branch
git_branch = None
if os.path.exists('.git'):
    try:
        import subprocess
        result = subprocess.run(
            ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
            capture_output=True,
            text=True,
            timeout=1
        )
        if result.returncode == 0:
            git_branch = result.stdout.strip()
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pass

status = Text()

status.append("🤖 ", style="bold")
status.append(f"{model}", style="bold cyan")
status.append(" │ ", style="dim")

status.append("📁 ", style="bold")
status.append(f"{workspace}", style="yellow")

if git_branch:
    status.append(" │ ", style="dim")
    status.append(f"{git_branch}", style="magenta")

status.append(" │ ", style="dim")
status.append("🎯 ", style="bold")
token_style = "green" if token_percentage < 50 else "yellow" if token_percentage < 80 else "red"
status.append(f"{tokens:,}", style=token_style)
status.append(f" ({token_percentage:.1f}%)", style="dim")

if cost > 0:
    status.append(" │ ", style="dim")
    status.append("💰 ", style="bold")
    cost_color = "green" if cost < 0.10 else "yellow" if cost < 1.0 else "red"
    status.append(f"${cost:.3f}", style=cost_color)

console.print(status)

Python Rich Statusline with Custom Themes

Version with configurable color themes via environment variables

#!/usr/bin/env python3

import sys
import json
import os
from rich.console import Console
from rich.text import Text

console = Console()

try:
    data = json.load(sys.stdin)
except json.JSONDecodeError:
    console.print("[red]Error: Invalid JSON input[/red]")
    sys.exit(1)

# Theme configuration (via environment variables)
theme = os.environ.get('RICH_STATUSLINE_THEME', 'default')

themes = {
    'default': {
        'model': 'bold cyan',
        'directory': 'yellow',
        'git': 'magenta',
        'token_low': 'green',
        'token_med': 'yellow',
        'token_high': 'red',
        'cost_low': 'green',
        'cost_med': 'yellow',
        'cost_high': 'red'
    },
    'dark': {
        'model': 'bold white',
        'directory': 'bright_white',
        'git': 'bright_magenta',
        'token_low': 'bright_green',
        'token_med': 'bright_yellow',
        'token_high': 'bright_red',
        'cost_low': 'bright_green',
        'cost_med': 'bright_yellow',
        'cost_high': 'bright_red'
    },
    'minimal': {
        'model': 'cyan',
        'directory': 'white',
        'git': 'white',
        'token_low': 'white',
        'token_med': 'white',
        'token_high': 'white',
        'cost_low': 'white',
        'cost_med': 'white',
        'cost_high': 'white'
    }
}

colors = themes.get(theme, themes['default'])

model = data.get('model', {}).get('display_name') or data.get('model', {}).get('id') or 'unknown'
workspace_path = data.get('workspace', {}).get('current_dir') or data.get('workspace', {}).get('path') or data.get('cwd', '~')
workspace = workspace_path.replace(os.path.expanduser('~'), '~')
workspace = os.path.basename(workspace)

tokens = data.get('cost', {}).get('total_lines_added') or data.get('session', {}).get('totalTokens', 0)
cost = data.get('cost', {}).get('total_cost_usd') or data.get('session', {}).get('estimatedCost', 0.0)

git_branch = None
if os.path.exists('.git'):
    try:
        import subprocess
        result = subprocess.run(
            ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
            capture_output=True,
            text=True,
            timeout=1
        )
        if result.returncode == 0:
            git_branch = result.stdout.strip()
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pass

status = Text()

status.append("🤖 ", style="bold")
status.append(f"{model}", style=colors['model'])
status.append(" │ ", style="dim")

status.append("📁 ", style="bold")
status.append(f"{workspace}", style=colors['directory'])

if git_branch:
    status.append(" │ ", style="dim")
    status.append(f"{git_branch}", style=colors['git'])

status.append(" │ ", style="dim")
status.append("🎯 ", style="bold")
token_style = colors['token_low'] if tokens < 100000 else colors['token_med'] if tokens < 500000 else colors['token_high']
status.append(f"{tokens:,}", style=token_style)

if cost > 0:
    status.append(" │ ", style="dim")
    status.append("💰 ", style="bold")
    cost_style = colors['cost_low'] if cost < 0.10 else colors['cost_med'] if cost < 1.0 else colors['cost_high']
    status.append(f"${cost:.3f}", style=cost_style)

console.print(status)

Python Rich Statusline Installation Example

Complete setup script with Rich library installation and Python version verification

#!/bin/bash
# Installation script for Python Rich Statusline

# Check Python version
if ! command -v python3 &> /dev/null; then
    echo "Python 3 is required but not installed"
    echo "Install Python 3: macOS (brew install python3), Linux (sudo apt-get install python3 or sudo yum install python3)"
    exit 1
fi

PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}')
echo "Python version: $PYTHON_VERSION"

# Check if Rich is installed
if ! python3 -c "import rich" 2>/dev/null; then
    echo "Installing Rich library..."
    python3 -m pip install rich --user || python3 -m pip install rich
    echo "Rich library installed"
else
    echo "Rich library already installed"
fi

# Verify Rich installation
if python3 -c "import rich" 2>/dev/null; then
    RICH_VERSION=$(python3 -c "import rich; print(rich.__version__)" 2>/dev/null || echo "unknown")
    echo "Rich version: $RICH_VERSION"
else
    echo "Error: Rich library installation failed"
    exit 1
fi

# Test emoji support
if python3 -c "print('🤖 📁 🎯 💰')" &> /dev/null; then
    echo "Emoji support verified: 🤖 📁 🎯 💰"
else
    echo "Warning: Emoji support may not be available"
fi

# Test Rich console
if python3 -c "from rich.console import Console; Console().print('[bold green]Test[/bold green]')" &> /dev/null; then
    echo "Rich console working"
else
    echo "Warning: Rich console test failed"
fi

# Check for truecolor support
if [ -n "$COLORTERM" ] && [ "$COLORTERM" = "truecolor" ]; then
    echo "Truecolor support enabled"
else
    echo "Note: Set COLORTERM=truecolor for best color support"
fi

# Create statuslines directory
mkdir -p .claude/statuslines

cat > .claude/statuslines/python-rich-statusline.py << 'SCRIPT_EOF'
#!/usr/bin/env python3

import sys
import json
import os
from rich.console import Console
from rich.text import Text

console = Console()

try:
    data = json.load(sys.stdin)
except json.JSONDecodeError:
    console.print("[red]Error: Invalid JSON input[/red]")
    sys.exit(1)

model = data.get('model', {}).get('display_name') or data.get('model', {}).get('id') or 'unknown'
workspace_path = data.get('workspace', {}).get('current_dir') or data.get('workspace', {}).get('path') or data.get('cwd', '~')
workspace = workspace_path.replace(os.path.expanduser('~'), '~')
workspace = os.path.basename(workspace)

tokens = data.get('cost', {}).get('total_lines_added') or data.get('session', {}).get('totalTokens', 0)
cost = data.get('cost', {}).get('total_cost_usd') or data.get('session', {}).get('estimatedCost', 0.0)

git_branch = None
if os.path.exists('.git'):
    try:
        import subprocess
        result = subprocess.run(
            ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
            capture_output=True,
            text=True,
            timeout=1
        )
        if result.returncode == 0:
            git_branch = result.stdout.strip()
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pass

status = Text()

status.append("🤖 ", style="bold")
status.append(f"{model}", style="bold cyan")
status.append(" │ ", style="dim")

status.append("📁 ", style="bold")
status.append(f"{workspace}", style="yellow")

if git_branch:
    status.append(" │ ", style="dim")
    status.append(f"{git_branch}", style="magenta")

status.append(" │ ", style="dim")
status.append("🎯 ", style="bold")
token_style = "green" if tokens < 100000 else "yellow" if tokens < 500000 else "red"
status.append(f"{tokens:,}", style=token_style)

if cost > 0:
    status.append(" │ ", style="dim")
    status.append("💰 ", style="bold")
    cost_color = "green" if cost < 0.10 else "yellow" if cost < 1.0 else "red"
    status.append(f"${cost:.3f}", style=cost_color)

console.print(status)
SCRIPT_EOF

chmod +x .claude/statuslines/python-rich-statusline.py

# Add to settings.json
if [ ! -f .claude/settings.json ]; then
    echo '{"statusLine":{"type":"command","command":"$CLAUDE_PROJECT_DIR/.claude/statuslines/python-rich-statusline.py","refreshInterval":1000}}' > .claude/settings.json
else
    jq '.statusLine = {"type":"command","command":"$CLAUDE_PROJECT_DIR/.claude/statuslines/python-rich-statusline.py","refreshInterval":1000}' .claude/settings.json > .claude/settings.json.tmp
    mv .claude/settings.json.tmp .claude/settings.json
fi

echo "Python Rich Statusline installed successfully!"
echo "Note: Ensure terminal supports truecolor for best color rendering"
echo "Set with: export COLORTERM=truecolor"

Troubleshooting

ModuleNotFoundError: No module named 'rich'

Install the Rich library: pip3 install rich or python3 -m pip install rich. Verify installation: python3 -c 'import rich'. Check Python path: python3 -c 'import sys; print(sys.path)'. If using virtual environment, activate it first. For user installation: python3 -m pip install --user rich. Verify Rich version: python3 -c 'import rich; print(rich.version)'.

Emojis displaying as boxes or not rendering

Ensure terminal supports Unicode emojis. Use iTerm2, Kitty, or Windows Terminal. Test with: python3 -c 'print("🤖 Test")'. Verify terminal encoding: locale charmap (should be UTF-8). Set encoding: export LANG=en_US.UTF-8. Check font supports emojis: Install emoji-capable font (e.g., Noto Color Emoji). Test emoji rendering: python3 -c "from rich.console import Console; Console().print('🤖 📁 🎯 💰')".

Colors look washed out or incorrect

Enable truecolor support. Set COLORTERM=truecolor environment variable or use a terminal that supports 24-bit color. Verify truecolor: python3 -c "from rich.console import Console; c = Console(); print(c.is_terminal, c.color_system)". Check terminal: iTerm2, Kitty, Windows Terminal support truecolor. Test colors: python3 -c "from rich.console import Console; Console().print('[bold red]Red[/bold red] [bold green]Green[/bold green]')". If still issues, check terminal color settings.

Script execution too slow

Rich has some startup overhead. Consider increasing refreshInterval to 2000-3000ms or use the minimal-powerline statusline instead. Check Python startup time: time python3 -c 'import rich'. Optimize imports: Only import what's needed (from rich.console import Console, from rich.text import Text). Consider using faster alternatives: Use bash statusline for better performance. Profile script: python3 -m cProfile script.py to identify bottlenecks.

Python version error or incompatible

Rich requires Python 3.7+. Check Python version: python3 --version (should be 3.7+). Verify Python 3: which python3 (should return path). If Python 3.6 or older, upgrade: macOS (brew install python3), Linux (sudo apt-get install python3.8 or newer). Check if python3 points to correct version: python3 -c 'import sys; print(sys.version)'.

Git branch not detected despite being in repository

Check Git installed: git --version. Verify subprocess works: python3 -c "import subprocess; print(subprocess.run(['git', '--version'], capture_output=True, text=True).stdout)". Check timeout: Script uses 1 second timeout - increase if Git is slow. Verify Git command: python3 -c "import subprocess; result = subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], capture_output=True, text=True, timeout=1); print(result.stdout)". Check .git directory: os.path.exists('.git') should return True.

JSON parsing errors or invalid input

Verify JSON input: echo '$input' | python3 -m json.tool (should parse without errors). Check JSON structure: echo '$input' | python3 -c "import json, sys; data = json.load(sys.stdin); print(data.keys())". Verify stdin: Script reads from sys.stdin - ensure JSON is piped correctly. Test with sample JSON: echo '{"model":{"display_name":"test"}}' | python3 script.py. Check error handling: Script should print error message and exit gracefully on invalid JSON.

Token count or cost showing as 0 or incorrect values

Check JSON field names: echo '$input' | python3 -c "import json, sys; data = json.load(sys.stdin); print(data.get('cost', {}).get('total_cost_usd'))". Verify field extraction: Script checks multiple field names (cost.total_cost_usd, session.estimatedCost). Check if fields exist: echo '$input' | python3 -c "import json, sys; data = json.load(sys.stdin); print('cost' in data, 'session' in data)". Verify data structure: Some Claude Code versions may use different field names. Update script to check additional field names if needed.

#python#rich#advanced#cost-tracking#progress#colorful

Source citations

Signals

Loading live community signals…

More like this, weekly

A short, calm digest of reviewed Claude resources. Unsubscribe any time.