Skip to content

Instantly share code, notes, and snippets.

@vandersonarruda
Last active July 30, 2026 16:36
Show Gist options
  • Select an option

  • Save vandersonarruda/69359733411933c7d8f26c87aa9cc214 to your computer and use it in GitHub Desktop.

Select an option

Save vandersonarruda/69359733411933c7d8f26c87aa9cc214 to your computer and use it in GitHub Desktop.
Claude Code status line customizado (contexto + cotas Sessão/Semanal com barras e projeção de esgotamento)

Claude Code — status line customizado

Status line em duas linhas para o Claude Code:

  1. [modelo] 📁 diretório 🌿 branch git (✗ se sujo) Contexto: barra NN%
  2. Sessão (5h) e Semanal (7d) — consumo de cota com barra colorida (verde < 50%, amarelo < 80%, vermelho ≥ 80%), horário de renovação (renova HH:MM, com dia da semana no Semanal) e projeção de esgotamento (esgota ~HH:MM) quando o ritmo de uso não fecha a janela.

Requisitos

  • jq (brew install jq)
  • macOS ou Linux (o script trata date -r vs date -d)

Instalação

  1. Copie statusline-command.sh para ~/.claude/statusline-command.sh.
  2. No ~/.claude/settings.json, adicione:
{
	"statusLine": {
		"type": "command",
		"command": "sh /Users/SEU_USUARIO/.claude/statusline-command.sh",
		"refreshInterval": 60
	}
}
  1. Reinicie o Claude Code (ou abra uma sessão nova).

Notas

  • Os dados vêm do JSON que o Claude Code envia via stdin ao comando do status line (docs: https://code.claude.com/docs/en/statusline.md).
  • rate_limits só existe para assinantes Pro/Max e só expõe as janelas five_hour e seven_day — não há cota por modelo (Fable/Opus) no payload; a cota por modelo só aparece no /usage.
#!/bin/sh
# Claude Code status line - two lines (segments separated by 3 plain spaces):
# 1) [model name] 📁 cwd 🌿 git branch (+ dirty marker)
# 2) Contexto: NN% Sessão (5h) Semanal (7d) rate-limit usage, each as:
# Label: NN% (renova HH:MM[ - esgota HH:MM])
# "renova" is the reset time (local tz); the Semanal segment prefixes it
# with the abbreviated weekday since the reset can be days away. "esgota"
# (projected time to 100%, based on the average usage rate observed so
# far in the window) only appears when NOT on pace to make the reset —
# when on pace, nothing about exhaustion is shown. The esgota time gets
# its own weekday prefix (independent from renova's) when applicable,
# since the exhaustion date and the reset date can differ.
input=$(cat)
# Starship/P10k-style vivid ANSI colors: distinct accent per segment,
# plus a readable (non-flat-gray) neutral tone for labels/separators.
RESET="\033[0m"
MUTED="\033[38;5;250m"
FAINT="\033[2m"
GREEN="\033[1;32m"
YELLOW="\033[1;33m"
RED="\033[1;31m"
CYAN="\033[1;36m"
MAGENTA="\033[1;35m"
BLUE="\033[1;34m"
# Picks a color for a 0-100 percentage: green < 50, yellow < 80, red >= 80
color_for_pct() {
pct="$1"
if awk -v p="$pct" 'BEGIN{exit !(p+0>=80)}' 2>/dev/null; then
printf '%s' "$RED"
elif awk -v p="$pct" 'BEGIN{exit !(p+0>=50)}' 2>/dev/null; then
printf '%s' "$YELLOW"
else
printf '%s' "$GREEN"
fi
}
# ---- Line 1: cwd (cyan) + git branch (magenta) + dirty marker (yellow) -
cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // empty')
[ -z "$cwd" ] && cwd=$(pwd)
# Shorten home directory to ~
cwd=$(echo "$cwd" | sed "s|^$HOME|~|")
git_dir=$(echo "$input" | jq -r '.workspace.current_dir // empty')
[ -z "$git_dir" ] && git_dir="$cwd"
branch=""
dirty=""
# Skip optional locks so this never blocks on a git lock file
if git -C "$git_dir" --no-optional-locks rev-parse --is-inside-work-tree >/dev/null 2>&1; then
branch=$(git -C "$git_dir" --no-optional-locks branch --show-current 2>/dev/null)
if [ -n "$(git -C "$git_dir" --no-optional-locks status --porcelain 2>/dev/null)" ]; then
dirty="✗"
fi
fi
# 3 plain spaces: a fixed-width separator that renders consistently, unlike
# tabs (whose gap size varies with terminal tab stops) or a pipe glyph.
sep=" "
path_display=$(printf "%b📁 %s%b" "$CYAN" "$cwd" "$RESET")
git_display=""
if [ -n "$branch" ]; then
git_display=$(printf "%b🌿 %s%b" "$MAGENTA" "$branch" "$RESET")
if [ -n "$dirty" ]; then
git_display="$git_display$(printf " %b%s%b" "$YELLOW" "$dirty" "$RESET")"
fi
fi
# ---- Line 1, part B: model name -----------------------------------------
model=$(echo "$input" | jq -r '.model.display_name // empty')
[ -z "$model" ] && model="?"
model_display=$(printf "%b[%s]%b" "$BLUE" "$model" "$RESET")
line1="${model_display}${sep}${path_display}"
[ -n "$git_display" ] && line1="${line1}${sep}${git_display}"
# ---- Context-window percentage (shown on line 2, before the quotas) -----
used_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
if [ -n "$used_pct" ]; then
ctx_color=$(color_for_pct "$used_pct")
pct_txt=$(awk -v p="$used_pct" 'BEGIN{printf "%.0f", p}')
ctx_display=$(printf "%bContexto: %b%s%%%b" "$MUTED" "$ctx_color" "$pct_txt" "$RESET")
else
ctx_display=$(printf "%bContexto: sem dados%b" "$MUTED" "$RESET")
fi
# ---- Line 2: Sessão (5h) + Semanal (7d) rate-limit windows -------------
# Portuguese abbreviated weekday (lowercase + period) from ISO weekday number (1=seg ... 7=dom)
weekday_abbr() {
case "$1" in
1) printf 'seg.' ;;
2) printf 'ter.' ;;
3) printf 'qua.' ;;
4) printf 'qui.' ;;
5) printf 'sex.' ;;
6) printf 'sáb.' ;;
7) printf 'dom.' ;;
*) printf '' ;;
esac
}
# format_rate_limit <label> <used_pct> <resets_at epoch> <window seconds> <show_weekday: 0|1>
format_rate_limit() {
label="$1"
pct="$2"
resets_at="$3"
window_seconds="$4"
show_weekday="$5"
if [ -z "$pct" ] || [ "$pct" = "null" ] || [ -z "$resets_at" ] || [ "$resets_at" = "null" ]; then
printf "%b%s: sem dados%b" "$MUTED" "$label" "$RESET"
return
fi
now=$(date +%s)
resets_at_i=$(awk -v v="$resets_at" 'BEGIN{printf "%d", v}')
reset_time=$(date -r "$resets_at_i" "+%H:%M" 2>/dev/null || date -d "@$resets_at_i" "+%H:%M" 2>/dev/null)
reset_str="$reset_time"
if [ "$show_weekday" = "1" ]; then
wd_num=$(date -r "$resets_at_i" "+%u" 2>/dev/null || date -d "@$resets_at_i" "+%u" 2>/dev/null)
wd_abbr=$(weekday_abbr "$wd_num")
[ -n "$wd_abbr" ] && reset_str="$wd_abbr $reset_time"
fi
window_start=$((resets_at_i - window_seconds))
elapsed=$((now - window_start))
# Only set when NOT on pace to make the reset; stays empty ("on pace") otherwise
esgota_part=""
if awk -v p="$pct" -v e="$elapsed" 'BEGIN{exit !(p>0 && e>0)}' 2>/dev/null; then
eta_epoch=$(awk -v p="$pct" -v e="$elapsed" -v n="$now" 'BEGIN{rate=p/e; remain=(100-p)/rate; printf "%d", n+remain}')
if [ "$eta_epoch" -lt "$resets_at_i" ] 2>/dev/null; then
eta_time=$(date -r "$eta_epoch" "+%H:%M" 2>/dev/null || date -d "@$eta_epoch" "+%H:%M" 2>/dev/null)
if [ "$show_weekday" = "1" ]; then
# Computed from the ETA's own epoch, not the reset's, since they can fall on different days
eta_wd_num=$(date -r "$eta_epoch" "+%u" 2>/dev/null || date -d "@$eta_epoch" "+%u" 2>/dev/null)
eta_wd_abbr=$(weekday_abbr "$eta_wd_num")
if [ -n "$eta_wd_abbr" ]; then
esgota_part=" - esgota $eta_wd_abbr ~$eta_time"
else
esgota_part=" - esgota ~$eta_time"
fi
else
esgota_part=" - esgota ~$eta_time"
fi
fi
fi
color=$(color_for_pct "$pct")
pct_txt=$(awk -v p="$pct" 'BEGIN{printf "%.0f", p}')
printf "%b%s: %b%s%%%b%b (renova %s%s)%b" "$MUTED" "$label" "$color" "$pct_txt" "$RESET" "$FAINT" "$reset_str" "$esgota_part" "$RESET"
}
five_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
five_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
week_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
week_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
line_five=$(format_rate_limit "Sessão" "$five_pct" "$five_reset" 18000 0)
line_week=$(format_rate_limit "Semanal" "$week_pct" "$week_reset" 604800 1)
line2="${ctx_display}${sep}${line_five}${sep}${line_week}"
# A fully empty line here gets stripped by Claude Code's statusline renderer,
# collapsing the intended gap. A line containing a single space is still
# "blank" visually but isn't an empty string, so the renderer keeps it and a
# real vertical gap shows up between line 1 and line 2.
blank_line=" "
printf "%s\n%s\n%s\n" "$line1" "$blank_line" "$line2"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment