Created
February 5, 2026 20:32
-
-
Save vivekhaldar/ce65d62398e90905bdd3ae31a3260bcf to your computer and use it in GitHub Desktop.
Dynamic statusline script for Claude Code CLI — shows git status, context usage, model info, and estimated cost
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Read JSON input from stdin | |
| input=$(cat) | |
| # Extract data from JSON | |
| current_dir=$(echo "$input" | jq -r '.workspace.current_dir') | |
| model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"') | |
| output_style=$(echo "$input" | jq -r '.output_style.name // ""') | |
| # Change to the current directory | |
| cd "$current_dir" 2>/dev/null || exit 1 | |
| # Build status line components (matching Starship config) | |
| status_parts=() | |
| # Username (only show if not default user) | |
| current_user=$(whoami) | |
| if [[ "$current_user" != "your_username" ]]; then | |
| status_parts+=("$(printf '\033[1;33m%s\033[0m' "$current_user")") | |
| fi | |
| # Hostname (trimmed at .local, matching Starship) | |
| hostname_short=$(hostname -s | sed 's/\.local$//') | |
| if [[ -n "$hostname_short" ]]; then | |
| status_parts+=("$(printf 'at \033[1;32m%s\033[0m' "$hostname_short")") | |
| fi | |
| # Directory (use basename if in repo, matching truncate_to_repo) | |
| if git rev-parse --git-dir >/dev/null 2>&1; then | |
| git_root=$(git rev-parse --show-toplevel 2>/dev/null) | |
| if [[ "$current_dir" == "$git_root" ]]; then | |
| dir_display=$(basename "$current_dir") | |
| else | |
| rel_path=${current_dir#$git_root/} | |
| dir_display="$(basename "$git_root")/$rel_path" | |
| fi | |
| else | |
| # Show last 3 components | |
| dir_display=$(echo "$current_dir" | awk -F/ '{if (NF<=3) print $0; else print $(NF-2)"/"$(NF-1)"/"$NF}') | |
| fi | |
| status_parts+=("$(printf 'in \033[1;36m%s\033[0m' "$dir_display")") | |
| # Git branch (if in a repo) | |
| if git rev-parse --git-dir >/dev/null 2>&1; then | |
| git_branch=$(git symbolic-ref --short HEAD 2>/dev/null) | |
| if [[ -n "$git_branch" ]]; then | |
| status_parts+=("$(printf 'on \033[1;35m%s\033[0m' "$git_branch")") | |
| # Git status (matching Starship symbols) | |
| git_status=$(git status --porcelain 2>/dev/null) | |
| if [[ -n "$git_status" ]]; then | |
| status_symbols="" | |
| [[ "$git_status" =~ ^\?\? ]] && status_symbols+="?" | |
| [[ "$git_status" =~ ^[MADRC] ]] && status_symbols+="+" | |
| [[ "$git_status" =~ ^.[MD] ]] && status_symbols+="!" | |
| if [[ -n "$status_symbols" ]]; then | |
| status_parts+=("$(printf '\033[1;31m%s\033[0m' "$status_symbols")") | |
| fi | |
| fi | |
| # Git ahead/behind (skip optional locks for performance) | |
| upstream=$(git rev-parse --abbrev-ref @{upstream} 2>/dev/null) | |
| if [[ -n "$upstream" ]]; then | |
| ahead=$(git rev-list --count --no-optional-locks "$upstream"..HEAD 2>/dev/null || echo "0") | |
| behind=$(git rev-list --count --no-optional-locks HEAD.."$upstream" 2>/dev/null || echo "0") | |
| if [[ "$ahead" -gt 0 ]]; then | |
| status_parts+=("$(printf '\033[1;31mahead %s\033[0m' "$ahead")") | |
| fi | |
| if [[ "$behind" -gt 0 ]]; then | |
| status_parts+=("$(printf '\033[1;31mbehind %s\033[0m' "$behind")") | |
| fi | |
| fi | |
| fi | |
| fi | |
| # Python version (if pyproject.toml or .py files exist) | |
| if [[ -f "pyproject.toml" ]] || ls *.py >/dev/null 2>&1; then | |
| python_version=$(python3 --version 2>/dev/null | awk '{print $2}') | |
| if [[ -n "$python_version" ]]; then | |
| status_parts+=("$(printf 'via \033[1;33mpy:%s\033[0m' "$python_version")") | |
| fi | |
| fi | |
| # Context window usage | |
| total_input=$(echo "$input" | jq -r '.context_window.total_input_tokens // 0') | |
| total_output=$(echo "$input" | jq -r '.context_window.total_output_tokens // 0') | |
| context_size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000') | |
| total_tokens=$((total_input + total_output)) | |
| if [[ $context_size -gt 0 ]]; then | |
| usage_pct=$((total_tokens * 100 / context_size)) | |
| else | |
| usage_pct=0 | |
| fi | |
| # Format tokens with K suffix for readability | |
| if [[ $total_tokens -ge 1000 ]]; then | |
| tokens_display="$((total_tokens / 1000))K" | |
| else | |
| tokens_display="$total_tokens" | |
| fi | |
| # Pricing (approximate, based on Claude 3.5 Sonnet pricing) | |
| # Input: $3 per 1M tokens, Output: $15 per 1M tokens | |
| cost_input=$(echo "scale=4; $total_input * 3 / 1000000" | bc 2>/dev/null || echo "0") | |
| cost_output=$(echo "scale=4; $total_output * 15 / 1000000" | bc 2>/dev/null || echo "0") | |
| total_cost=$(echo "scale=2; $cost_input + $cost_output" | bc 2>/dev/null || echo "0") | |
| # Model and output style info (Claude-specific additions) | |
| if [[ -n "$output_style" ]]; then | |
| status_parts+=("$(printf '[\033[1;34m%s\033[0m|\033[1;36m%s\033[0m]' "$model_name" "$output_style")") | |
| else | |
| status_parts+=("$(printf '[\033[1;34m%s\033[0m]' "$model_name")") | |
| fi | |
| # Add context usage and cost | |
| status_parts+=("$(printf '\033[1;33m%s tok\033[0m \033[1;32m%d%%\033[0m \033[1;36m$%s\033[0m' "$tokens_display" "$usage_pct" "$total_cost")") | |
| # Join all parts with spaces | |
| printf '%s' "${status_parts[*]}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment