-
-
Save eladcandroid/bc8978b5f52853772740641e19bc13a8 to your computer and use it in GitHub Desktop.
statusline command for Claude Code
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
| #!/bin/bash | |
| # Make sure this file is executable: chmod +x ~/.claude/statusline-command.sh | |
| # Claude Code statusline script - optimized with colors and emojis | |
| # Reads JSON input from stdin and outputs a formatted status line to stdout | |
| # Add to your ~/.claude/settings.json | |
| # | |
| # "statusLine": { | |
| # "type": "command", | |
| # "command": "bash ~/.claude/statusline-command.sh" | |
| # } | |
| # | |
| # SYMBOL LEGEND: | |
| # π€ Model indicator | |
| # π Current directory | |
| # Git status icons: | |
| # β Clean repository (green) | |
| # β‘ Changes present (yellow) | |
| # β Merge conflicts (red) | |
| # +N Staged files count (green) | |
| # ~N Modified files count (yellow) | |
| # ?N Untracked files count (gray) | |
| # βN Commits ahead of remote (green) | |
| # βN Commits behind remote (yellow) | |
| # βN/M Diverged from remote (yellow) | |
| # PR#N Open pull request number (cyan) | |
| # π³ Git worktree indicator | |
| # π Python virtual environment | |
| # β¬’ Node.js version | |
| # π³ Docker environment detected | |
| # π Current time | |
| # Debug mode - set STATUSLINE_DEBUG=1 to see raw values | |
| DEBUG="${STATUSLINE_DEBUG:-0}" | |
| # Color codes for better visual separation | |
| readonly BLUE='\033[94m' # Bright blue for model/main info | |
| readonly GREEN='\033[92m' # Bright green for clean git status | |
| readonly YELLOW='\033[93m' # Bright yellow for modified git status | |
| readonly RED='\033[91m' # Bright red for conflicts/errors | |
| readonly PURPLE='\033[95m' # Bright purple for directory | |
| readonly CYAN='\033[96m' # Bright cyan for python venv | |
| readonly WHITE='\033[97m' # Bright white for time | |
| readonly GRAY='\033[37m' # Gray for separators | |
| readonly RESET='\033[0m' # Reset colors | |
| readonly BOLD='\033[1m' # Bold text | |
| # Read JSON input from stdin | |
| input=$(cat) | |
| # Extract data from JSON input using jq | |
| model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"') | |
| current_dir=$(echo "$input" | jq -r '.workspace.current_dir // "."') | |
| # Get current directory relative to home directory with smart truncation | |
| if [[ "$current_dir" == "$HOME"* ]]; then | |
| # Replace home path with ~ for display | |
| dir_display="${current_dir/#$HOME/~}" | |
| else | |
| # Keep full path if not under home directory | |
| dir_display="$current_dir" | |
| fi | |
| # Smart path truncation - show beginning and end for context | |
| if [[ ${#dir_display} -gt 50 ]]; then | |
| # For very long paths, show important parts | |
| if [[ "$dir_display" == "~"/* ]]; then | |
| # Keep home indicator and last parts | |
| # shellcheck disable=SC2088 # We want literal ~ for display, not expansion | |
| dir_display="~/.../$(basename "$(dirname "$current_dir")")/$(basename "$current_dir")" | |
| else | |
| # Show first and last directory | |
| dir_display="...$(echo "$dir_display" | grep -o '/[^/]*/[^/]*$')" | |
| fi | |
| # If still too long, just show current directory | |
| if [[ ${#dir_display} -gt 50 ]]; then | |
| dir_display="...$(basename "$current_dir")" | |
| fi | |
| fi | |
| # Get git status and worktree information with enhanced detection | |
| git_info="" | |
| if git -C "$current_dir" rev-parse --git-dir >/dev/null 2>&1; then | |
| branch=$(git -C "$current_dir" branch --show-current 2>/dev/null) | |
| # If no branch (detached HEAD), show short commit hash | |
| if [[ -z "$branch" ]]; then | |
| branch=$(git -C "$current_dir" rev-parse --short HEAD 2>/dev/null) | |
| branch="detached:${branch}" | |
| fi | |
| # Enhanced worktree detection | |
| worktree_info="" | |
| git_dir=$(git -C "$current_dir" rev-parse --git-dir 2>/dev/null) | |
| # Check if we're in a worktree | |
| if [[ "$git_dir" == *".git/worktrees/"* ]] || [[ -f "$git_dir/gitdir" ]]; then | |
| worktree_name=$(basename "$current_dir") | |
| # Only show worktree indicator if it adds information | |
| # Don't show if branch name already contains the worktree info | |
| if [[ "$worktree_name" =~ ^TOK ]] && [[ "$branch" != *"$worktree_name"* ]]; then | |
| # For TOK worktrees, just show the tree emoji | |
| worktree_info=" ${CYAN}π³${RESET}" | |
| elif [[ ! "$worktree_name" =~ ^TOK ]] && [[ "$branch" != "$worktree_name" ]]; then | |
| # For other worktrees, just show tree emoji | |
| worktree_info=" ${CYAN}π³${RESET}" | |
| fi | |
| fi | |
| if [[ -n "$branch" ]]; then | |
| # Comprehensive git status check | |
| # Git status format: XY filename | |
| # X = status of staging area, Y = status of working tree | |
| git_status=$(git -C "$current_dir" status --porcelain 2>/dev/null) | |
| # Count different types of changes (handle empty status gracefully) | |
| if [[ -n "$git_status" ]]; then | |
| # Count untracked files (starts with ??) | |
| untracked=$(echo "$git_status" | grep -c '^??') | |
| # Count modified files (M in second column or first column with space) | |
| modified=$(echo "$git_status" | grep -c '^.M\|^ M') | |
| # Count staged files (non-space in first column, excluding ??) | |
| staged=$(echo "$git_status" | grep -c '^[ADMR]') | |
| # Count conflicts (UU, AA, DD) | |
| conflicts=$(echo "$git_status" | grep -c '^UU\|^AA\|^DD') | |
| else | |
| untracked=0 | |
| modified=0 | |
| staged=0 | |
| conflicts=0 | |
| fi | |
| # Debug output | |
| if [[ "$DEBUG" == "1" ]]; then | |
| echo "DEBUG: Git Status Raw:" >&2 | |
| echo "$git_status" >&2 | |
| echo "DEBUG: Counts - Staged:$staged Modified:$modified Untracked:$untracked Conflicts:$conflicts" >&2 | |
| fi | |
| # Check for ahead/behind status | |
| ahead_behind="" | |
| upstream=$(git -C "$current_dir" rev-parse --abbrev-ref '@{u}' 2>/dev/null) | |
| if [[ -n "$upstream" ]]; then | |
| ahead=$(git -C "$current_dir" rev-list --count '@{u}..HEAD' 2>/dev/null) | |
| behind=$(git -C "$current_dir" rev-list --count 'HEAD..@{u}' 2>/dev/null) | |
| if [[ "$ahead" -gt 0 ]] && [[ "$behind" -gt 0 ]]; then | |
| ahead_behind=" ${YELLOW}β${ahead}/${behind}${RESET}" | |
| elif [[ "$ahead" -gt 0 ]]; then | |
| ahead_behind=" ${GREEN}β${ahead}${RESET}" | |
| elif [[ "$behind" -gt 0 ]]; then | |
| ahead_behind=" ${YELLOW}β${behind}${RESET}" | |
| fi | |
| fi | |
| # Check for open PRs using GitHub CLI if available | |
| pr_info="" | |
| if command -v gh >/dev/null 2>&1; then | |
| # Only check for PRs if we're in a GitHub repo | |
| remote_url=$(git -C "$current_dir" config --get remote.origin.url 2>/dev/null) | |
| if [[ "$remote_url" == *"github.com"* ]]; then | |
| # Quick PR check (gh caches this, so it's usually fast after first run) | |
| pr_number=$(gh pr view --json number -q .number 2>/dev/null) | |
| if [[ -n "$pr_number" ]]; then | |
| pr_info=" ${CYAN}PR#${pr_number}${RESET}" | |
| fi | |
| fi | |
| fi | |
| # Build status indicators | |
| status_indicators="" | |
| if [[ "$conflicts" -gt 0 ]]; then | |
| git_color="${RED}" | |
| git_icon="β " | |
| status_indicators="${RED}β οΈ ${conflicts} conflicts${RESET}" | |
| elif [[ -n "$git_status" ]]; then | |
| git_color="${YELLOW}" | |
| git_icon="β‘" | |
| # Build status string with descriptive text | |
| status_parts=() | |
| if [[ "$untracked" -gt 0 ]]; then | |
| status_parts+=("${GRAY}π ${untracked} untracked${RESET}") | |
| fi | |
| if [[ "$modified" -gt 0 ]]; then | |
| status_parts+=("${YELLOW}π ${modified} modified${RESET}") | |
| fi | |
| if [[ "$staged" -gt 0 ]]; then | |
| status_parts+=("${GREEN}β ${staged} staged${RESET}") | |
| fi | |
| # Join status parts with commas | |
| if [[ ${#status_parts[@]} -gt 0 ]]; then | |
| status_indicators=$(IFS=", "; echo "${status_parts[*]}") | |
| fi | |
| else | |
| git_color="${GREEN}" | |
| git_icon="β " | |
| fi | |
| # Construct git info string with separate sections | |
| git_branch_info="${git_color}${git_icon} ${branch}${RESET}${worktree_info}${ahead_behind}${pr_info}" | |
| if [[ -n "$status_indicators" ]]; then | |
| # Has changes - show branch and status in separate sections | |
| git_info=" ${GRAY}β${RESET} ${git_branch_info} ${GRAY}β${RESET} ${status_indicators}" | |
| else | |
| # Clean repository - just show branch | |
| git_info=" ${GRAY}β${RESET} ${git_branch_info}" | |
| fi | |
| fi | |
| fi | |
| # Get Python virtual environment info | |
| venv_info="" | |
| if [[ -n "$VIRTUAL_ENV" ]]; then | |
| venv_name=$(basename "$VIRTUAL_ENV") | |
| # Smart truncation for long venv names | |
| if [[ ${#venv_name} -gt 15 ]]; then | |
| venv_name="${venv_name:0:12}..." | |
| fi | |
| venv_info=" ${GRAY}β${RESET} ${CYAN}π${venv_name}${RESET}" | |
| fi | |
| # Get Node.js version if in a Node project | |
| node_info="" | |
| if [[ -f "$current_dir/package.json" ]]; then | |
| node_version=$(node --version 2>/dev/null | sed 's/v//') | |
| if [[ -n "$node_version" ]]; then | |
| # Truncate to major.minor version | |
| node_version=${node_version%.*} | |
| node_info=" ${GRAY}β${RESET} ${GREEN}β¬’ ${node_version}${RESET}" | |
| fi | |
| fi | |
| # Get current time with better formatting | |
| current_time="$(date '+%H:%M')" | |
| # Build output string with smart separators | |
| output_string=" ${BLUE}π€${RESET} ${BOLD}${BLUE}${model_name}${RESET} ${GRAY}β${RESET} ${PURPLE}π${dir_display}${RESET}" | |
| # Add git info if present | |
| [[ -n "$git_info" ]] && output_string="${output_string}${git_info}" | |
| # Add other components with separators only when needed | |
| extra_items="" | |
| # Collect non-empty items | |
| [[ -n "$venv_info" ]] && extra_items="${extra_items} ${venv_info}" | |
| [[ -n "$node_info" ]] && extra_items="${extra_items} ${node_info}" | |
| # Add extra items with separator if there are any | |
| if [[ -n "$extra_items" ]]; then | |
| output_string="${output_string} ${GRAY}β${RESET}${extra_items}" | |
| fi | |
| # Always add time at the end | |
| output_string="${output_string} ${GRAY}β${RESET} ${WHITE}π${current_time}${RESET} " | |
| # Output the complete string | |
| echo -e "$output_string" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment