Skip to content

Instantly share code, notes, and snippets.

@ericboehs
Last active February 15, 2026 02:03
Show Gist options
  • Select an option

  • Save ericboehs/23e7d4796460e9eb6714adac915ed742 to your computer and use it in GitHub Desktop.

Select an option

Save ericboehs/23e7d4796460e9eb6714adac915ed742 to your computer and use it in GitHub Desktop.
claude-context: Scrape Claude Code /context output via tmux
#!/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
@ericboehs
Copy link
Author

ericboehs commented Feb 15, 2026

claude-context

Scrape Claude Code /context output via tmux. Resumes an existing Claude session in a detached tmux pane, runs /context, parses the output, and returns structured data.

Features

  • Resumes a Claude Code session by ID using --fork-session (non-destructive)
  • Parses unicode icon-prefixed category lines (⛁ ⛶ ⛝)
  • Outputs human-readable or JSON format
  • Extracts model, token counts, percentages, and all context categories

Dependencies

  • tmux
  • claude CLI

Installation

curl -fsSL https://gist.githubusercontent.com/ericboehs/23e7d4796460e9eb6714adac915ed742/raw/claude-context -o /usr/local/bin/claude-context
chmod +x /usr/local/bin/claude-context

Usage

# Human-readable output
claude-context <session-id>

# JSON output for programmatic use
claude-context <session-id> --json

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

  1. Spawns a detached tmux session (200x80) with claude --resume <id> --fork-session
  2. Waits up to 30s for the prompt to appear (checks for known prompt indicators)
  3. Sends /context via tmux send-keys
  4. Captures 200 lines of pane scrollback
  5. Parses the summary line and category breakdowns using regex
  6. Exits Claude cleanly with /exit
  7. Cleans up the tmux session on exit (via trap)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment