|
#!/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" |