Put the context window where you can see it
Claude Code will tell you how full its context is on every turn — if you ask. A forty-line status line that shows model, cost, context used, and the autocompact threshold, with the script to copy.
The most common way a long Claude Code session goes wrong is not a bad answer. It is a good answer given after the context has quietly been compacted, by a model that no longer remembers the constraint you set forty minutes ago.
Compaction is the right behaviour. The problem is that it is invisible. You find out it happened when the next edit contradicts an earlier one, and by then you are debugging the conversation instead of the code.
Claude Code already knows how full the window is. It will tell you on every turn, in the bar under the prompt, if you give it a script to print with. This is that script.
What a status line is
Claude Code has a statusLine setting. Point it at a command, and after each response Claude Code runs that command with a JSON payload on stdin and prints whatever the command writes to stdout under the prompt. That is the entire mechanism. No plugin, no extension, no daemon.
The payload is richer than most people expect. It carries the model's display name and id; the session cost in dollars and total API wall time; the working directory; and a context_window object with the window size, tokens currently in it, and a pre-calculated used_percentage. On subscription plans it also carries rate_limits with your five-hour and seven-day usage. Anything in there can be on your screen.
The one thing it does not carry is the autocompact threshold — the point at which Claude Code will compress the conversation. That is the number people most want to see, so the script below computes it from the window size.
The script
Two lines of output. The first is the session: model, cost so far, cumulative API time, a ten-cell context bar with the percentage and raw token count, and the autocompact threshold. The bar goes green, amber, red at 60 and 85 percent. The second line is where you are: folder and git branch.
It needs jq. On a Mac, brew install jq.
#!/bin/bash
# Claude Code status line — model | cost | api time | context % + autocompact threshold | folder + git branch
# From: https://intlo.com/news/claude-code-status-line
# Install: save as ~/.claude/statusline.sh, chmod +x, then point "statusLine.command" at it in ~/.claude/settings.json
input=$(cat)
j() { echo "$input" | jq -r "$1"; }
MODEL=$(j '.model.display_name // "?"')
COST=$(j '.cost.total_cost_usd // 0')
API_MS=$(j '.cost.total_api_duration_ms // 0')
DIR=$(j '.workspace.current_dir // ""')
PCT=$(j '.context_window.used_percentage // 0' | cut -d. -f1)
SIZE=$(j '.context_window.context_window_size // 200000')
IN=$(j '.context_window.total_input_tokens // 0')
OUT=$(j '.context_window.total_output_tokens // 0')
# Autocompact threshold — Claude Code compacts at roughly this share of the window.
# Override with AUTOCOMPACT_PCT in your shell env if you have tuned it.
AUTOCOMPACT_PCT=${AUTOCOMPACT_PCT:-80}
AUTOCOMPACT=$(( SIZE * AUTOCOMPACT_PCT / 100 ))
USED=$(( IN + OUT ))
# Colors
DIM='\033[2m'; RESET='\033[0m'
ORANGE='\033[38;5;214m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; CYAN='\033[36m'; BLUE='\033[34m'
# Context color by usage
if [ "$PCT" -ge 85 ]; then CTX_C=$RED
elif [ "$PCT" -ge 60 ]; then CTX_C=$YELLOW
else CTX_C=$GREEN; fi
# Progress bar (10 cells)
FILLED=$(( PCT / 10 )); BAR=""
for i in $(seq 1 10); do
if [ "$i" -le "$FILLED" ]; then BAR="${BAR}█"; else BAR="${BAR}░"; fi
done
# Git branch
BRANCH=""
if [ -n "$DIR" ] && git -C "$DIR" rev-parse --git-dir >/dev/null 2>&1; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
fi
fmt_k() { awk -v n="$1" 'BEGIN{ if (n>=1000000) printf "%.1fM", n/1000000; else if (n>=1000) printf "%.0fk", n/1000; else printf "%d", n }'; }
API_S=$(awk -v ms="$API_MS" 'BEGIN{printf "%.0fs", ms/1000}')
COST_F=$(awk -v c="$COST" 'BEGIN{printf "$%.2f", c}')
# Line 1: model, cost, api, context
printf "${ORANGE}%s${RESET} ${DIM}|${RESET} ${GREEN}%s${RESET} ${DIM}|${RESET} ${DIM}api:${RESET}%s ${DIM}|${RESET} ${CTX_C}%s %s%%${RESET} ${DIM}(%s/%s)${RESET} ${DIM}autocompact${RESET} ${BLUE}%s${RESET}\n" \
"$MODEL" "$COST_F" "$API_S" "$BAR" "$PCT" "$(fmt_k "$USED")" "$(fmt_k "$SIZE")" "$AUTOCOMPACT"
# Line 2: folder + branch
printf "${CYAN}📁 %s${RESET}" "${DIR##*/}"
[ -n "$BRANCH" ] && printf " ${DIM}on${RESET} ${YELLOW} %s${RESET}" "$BRANCH"
printf "\n"
Save it as ~/.claude/statusline.sh and make it executable with chmod +x ~/.claude/statusline.sh. Or fetch it directly: intlo.com/news/statusline.sh.
Wire it up
Add a statusLine block to ~/.claude/settings.json. Merge it into whatever is already there; it is a normal top-level key.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 0
}
}Test it before restarting anything. Claude Code pipes JSON on stdin, so you can do the same by hand and see exactly what will render:
echo '{"model":{"display_name":"Opus 5.5"},"cost":{"total_cost_usd":1.23,"total_api_duration_ms":4200},"workspace":{"current_dir":"'$PWD'"},"context_window":{"used_percentage":66,"context_window_size":1000000,"total_input_tokens":640000,"total_output_tokens":20000}}' | ~/.claude/statusline.shIf you see two coloured lines, restart Claude Code and they will appear under the prompt. If you see nothing, the script is not executable or jq is not on the path — those are the only two failure modes we have hit.
The autocompact number
The script shows the threshold as window size × AUTOCOMPACT_PCT, defaulting to 80 percent. That is a working estimate, not a value Claude Code reports. If you have tuned compaction, export AUTOCOMPACT_PCT in your shell and the status line follows it.
What matters is not the exact token. It is that the bar turning amber is now a signal to do something deliberate: write the decisions so far into CLAUDE.md or a notes file, run /compact yourself with a summary you control, or start a fresh session on purpose. Compaction you choose keeps what you choose. Compaction that happens to you keeps what the summariser guessed was important.
Things worth adding
Plan usage instead of cost. On a Max subscription the dollar figure is informational; what actually runs out is the five-hour window. Swap cost.total_cost_usd for rate_limits.five_hour.used_percentage and rate_limits.seven_day.used_percentage and the status line becomes a fuel gauge.
Lines changed. The payload includes cost.total_lines_added and cost.total_lines_removed. A net line count per session is a surprisingly honest measure of whether the afternoon went anywhere.
Sync it. settings.json is per machine. If you run Claude Code on more than one, put statusline.sh in your dotfiles and let every machine pick it up — the JSON schema is the same everywhere.
Or skip the hand editing entirely: type /statusline inside Claude Code, describe what you want in a sentence, and it will write and install the script for you. The one above is just what we ended up with after a few rounds of that.