# Status line configuration > Create a custom status line for Claude Code to display contextual information Make Claude Code your own with a custom status line that displays at the bottom of the Claude Code interface, similar to how terminal prompts (PS1) work in shells like Oh-my-zsh. ## Create a custom status line You can either: - Run `/statusline` to ask Claude Code to help you set up a custom status line. By default, it will try to reproduce your terminal's prompt, but you can provide additional instructions about the behavior you want to Claude Code, such as `/statusline show the model name in orange` - Directly add a `statusLine` command to your `.claude/settings.json`: ```json theme={null} { "statusLine": { "type": "command", "command": "~/.claude/statusline.sh", "padding": 0 // Optional: set to 0 to let status line go to edge } } ``` ## How it Works - The status line is updated when the conversation messages update - Updates run at most every 300 ms - The first line of stdout from your command becomes the status line text - ANSI color codes are supported for styling your status line - Claude Code passes contextual information about the current session (model, directories, etc.) as JSON to your script via stdin ## JSON Input Structure Your status line command receives structured data via stdin in JSON format: ```json theme={null} { "hook_event_name": "Status", "session_id": "abc123...", "transcript_path": "/path/to/transcript.json", "cwd": "/current/working/directory", "model": { "id": "claude-opus-4-1", "display_name": "Opus" }, "workspace": { "current_dir": "/current/working/directory", "project_dir": "/original/project/directory" }, "version": "1.0.80", "output_style": { "name": "default" }, "cost": { "total_cost_usd": 0.01234, "total_duration_ms": 45000, "total_api_duration_ms": 2300, "total_lines_added": 156, "total_lines_removed": 23 }, "context_window": { "total_input_tokens": 15234, "total_output_tokens": 4521, "context_window_size": 200000, "current_usage": { "input_tokens": 8500, "output_tokens": 1200, "cache_creation_input_tokens": 5000, "cache_read_input_tokens": 2000 } } } ``` ## Example Scripts ### Simple Status Line ```bash theme={null} #!/bin/bash # Read JSON input from stdin input=$(cat) # Extract values using jq MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name') CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir') echo "[$MODEL_DISPLAY] 📁 ${CURRENT_DIR##*/}" ``` ### Git-Aware Status Line ```bash theme={null} #!/bin/bash # Read JSON input from stdin input=$(cat) # Extract values using jq MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name') CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir') # Show git branch if in a git repo GIT_BRANCH="" if git rev-parse --git-dir > /dev/null 2>&1; then BRANCH=$(git branch --show-current 2>/dev/null) if [ -n "$BRANCH" ]; then GIT_BRANCH=" | 🌿 $BRANCH" fi fi echo "[$MODEL_DISPLAY] 📁 ${CURRENT_DIR##*/}$GIT_BRANCH" ``` ### Python Example ```python theme={null} #!/usr/bin/env python3 import json import sys import os # Read JSON from stdin data = json.load(sys.stdin) # Extract values model = data['model']['display_name'] current_dir = os.path.basename(data['workspace']['current_dir']) # Check for git branch git_branch = "" if os.path.exists('.git'): try: with open('.git/HEAD', 'r') as f: ref = f.read().strip() if ref.startswith('ref: refs/heads/'): git_branch = f" | 🌿 {ref.replace('ref: refs/heads/', '')}" except: pass print(f"[{model}] 📁 {current_dir}{git_branch}") ``` ### Node.js Example ```javascript theme={null} #!/usr/bin/env node const fs = require("fs"); const path = require("path"); // Read JSON from stdin let input = ""; process.stdin.on("data", (chunk) => input += chunk); process.stdin.on("end", () => { const data = JSON.parse(input); // Extract values const model = data.model.display_name; const currentDir = path.basename(data.workspace.current_dir); // Check for git branch let gitBranch = ""; try { const headContent = fs.readFileSync(".git/HEAD", "utf8").trim(); if (headContent.startsWith("ref: refs/heads/")) { gitBranch = ` | 🌿 ${headContent.replace("ref: refs/heads/", "")}`; } } catch (e) { // Not a git repo or can't read HEAD } console.log(`[${model}] 📁 ${currentDir}${gitBranch}`); }); ``` ### Helper Function Approach For more complex bash scripts, you can create helper functions: ```bash theme={null} #!/bin/bash # Read JSON input once input=$(cat) # Helper functions for common extractions get_model_name() { echo "$input" | jq -r '.model.display_name'; } get_current_dir() { echo "$input" | jq -r '.workspace.current_dir'; } get_project_dir() { echo "$input" | jq -r '.workspace.project_dir'; } get_version() { echo "$input" | jq -r '.version'; } get_cost() { echo "$input" | jq -r '.cost.total_cost_usd'; } get_duration() { echo "$input" | jq -r '.cost.total_duration_ms'; } get_lines_added() { echo "$input" | jq -r '.cost.total_lines_added'; } get_lines_removed() { echo "$input" | jq -r '.cost.total_lines_removed'; } get_input_tokens() { echo "$input" | jq -r '.context_window.total_input_tokens'; } get_output_tokens() { echo "$input" | jq -r '.context_window.total_output_tokens'; } get_context_window_size() { echo "$input" | jq -r '.context_window.context_window_size'; } # Use the helpers MODEL=$(get_model_name) DIR=$(get_current_dir) echo "[$MODEL] 📁 ${DIR##*/}" ``` ### Context Window Usage Display the percentage of context window consumed. The `context_window` object contains: - `total_input_tokens` / `total_output_tokens`: Cumulative totals across the entire session - `current_usage`: Current context window usage from the last API call (may be `null` if no messages yet) - `input_tokens`: Input tokens in current context - `output_tokens`: Output tokens generated - `cache_creation_input_tokens`: Tokens written to cache - `cache_read_input_tokens`: Tokens read from cache For accurate context percentage, use `current_usage` which reflects the actual context window state: ```bash theme={null} #!/bin/bash input=$(cat) MODEL=$(echo "$input" | jq -r '.model.display_name') CONTEXT_SIZE=$(echo "$input" | jq -r '.context_window.context_window_size') USAGE=$(echo "$input" | jq '.context_window.current_usage') if [ "$USAGE" != "null" ]; then # Calculate current context from current_usage fields CURRENT_TOKENS=$(echo "$USAGE" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens') PERCENT_USED=$((CURRENT_TOKENS * 100 / CONTEXT_SIZE)) echo "[$MODEL] Context: ${PERCENT_USED}%" else echo "[$MODEL] Context: 0%" fi ``` ## Tips - Keep your status line concise - it should fit on one line - Use emojis (if your terminal supports them) and colors to make information scannable - Use `jq` for JSON parsing in Bash (see examples above) - Test your script by running it manually with mock JSON input: `echo '{"model":{"display_name":"Test"},"workspace":{"current_dir":"/test"}}' | ./statusline.sh` - Consider caching expensive operations (like git status) if needed ## Troubleshooting - If your status line doesn't appear, check that your script is executable (`chmod +x`) - Ensure your script outputs to stdout (not stderr) --- > To find navigation and other pages in this documentation, fetch the llms.txt > file at: https://code.claude.com/docs/llms.txt