Skip to content

Instantly share code, notes, and snippets.

@glauberlima
Last active December 29, 2025 23:55
Show Gist options
  • Select an option

  • Save glauberlima/6422ca2a55eb91dc08ec3d3456a4d5c0 to your computer and use it in GitHub Desktop.

Select an option

Save glauberlima/6422ca2a55eb91dc08ec3d3456a4d5c0 to your computer and use it in GitHub Desktop.
Custom Status Line for Claude Code - displays directory, model, context usage bar, and git stats

Custom Status Line for Claude Code

A custom status line script for Claude Code CLI that displays directory, model name, context usage with a visual progress bar, and comprehensive git repository statistics.

For more information about how status lines work in Claude Code, see the official documentation.

Inspirations

This script was inspired by these Claude Code status line implementations:

Prerequisites

This script requires jq (command-line JSON processor):

macOS:

brew install jq

Linux (Debian/Ubuntu):

sudo apt-get install jq

Linux (RHEL/CentOS/Fedora):

sudo yum install jq

Installation

  1. Download the script and save it to your Claude directory:
curl -o ~/.claude/statusline.sh https://gist.githubusercontent.com/glauberlima/6422ca2a55eb91dc08ec3d3456a4d5c0/raw/statusline.sh

Or manually create the file at ~/.claude/statusline.sh and paste the script content.

  1. Make the script executable:
chmod +x ~/.claude/statusline.sh

Configuration

Configure Claude Code to use the custom status line by editing your settings file:

  1. Open your Claude Code settings:
nano ~/.claude/settings.json
  1. Add or update the statusLine property:
{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 0
  }
}
  1. Restart Claude Code for changes to take effect.

Preview

screenshot

Features

The status line displays:

  • Current directory name (in blue)
  • Model name (in cyan)
  • Context usage with visual progress bar (15-character bar with percentage)
  • Git information (when in a repository):
    • Current branch name
    • Commits ahead/behind upstream (↑ green, ↓ red)
    • Number of modified files
    • Lines added (green)
    • Lines removed (red)

License

MIT

#!/bin/bash
set -euo pipefail
# ============================================================
# CONFIGURATION
# ============================================================
readonly BAR_WIDTH=15
readonly BAR_FILLED="█"
readonly BAR_EMPTY="░"
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly BLUE='\033[0;34m'
readonly MAGENTA='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly ORANGE='\033[0;33m'
readonly GRAY='\033[0;90m'
readonly NC='\033[0m'
# ============================================================
# FUNCTIONS
# ============================================================
parse_claude_input() {
local input="$1"
local parsed
parsed=$(echo "$input" | jq -r '
.model.display_name,
.workspace.current_dir,
(.context_window.context_window_size // 200000),
(.context_window.total_input_tokens + .context_window.total_output_tokens)
' 2>/dev/null) || {
echo "Error: Failed to parse JSON input" >&2
return 1
}
echo "$parsed"
}
build_progress_bar() {
local percent="$1"
local width="${2:-$BAR_WIDTH}"
local filled=$((percent * width / 100))
local empty=$((width - filled))
local bar empty_bar
printf -v bar '%*s' "$filled" ''
bar=${bar// /$BAR_FILLED}
printf -v empty_bar '%*s' "$empty" ''
echo "${bar}${empty_bar// /$BAR_EMPTY}"
}
get_ahead_behind() {
local git_opts=("$@")
local upstream counts
upstream=$(git "${git_opts[@]}" rev-parse --abbrev-ref '@{upstream}' 2>/dev/null) && \
counts=$(git "${git_opts[@]}" rev-list --left-right --count HEAD..."$upstream" 2>/dev/null) && \
echo "$counts" | awk '{print $1 "|" $2}' || echo "0|0"
}
append_ahead_behind() {
local ahead="$1"
local behind="$2"
local output=""
if [ "$ahead" -gt 0 ] || [ "$behind" -gt 0 ]; then
output="${GRAY}|${NC}"
[ "$ahead" -gt 0 ] && output+=" ${GREEN}↑${ahead}${NC}"
[ "$behind" -gt 0 ] && output+=" ${RED}↓${behind}${NC}"
fi
echo "$output"
}
get_git_stats() {
local current_dir="$1"
local git_opts=()
# Build git -C option if directory specified
if [ -n "$current_dir" ] && [ "$current_dir" != "null" ]; then
git_opts=(-C "$current_dir")
fi
git "${git_opts[@]}" rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "not_repo"
return 0
}
local branch
branch=$(git "${git_opts[@]}" branch --show-current 2>/dev/null || echo "detached")
local status_output
status_output=$(git "${git_opts[@]}" status --porcelain 2>/dev/null) || {
echo "not_repo"
return 0
}
local ahead_behind
ahead_behind=$(get_ahead_behind "${git_opts[@]}")
# Clean state
if [ -z "$status_output" ]; then
echo "clean|$branch|$ahead_behind"
return 0
fi
# Dirty state: count files and lines
local total_files
total_files=$(echo "$status_output" | wc -l | tr -d ' ')
# Combine staged and unstaged diffs in single awk pass
local added removed
read -r added removed < <(
{
git "${git_opts[@]}" diff --numstat --cached 2>/dev/null
git "${git_opts[@]}" diff --numstat 2>/dev/null
} | awk '{added+=$1; removed+=$2} END {print added+0, removed+0}'
)
echo "dirty|$branch|$total_files|$added|$removed|$ahead_behind"
}
format_git_info() {
local git_data="$1"
local state branch rest
IFS='|' read -r state branch rest <<< "$git_data"
case "$state" in
not_repo)
echo " ${ORANGE}(not a git repository)${NC}"
;;
clean)
local ahead_behind="$rest"
local ahead behind
IFS='|' read -r ahead behind <<< "$ahead_behind"
local output="${GRAY}(${NC}${MAGENTA}$branch${NC}"
local ahead_behind_str
ahead_behind_str=$(append_ahead_behind "$ahead" "$behind")
[ -n "$ahead_behind_str" ] && output+=" $ahead_behind_str"
output+="${GRAY})${NC}"
echo " $output"
;;
dirty)
local total_files added removed ahead_behind
IFS='|' read -r total_files added removed ahead_behind <<< "$rest"
local ahead behind
IFS='|' read -r ahead behind <<< "$ahead_behind"
local output="${GRAY}(${NC}${MAGENTA}$branch${NC} ${GRAY}|${NC} ${GRAY}${total_files} files${NC}"
[ "$added" -gt 0 ] && output+=" ${GREEN}+${added}${NC}"
[ "$removed" -gt 0 ] && output+=" ${RED}-${removed}${NC}"
local ahead_behind_str
ahead_behind_str=$(append_ahead_behind "$ahead" "$behind")
[ -n "$ahead_behind_str" ] && output+=" $ahead_behind_str"
output+="${GRAY})${NC}"
echo " $output"
;;
esac
}
# ============================================================
# MAIN
# ============================================================
main() {
command -v jq >/dev/null 2>&1 || {
echo "Error: jq required" >&2
exit 1
}
local input
input=$(< /dev/stdin)
local parsed
parsed=$(parse_claude_input "$input") || exit 1
local model_name current_dir context_size current_usage
{
read -r model_name
read -r current_dir
read -r context_size
read -r current_usage
} <<< "$parsed"
# Calculate context percentage
local context_percent=0
if [[ "$current_usage" != "0" && "$context_size" -gt 0 ]]; then
context_percent=$((current_usage * 100 / context_size))
fi
# Build progress bar
local bar
bar=$(build_progress_bar "$context_percent" "$BAR_WIDTH")
# Extract directory name
local dir_name
if [ -n "$current_dir" ] && [ "$current_dir" != "null" ]; then
dir_name=$(basename "$current_dir")
else
dir_name=$(basename "$PWD")
fi
# Get git information
local git_data git_info
git_data=$(get_git_stats "$current_dir")
git_info=$(format_git_info "$git_data")
# Build final output
local context_info="${GRAY}${bar}${NC} ${context_percent}%"
echo -e "${BLUE}${dir_name}${NC} ${GRAY}|${NC} ${CYAN}${model_name}${NC} ${GRAY}|${NC} ${context_info}${git_info:+ ${GRAY}|${NC}}${git_info}"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment