Last active
February 15, 2026 02:03
-
-
Save ericboehs/23e7d4796460e9eb6714adac915ed742 to your computer and use it in GitHub Desktop.
claude-context: Scrape Claude Code /context output via tmux
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 | |
| # Scrape Claude Code /context output via tmux. | |
| # Resumes an existing session, runs /context, captures output, exits. | |
| # | |
| # Usage: | |
| # bin/claude-context <session-id> # Human-readable output | |
| # bin/claude-context <session-id> --json # JSON output for programmatic use | |
| # | |
| # Requires: tmux, claude CLI | |
| set -euo pipefail | |
| SESSION_ID="${1:?Usage: $0 <session-id> [--json]}" | |
| JSON_MODE=false | |
| if [[ "${2:-}" == "--json" ]]; then | |
| JSON_MODE=true | |
| fi | |
| TMUX_SESSION="earl-ctx-$$" | |
| TIMEOUT=30 | |
| cleanup() { tmux kill-session -t "$TMUX_SESSION" 2>/dev/null || true; } | |
| trap cleanup EXIT | |
| # Resume the Claude session in detached tmux (wide to avoid text wrapping) | |
| tmux new-session -d -s "$TMUX_SESSION" -x 200 -y 80 "CLAUDECODE= claude --resume $SESSION_ID --fork-session 2>&1; sleep 30" | |
| # Wait for the session to be ready (prompt indicators) | |
| for _ in $(seq 1 "$TIMEOUT"); do | |
| sleep 1 | |
| if tmux capture-pane -t "$TMUX_SESSION" -p -S -20 2>/dev/null | grep -q 'PR #\|Try \|Welcome\|INSERT'; then | |
| break | |
| fi | |
| done | |
| # Extra settle time after prompt appears | |
| sleep 5 | |
| # Clear any pending input and send /context | |
| # NOTE: Do NOT send Escape — Claude TUI uses vim-style modes and Escape | |
| # switches from INSERT to NORMAL mode, where "/" starts a search. | |
| # First Enter triggers autocomplete menu, second Enter selects /context | |
| tmux send-keys -t "$TMUX_SESSION" C-u | |
| sleep 0.5 | |
| tmux send-keys -t "$TMUX_SESSION" '/context' Enter | |
| sleep 2 | |
| tmux send-keys -t "$TMUX_SESSION" Enter | |
| sleep 5 | |
| # Capture the pane content (large scrollback to get full /context output) | |
| OUTPUT=$(tmux capture-pane -t "$TMUX_SESSION" -p -S -200 2>/dev/null) | |
| # Exit Claude cleanly | |
| tmux send-keys -t "$TMUX_SESSION" C-u | |
| sleep 0.5 | |
| tmux send-keys -t "$TMUX_SESSION" '/exit' Enter | |
| sleep 1 | |
| # /context output uses unicode icons (⛁ ⛶ ⛝) as prefixes for each category line. | |
| # We anchor our greps on these icons to avoid matching conversation history that may | |
| # contain similar text (e.g., diffs of this very script). | |
| # | |
| # Example lines: | |
| # ⛁ System prompt: 3k tokens (1.5%) | |
| # ⛶ Free space: 133k (66.7%) | |
| # ⛝ Autocompact buffer: 33k tokens (16.5%) | |
| # Parse the context summary line: "claude-opus-4-6 · 34k/200k tokens (17%)" | |
| # The · is a UTF-8 middle dot (U+00B7), match with [^a-z]* | |
| # Take the LAST match in case scrollback has older /context outputs | |
| summary_line=$(echo "$OUTPUT" | grep -o '[a-z0-9-]* [^a-z]* [0-9.]*k*/[0-9.]*k* tokens ([0-9.]*%)' | tail -1 || echo "") | |
| if [[ -z "$summary_line" ]]; then | |
| if [[ "$JSON_MODE" == true ]]; then | |
| echo '{"error": "Could not parse context data"}' | |
| else | |
| echo "⚠️ Could not parse context data." | |
| echo "Raw output (last 30 lines):" | |
| echo "$OUTPUT" | tail -30 | |
| fi | |
| exit 1 | |
| fi | |
| model=$(echo "$summary_line" | sed 's/ [^a-z].*//') | |
| used_tokens=$(echo "$summary_line" | grep -o '[0-9.]*k*/' | sed 's/\///') | |
| total_tokens=$(echo "$summary_line" | grep -o '/[0-9.]*k* tokens' | sed 's|^/||' | sed 's/ tokens//') | |
| percent=$(echo "$summary_line" | grep -o '([0-9.]*%)' | tr -d '()') | |
| # Helper: extract token count from a /context category line. | |
| # Anchors on unicode icons (⛁ ⛶ ⛝) to avoid matching conversation history. | |
| parse_tokens() { | |
| echo "$OUTPUT" | grep "[⛁⛶⛝] $1" | tail -1 | grep -o '[0-9.]*k* tokens' | sed 's/ tokens//' || echo "" | |
| } | |
| # Helper: extract percentage from a /context category line | |
| parse_pct() { | |
| echo "$OUTPUT" | grep "[⛁⛶⛝] $1" | tail -1 | grep -o '([0-9.]*%)' | tr -d '()' || echo "" | |
| } | |
| # Parse category breakdowns | |
| system_prompt=$(parse_tokens 'System prompt:') | |
| system_tools=$(parse_tokens 'System tools:') | |
| custom_agents=$(parse_tokens 'Custom agents:') | |
| memory_files=$(parse_tokens 'Memory files:') | |
| skills_val=$(parse_tokens 'Skills:') | |
| messages=$(parse_tokens 'Messages:') | |
| free_space=$(echo "$OUTPUT" | grep '[⛁⛶⛝] Free space:' | tail -1 | grep -o '[0-9.]*k*' | head -1 || echo "") | |
| autocompact=$(echo "$OUTPUT" | grep '[⛁⛶⛝] Autocompact buffer:' | tail -1 | grep -o '[0-9.]*k* tokens' | sed 's/ tokens//' || echo "") | |
| # Parse percentage breakdowns | |
| system_prompt_pct=$(parse_pct 'System prompt:') | |
| system_tools_pct=$(parse_pct 'System tools:') | |
| custom_agents_pct=$(parse_pct 'Custom agents:') | |
| memory_files_pct=$(parse_pct 'Memory files:') | |
| skills_pct=$(parse_pct 'Skills:') | |
| messages_pct=$(parse_pct 'Messages:') | |
| free_space_pct=$(parse_pct 'Free space:') | |
| autocompact_pct=$(echo "$OUTPUT" | grep '[⛁⛶⛝] Autocompact buffer:' | tail -1 | grep -o '([0-9.]*%)' | tr -d '()' || echo "") | |
| if [[ "$JSON_MODE" == true ]]; then | |
| # Helper to output a JSON string or null | |
| json_str() { if [[ -n "$1" ]]; then echo "\"$1\""; else echo "null"; fi; } | |
| cat <<JSON | |
| { | |
| "model": "$model", | |
| "used_tokens": "$used_tokens", | |
| "total_tokens": "$total_tokens", | |
| "percent_used": "$percent", | |
| "categories": { | |
| "system_prompt": {"tokens": $(json_str "$system_prompt"), "percent": $(json_str "$system_prompt_pct")}, | |
| "system_tools": {"tokens": $(json_str "$system_tools"), "percent": $(json_str "$system_tools_pct")}, | |
| "custom_agents": {"tokens": $(json_str "$custom_agents"), "percent": $(json_str "$custom_agents_pct")}, | |
| "memory_files": {"tokens": $(json_str "$memory_files"), "percent": $(json_str "$memory_files_pct")}, | |
| "skills": {"tokens": $(json_str "$skills_val"), "percent": $(json_str "$skills_pct")}, | |
| "messages": {"tokens": $(json_str "$messages"), "percent": $(json_str "$messages_pct")}, | |
| "free_space": {"tokens": $(json_str "$free_space"), "percent": $(json_str "$free_space_pct")}, | |
| "autocompact_buffer": {"tokens": $(json_str "$autocompact"), "percent": $(json_str "$autocompact_pct")} | |
| } | |
| } | |
| JSON | |
| else | |
| echo "📊 Claude Context Usage" | |
| echo "" | |
| echo " Model: $model" | |
| echo " Context: $used_tokens / $total_tokens tokens ($percent)" | |
| echo "" | |
| [[ -n "$messages" ]] && echo " Messages: $messages tokens ($messages_pct)" | |
| [[ -n "$system_prompt" ]] && echo " System prompt: $system_prompt tokens ($system_prompt_pct)" | |
| [[ -n "$system_tools" ]] && echo " System tools: $system_tools tokens ($system_tools_pct)" | |
| [[ -n "$custom_agents" ]] && echo " Custom agents: $custom_agents tokens ($custom_agents_pct)" | |
| [[ -n "$memory_files" ]] && echo " Memory files: $memory_files tokens ($memory_files_pct)" | |
| [[ -n "$skills_val" ]] && echo " Skills: $skills_val tokens ($skills_pct)" | |
| [[ -n "$free_space" ]] && echo " Free space: $free_space ($free_space_pct)" | |
| [[ -n "$autocompact" ]] && echo " Autocompact buf: $autocompact tokens ($autocompact_pct)" | |
| fi |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
claude-context
Scrape Claude Code
/contextoutput via tmux. Resumes an existing Claude session in a detached tmux pane, runs/context, parses the output, and returns structured data.Features
--fork-session(non-destructive)Dependencies
tmuxclaudeCLIInstallation
Usage
Example JSON output
{ "model": "claude-opus-4-6", "used_tokens": "79k", "total_tokens": "200k", "percent_used": "39%", "categories": { "system_prompt": {"tokens": "3k", "percent": "1.5%"}, "system_tools": {"tokens": "23.7k", "percent": "11.9%"}, "messages": {"tokens": "27.5k", "percent": "13.8%"}, "free_space": {"tokens": "106k", "percent": "53.0%"}, "autocompact_buffer": {"tokens": "33k", "percent": "16.5%"} } }How it works
claude --resume <id> --fork-session/contextvia tmuxsend-keys/exit