{"slug": "add-cache-health-to-statusline", "title": "Add cache health to statusline", "summary": "To add cache health monitoring to a custom statusline script for Claude Code, tracking the ratio between cache reads (cheap, reused tokens) and cache creation (expensive, rebuilding tokens). The implementation uses a JSON state file to detect consecutive zero-read checks, displaying a \"CACHE MISS\" warning in red when the cache is completely thrashed (3+ consecutive zero reads) or showing the cache hit percentage in green, yellow, or red based on performance. A broken cache means users pay full input token prices instead of receiving the ~90% cache discount, and the author notes that certain workflows like batch tool calls can consistently thrash the cache.", "body_md": "# Add a cache health indicator to your Claude Code statusline\n\nIf you're using a custom statusline script, you can add cache health monitoring to catch when your prompt cache is getting thrashed. Here's what to add and what it means.\n\n**What it tracks:** The ratio between `cache_read_input_tokens` (cheap, reused from cache) and `cache_creation_input_tokens` (expensive, rebuilding cache). When cache reads drop to zero while creation stays high, you're paying full price every turn instead of getting the ~90% cache discount.\n\n## Step 1: Extract the token data\n\nAdd this near the top where you parse the statusline JSON input:\n\n``` bash\n# Extract context_window data\ncontext_window=$(echo \"$input\" | jq -c '.context_window // null')\ncurrent_usage=$(echo \"$input\" | jq -c '.context_window.current_usage // null')\n\n# Calculate context percentage\ncontext_pct=\"\"\nif [ \"$context_window\" != \"null\" ]; then\n    context_window_size=$(echo \"$context_window\" | jq -r '.context_window_size // 0')\n    if [ \"$current_usage\" != \"null\" ]; then\n        input_tokens=$(echo \"$current_usage\" | jq -r '.input_tokens // 0')\n        cache_creation=$(echo \"$current_usage\" | jq -r '.cache_creation_input_tokens // 0')\n        cache_read=$(echo \"$current_usage\" | jq -r '.cache_read_input_tokens // 0')\n        total_tokens=$((input_tokens + cache_creation + cache_read))\n        if [ \"$context_window_size\" -gt 0 ]; then\n            context_pct=$((total_tokens * 100 / context_window_size))\n        fi\n    fi\nfi\n```\n\n## Step 2: Add the cache health indicator\n\nAdd this where you build your status line segments:\n\n``` bash\n# Cache Health Indicator\n# Detects broken cache: cache_read stuck at 0 while cache_creation is high\nif [ \"$current_usage\" != \"null\" ] && [ -n \"$context_pct\" ]; then\n    STATE_DIR=\"/tmp/claude-code-state\"\n    mkdir -p \"$STATE_DIR\"\n    CACHE_HEALTH_FILE=\"${STATE_DIR}/${SESSION_ID}-cache-health.json\"\n\n    # Track consecutive zero-read checks\n    if [ -f \"$CACHE_HEALTH_FILE\" ]; then\n        prev_checks=$(jq -r '.checks // 0' \"$CACHE_HEALTH_FILE\" 2>/dev/null)\n        prev_zero_reads=$(jq -r '.zero_reads // 0' \"$CACHE_HEALTH_FILE\" 2>/dev/null)\n    else\n        prev_checks=0\n        prev_zero_reads=0\n    fi\n\n    new_checks=$((prev_checks + 1))\n    if [ \"$cache_read\" -eq 0 ] && [ \"$cache_creation\" -gt 0 ]; then\n        new_zero_reads=$((prev_zero_reads + 1))\n    else\n        # Reset on any successful cache read\n        new_zero_reads=0\n    fi\n\n    # Persist state across refreshes\n    printf '{\"checks\":%d,\"zero_reads\":%d,\"last_read\":%s,\"last_creation\":%s,\"timestamp\":\"%s\"}\\n' \\\n        \"$new_checks\" \"$new_zero_reads\" \"$cache_read\" \"$cache_creation\" \\\n        \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" > \"$CACHE_HEALTH_FILE\" 2>/dev/null\n\n    # Skip first 2 checks (cold start -- no cache exists yet, zero reads is normal)\n    # After 3+ consecutive zero-read checks, warn about cache thrashing\n    if [ \"$new_checks\" -gt 2 ] && [ \"$new_zero_reads\" -ge 3 ]; then\n        status_parts+=(\"${RED}CACHE MISS${RESET}\")\n    elif [ \"$new_checks\" -gt 2 ] && [ \"$cache_read\" -gt 0 ]; then\n        # Show cache hit ratio when healthy\n        cache_total=$((cache_read + cache_creation))\n        if [ \"$cache_total\" -gt 0 ]; then\n            cache_hit_pct=$((cache_read * 100 / cache_total))\n            dollar='$'\n            if [ \"$cache_hit_pct\" -gt 80 ]; then\n                status_parts+=(\"${GREEN}${cache_hit_pct}%${dollar}${RESET}\")\n            elif [ \"$cache_hit_pct\" -gt 50 ]; then\n                status_parts+=(\"${YELLOW}${cache_hit_pct}%${dollar}${RESET}\")\n            else\n                status_parts+=(\"${RED}${cache_hit_pct}%${dollar}${RESET}\")\n            fi\n        fi\n    fi\nfi\n```\n\n## What you'll see in the statusline\n\n- `92%$` in green -- healthy, 92% of tokens hitting cache (saving money)\n- `45%$` in red -- poor cache hit rate, you're overpaying\n- `CACHE MISS` in red -- cache is completely thrashed, zero reads for 3+ consecutive checks\n\n## Why this matters\n\nA broken cache means you're paying full input token price every turn instead of getting the ~90% discount. It also increases latency. I found that certain workflows (like batch tool calls in quick succession) consistently thrash the cache, and was able to restructure them once I could actually see it happening in real time.\n\n`SESSION_ID` should match however you identify your session -- tmux pane ID, env var, or a hash of the working directory.\n", "url": "https://wpnews.pro/news/add-cache-health-to-statusline", "canonical_source": "https://gist.github.com/GGPrompts/8125321d4cd462da19769b04f43ee70a", "published_at": "2026-04-05 05:23:05+00:00", "updated_at": "2026-05-22 04:04:58.576360+00:00", "lang": "en", "topics": ["developer-tools", "large-language-models", "artificial-intelligence"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/add-cache-health-to-statusline", "markdown": "https://wpnews.pro/news/add-cache-health-to-statusline.md", "text": "https://wpnews.pro/news/add-cache-health-to-statusline.txt", "jsonld": "https://wpnews.pro/news/add-cache-health-to-statusline.jsonld"}}