Skip to content

Instantly share code, notes, and snippets.

@frankdilo
Created December 23, 2025 08:29
Show Gist options
  • Select an option

  • Save frankdilo/e49a680a2c946143868f7f61b45127e1 to your computer and use it in GitHub Desktop.

Select an option

Save frankdilo/e49a680a2c946143868f7f61b45127e1 to your computer and use it in GitHub Desktop.

Claude Code Custom Status Line

A custom status line for Claude Code that displays:

  • Directory name
  • Model name
  • Context usage with progress bar and percentage
  • Git branch with file counts and line changes (added/removed)

Preview

bin | Sonnet 4.5 | ░░░░░░░░░░░░░░░ 12% | (main | 2 files +87 -1)

Setup Instructions

1. Create the status line script

Save this script to ~/.claude/status-line.sh:

#!/bin/bash

# Read JSON input from stdin
input=$(cat)

# Extract information from JSON
model_name=$(echo "$input" | jq -r '.model.display_name')
current_dir=$(echo "$input" | jq -r '.workspace.current_dir')

# Extract context window information
context_size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
current_usage=$(echo "$input" | jq '.context_window.current_usage')

# Calculate context percentage
if [ "$current_usage" != "null" ]; then
    current_tokens=$(echo "$current_usage" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens')
    context_percent=$((current_tokens * 100 / context_size))
else
    context_percent=0
fi

# Build context progress bar (20 chars wide)
bar_width=15
filled=$((context_percent * bar_width / 100))
empty=$((bar_width - filled))
bar=""
for ((i=0; i<filled; i++)); do bar+=""; done
for ((i=0; i<empty; i++)); do bar+=""; done

# Get directory name (basename)
dir_name=$(basename "$current_dir")

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
NC='\033[0m' # No Color

# Change to the current directory to get git info
cd "$current_dir" 2>/dev/null || cd /

# Get git branch
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    branch=$(git branch --show-current 2>/dev/null || echo "detached")

    # Get git status with file counts
    status_output=$(git status --porcelain 2>/dev/null)

    if [ -n "$status_output" ]; then
        # Count files and get basic line stats (including new files)
        total_files=$(echo "$status_output" | wc -l | xargs)

        # Get stats from both staged and unstaged changes
        staged_stats=$(git diff --numstat --cached 2>/dev/null | awk '{added+=$1; removed+=$2} END {print added+0, removed+0}')
        unstaged_stats=$(git diff --numstat 2>/dev/null | awk '{added+=$1; removed+=$2} END {print added+0, removed+0}')

        staged_added=$(echo $staged_stats | cut -d' ' -f1)
        staged_removed=$(echo $staged_stats | cut -d' ' -f2)
        unstaged_added=$(echo $unstaged_stats | cut -d' ' -f1)
        unstaged_removed=$(echo $unstaged_stats | cut -d' ' -f2)

        # Count lines in untracked files (files starting with ??)
        untracked_lines=$(echo "$status_output" | grep '^??' | cut -c4- | xargs -I {} sh -c 'test -f "{}" && wc -l < "{}" || echo 0' 2>/dev/null | awk '{sum+=$1} END {print sum+0}')

        added=$((staged_added + unstaged_added + untracked_lines))
        removed=$((staged_removed + unstaged_removed))

        # Build status display
        git_info=" ${YELLOW}($branch${NC} ${YELLOW}|${NC} ${GRAY}${total_files} files${NC}"

        [ "$added" -gt 0 ] && git_info="${git_info} ${GREEN}+${added}${NC}"
        [ "$removed" -gt 0 ] && git_info="${git_info} ${RED}-${removed}${NC}"

        git_info="${git_info} ${YELLOW})${NC}"
    else
        git_info=" ${YELLOW}($branch)${NC}"
    fi
else
    git_info=""
fi

# Build context bar display
context_info="${GRAY}${bar}${NC} ${context_percent}%"

# Output the status line
echo -e "${BLUE}${dir_name}${NC} ${GRAY}|${NC} ${CYAN}${model_name}${NC} ${GRAY}|${NC} ${context_info}${git_info:+ ${GRAY}|${NC}}${git_info}"

2. Make it executable

chmod +x ~/.claude/status-line.sh

3. Update Claude Code settings

Create or update ~/.claude/settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/status-line.sh"
  }
}

4. Restart Claude Code

The new status line will appear in your next Claude Code session.

Requirements

  • jq - JSON processor (install via brew install jq on macOS)
  • git - for repository information
  • Claude Code CLI

Features

  • Directory name - Shows current working directory basename
  • Model name - Displays which Claude model is active
  • Context usage - Visual progress bar and percentage
  • Git status - Shows branch, file count, and line changes
    • Includes staged, unstaged, and untracked files
    • Shows added (+) and removed (-) line counts with colors
@blksmr
Copy link

blksmr commented Dec 23, 2025

Thanks for that!

I made a few tweaks since I'm on iTerm instead of Ghostty, so the loading bar looks a bit off.
And removed dead code cost extraction that was never used

#!/bin/bash

# Read JSON input from stdin
input=$(cat)

# Extract information from JSON
model_name=$(echo "$input" | jq -r '.model.display_name')
current_dir=$(echo "$input" | jq -r '.workspace.current_dir')
context_size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
current_usage=$(echo "$input" | jq '.context_window.current_usage')

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
NC='\033[0m'
LIGHT_GRAY='\033[38;5;250m'
DARK_GRAY='\033[38;5;238m'

# Calculate context percentage
if [ "$current_usage" != "null" ]; then
    current_tokens=$(echo "$current_usage" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens')
    context_percent=$((current_tokens * 100 / context_size))
else
    context_percent=0
fi

# Build context progress bar (15 chars wide)
bar_width=15
filled=$((context_percent * bar_width / 100))
empty=$((bar_width - filled))

bar=""
for ((i = 0; i < filled; i++)); do bar+="${LIGHT_GRAY}"; done
for ((i = 0; i < empty; i++)); do bar+="${DARK_GRAY}"; done
bar+="${NC}"

# Cost extraction
session_cost=$(echo "$input" | jq -r '.cost.total_cost_usd // empty')
[ "$session_cost" != "empty" ] && session_cost=$(printf "%.4f" "$session_cost") || session_cost=""

# Directory info
dir_name=$(basename "$current_dir")
cd "$current_dir" 2>/dev/null || cd /

# Git information
git_info=""

if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    branch=$(git branch --show-current 2>/dev/null || echo "detached")
    status_output=$(git status --porcelain 2>/dev/null)

    if [ -n "$status_output" ]; then
        total_files=$(echo "$status_output" | wc -l | xargs)

        # Staged changes
        staged_stats=$(git diff --numstat --cached 2>/dev/null | awk '{added+=$1; removed+=$2} END {print added+0, removed+0}')
        staged_added=$(echo "$staged_stats" | cut -d' ' -f1)
        staged_removed=$(echo "$staged_stats" | cut -d' ' -f2)

        # Unstaged changes
        unstaged_stats=$(git diff --numstat 2>/dev/null | awk '{added+=$1; removed+=$2} END {print added+0, removed+0}')
        unstaged_added=$(echo "$unstaged_stats" | cut -d' ' -f1)
        unstaged_removed=$(echo "$unstaged_stats" | cut -d' ' -f2)

        # Untracked files
        untracked_lines=$(echo "$status_output" | grep '^??' | cut -c4- | xargs -I {} sh -c 'test -f "{}" && wc -l < "{}" || echo 0' 2>/dev/null | awk '{sum+=$1} END {print sum+0}')

        added=$((staged_added + unstaged_added + untracked_lines))
        removed=$((staged_removed + unstaged_removed))

        git_info=" ${YELLOW}(${branch}${NC} ${YELLOW}|${NC} ${GRAY}${total_files} files${NC}"
        [ "$added" -gt 0 ] && git_info="${git_info} ${GREEN}+${added}${NC}"
        [ "$removed" -gt 0 ] && git_info="${git_info} ${RED}-${removed}${NC}"
        git_info="${git_info} ${YELLOW})${NC}"
    else
        git_info=" ${YELLOW}(${branch})${NC}"
    fi
fi

# Output
context_info="${bar} ${context_percent}%"
echo -e "${BLUE}${dir_name}${NC} ${GRAY}|${NC} ${CYAN}${model_name}${NC} ${GRAY}|${NC} ${context_info}${git_info:+ ${GRAY}|${NC}}${git_info}"

@faulomi
Copy link

faulomi commented Dec 25, 2025

If auto-compact is enabled, wouldn’t it make sense to add the 45k allocated buffer to the current_tokens calculation ?

@blksmr
Copy link

blksmr commented Dec 25, 2025

If auto-compact is enabled, wouldn’t it make sense to add the 45k allocated buffer to the current_tokens calculation ?

You raise a good point. However, I'd argue the opposite adjustment makes more sense: subtract the buffer from the total, not add it to current usage.

# The current approach: shows % of total context
context_percent=$((current_tokens * 100 / context_size))

# And with auto-compact awareness: shows % of usable context
usable_context=$((context_size - 45000))
context_percent=$((current_tokens * 100 / usable_context))

The real issue is that Claude Code doesn't have an official API or setting to expose the auto-compact status as a boolean (havn't found one 🤷). So you have to pass it manually either hardcoded or as an env var.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment