Skip to content

Commit 052f070

Browse files
authored
Merge pull request #81 from pchalasani/fix/statusline
Add context + session/weekly usage bars as statusline line 2
2 parents c641ff1 + a000003 commit 052f070

3 files changed

Lines changed: 136 additions & 52 deletions

File tree

34.8 KB
Loading

docs-site/src/content/docs/tools/statusline.mdx

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ usage directly in your terminal status bar. It uses a
99
powerline-style display with color-coded context
1010
indicators so you always know how much context remains.
1111

12+
A second line shows **context-window usage** plus --
13+
for Claude.ai Pro/Max subscribers -- your **session
14+
(5h) and weekly (7d) limit usage**, so you can see how
15+
close you are to each limit at a glance.
16+
1217
:::note[Installation]
1318
Part of the `claude-code-tools` package.
1419
See [Quick Start](../../getting-started/) for
@@ -20,18 +25,36 @@ installation.
2025
The status line is a shell script
2126
(`scripts/statusline.sh`) that reads JSON input from
2227
Claude Code (available since v2.1.6+) and renders a
23-
compact, color-coded bar showing:
28+
compact, color-coded display of up to two lines.
29+
30+
![two-line status line](/claude-code-tools/assets/statusline-limits.png)
31+
32+
**Line 1** -- where you are (always shown):
2433

2534
- **Model name** (e.g. `opus-4`, `sonnet`)
2635
- **Current directory**
2736
- **Git branch and status** (staged, modified,
2837
ahead/behind)
29-
- **Context usage** as a progress bar with percentage
38+
- **Date and time**
39+
40+
**Line 2** -- how much you've used:
41+
42+
- **ctx** -- context-window usage, as a color-coded
43+
progress bar with percentage (always shown)
44+
- **5h** -- the rolling 5-hour session limit, with a
45+
bar, percentage, and a countdown to reset
46+
(e.g. `↻3h12m`)
47+
- **7d** -- the 7-day (weekly) limit, same format
48+
49+
The **ctx** bar is always present. The **5h** and **7d**
50+
segments appear only for Claude.ai Pro/Max subscribers,
51+
after the first API response of a session; each is
52+
dropped when its data is unavailable.
3053

3154
## Color States
3255

33-
The context progress bar changes color based on usage
34-
level:
56+
The context bar and the two limit bars all change color
57+
based on the same usage thresholds:
3558

3659
### Green (0--70%)
3760

@@ -88,6 +111,24 @@ Requires `jq` and a
88111
[Nerd Font](https://www.nerdfonts.com/) for
89112
powerline symbols.
90113

114+
The **session/weekly limit line** appears automatically
115+
once Claude Code sends `rate_limits` data (Claude.ai
116+
Pro/Max, after the first message of a session). Its
117+
reset countdowns are computed when the line renders,
118+
which happens after each assistant message. To keep
119+
them ticking while the session is idle, add a
120+
`refreshInterval` (in seconds) to the config:
121+
122+
```json
123+
{
124+
"statusLine": {
125+
"type": "command",
126+
"command": "~/.claude/statusline.sh",
127+
"refreshInterval": 10
128+
}
129+
}
130+
```
131+
91132
## How It Works
92133

93134
The script receives JSON on stdin with fields including:
@@ -96,14 +137,22 @@ The script receives JSON on stdin with fields including:
96137
- `workspace.current_dir` -- Working directory
97138
- `context_window.used_percentage` -- Context usage
98139
(0--100)
140+
- `rate_limits.five_hour` -- Session limit, with
141+
`used_percentage` and `resets_at` (Unix epoch)
142+
- `rate_limits.seven_day` -- Weekly limit, same shape
99143

100144
It then:
101145

102146
1. Extracts and cleans the model name.
103147
2. Reads git status from the working directory.
104-
3. Builds a powerline-style output with ANSI colors.
105-
4. Renders a 10-character progress bar for context
106-
usage, color-coded by level.
148+
3. Builds line 1 (model, directory, git, date/time) as
149+
a powerline bar with ANSI colors.
150+
4. Builds line 2: a `ctx` context-usage bar (always
151+
shown), then 5h and 7d limit bars with reset
152+
countdowns when `rate_limits` is present.
153+
5. Color-codes every bar by usage level and handles
154+
each limit window independently, dropping any
155+
segment whose data is unavailable.
107156

108157
The model segment background is **green** when git is
109158
clean and **yellow** when there are uncommitted changes,

scripts/statusline.sh

Lines changed: 80 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,41 @@ BLINK=$'\033[5m'
4848
# Powerline separator
4949
SEP=''
5050

51+
# --- Helpers for the second (limits) line ---
52+
53+
# Format a Unix-epoch reset time as a compact countdown, e.g. "2h13m", "4d6h".
54+
fmt_reset() {
55+
local target=$1 now delta d h m
56+
now=$(date +%s)
57+
delta=$((target - now))
58+
[ "$delta" -le 0 ] && { echo "now"; return; }
59+
d=$((delta / 86400))
60+
h=$(((delta % 86400) / 3600))
61+
m=$(((delta % 3600) / 60))
62+
if [ "$d" -gt 0 ]; then echo "${d}d${h}h"
63+
elif [ "$h" -gt 0 ]; then echo "${h}h${m}m"
64+
else echo "${m}m"; fi
65+
}
66+
67+
# Build a 10-char color-coded progress bar for an integer percentage (0-100).
68+
# Used for every bar on line 2 (ctx, 5h, 7d), color-coded by usage level.
69+
build_bar() {
70+
local pct=$1 bar_width=10 filled empty fill_color blink="" empty_color
71+
local fb="" eb="" i
72+
filled=$((pct * bar_width / 100))
73+
[ "$filled" -gt "$bar_width" ] && filled=$bar_width
74+
[ "$filled" -lt 0 ] && filled=0
75+
empty=$((bar_width - filled))
76+
if [ "$pct" -gt 95 ]; then fill_color=$'\033[38;5;196m'; blink=$BLINK
77+
elif [ "$pct" -gt 85 ]; then fill_color=$'\033[38;5;208m'
78+
elif [ "$pct" -gt 70 ]; then fill_color=$'\033[38;5;220m'
79+
else fill_color=$'\033[38;5;29m'; fi
80+
empty_color=$'\033[38;5;240m'
81+
for ((i=0; i<filled; i++)); do fb+=""; done
82+
for ((i=0; i<empty; i++)); do eb+=""; done
83+
printf '%s' "${blink}${fill_color}${fb}${RESET}${empty_color}${eb}${RESET}"
84+
}
85+
5186
# Git info - check status first to determine model background color
5287
git_segment=""
5388
model_bg=$BG_GREEN # default to green
@@ -98,60 +133,60 @@ else
98133
next_bg=$BG_CYAN
99134
fi
100135

101-
# Context progress bar (uses built-in used_percentage from Claude Code 2.1.6+)
102-
context_segment=""
103-
pct=$(echo "$input" | jq '.context_window.used_percentage // empty' 2>/dev/null)
104-
if [ -n "$pct" ] && [ "$pct" != "null" ] && [ "$pct" -ge 0 ] 2>/dev/null; then
105-
# Build progress bar (10 chars wide)
106-
bar_width=10
107-
filled=$((pct * bar_width / 100))
108-
[ "$filled" -gt "$bar_width" ] && filled=$bar_width
109-
empty=$((bar_width - filled))
136+
# Context window usage -- rendered on line 2 (built-in since Claude Code 2.1.6+).
137+
ctx_pct=""
138+
ctx_raw=$(echo "$input" | jq -r '.context_window.used_percentage // empty' 2>/dev/null)
139+
[ -n "$ctx_raw" ] && [ "$ctx_raw" != "null" ] && ctx_pct=$(printf '%.0f' "$ctx_raw" 2>/dev/null)
110140

111-
# Colors for filled portion based on level
112-
if [ "$pct" -gt 95 ]; then
113-
fill_color=$'\033[38;5;196m' # bright red
114-
bar_blink=$BLINK
115-
elif [ "$pct" -gt 85 ]; then
116-
fill_color=$'\033[38;5;208m' # orange
117-
bar_blink=""
118-
elif [ "$pct" -gt 70 ]; then
119-
fill_color=$'\033[38;5;220m' # yellow
120-
bar_blink=""
121-
else
122-
fill_color=$'\033[38;5;29m' # forest green
123-
bar_blink=""
124-
fi
125-
empty_color=$'\033[38;5;240m' # dark gray
126-
127-
# Build the bar string
128-
filled_bar=""
129-
empty_bar=""
130-
for ((i=0; i<filled; i++)); do filled_bar+=""; done
131-
for ((i=0; i<empty; i++)); do empty_bar+=""; done
132-
133-
# Segment with dark background
134-
BG_DARK=$'\033[48;5;236m'
135-
FG_DARK=$'\033[38;5;236m'
136-
context_segment="${next_fg}${BG_DARK}${SEP}${bar_blink}${fill_color}${filled_bar}${RESET}${BG_DARK}${empty_color}${empty_bar}${FG_WHITE} ${pct}%${RESET}"
137-
next_fg=$FG_DARK
141+
# Date and time
142+
current_datetime=$(date +"%Y/%m/%d %H:%M")
143+
144+
# --- Line 2: context usage + session (5h) / weekly (7d) limit usage ---
145+
DIM=$'\033[38;5;244m'
146+
LABEL=$'\033[38;5;250m'
147+
148+
# Context segment (always shown; falls back to --% before the first response).
149+
if [ -n "$ctx_pct" ]; then
150+
ctx_segment="${LABEL}ctx ${RESET}$(build_bar "$ctx_pct")${FG_WHITE} ${ctx_pct}%${RESET}"
151+
else
152+
ctx_segment="${LABEL}ctx ${DIM}--%${RESET}"
138153
fi
139154

140-
# Fallback if no context data
141-
if [ -z "$context_segment" ]; then
142-
BG_DARK=$'\033[48;5;236m'
143-
FG_DARK=$'\033[38;5;236m'
144-
context_segment="${next_fg}${BG_DARK}${SEP}${FG_WHITE} --%${RESET}"
145-
next_fg=$FG_DARK
155+
# Session / weekly limit segments. rate_limits is present only for Claude.ai
156+
# Pro/Max subscribers, after the first API response; each window may be absent.
157+
limit_segment=""
158+
five_pct_raw=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty' 2>/dev/null)
159+
five_reset_raw=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty' 2>/dev/null)
160+
seven_pct_raw=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty' 2>/dev/null)
161+
seven_reset_raw=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty' 2>/dev/null)
162+
163+
if [ -n "$five_pct_raw" ]; then
164+
five_pct=$(printf '%.0f' "$five_pct_raw" 2>/dev/null)
165+
seg="${LABEL}5h ${RESET}$(build_bar "$five_pct")${FG_WHITE} ${five_pct}%${RESET}"
166+
[ -n "$five_reset_raw" ] && seg+="${DIM}$(fmt_reset "$five_reset_raw")${RESET}"
167+
limit_segment="$seg"
146168
fi
147169

148-
# Date and time
149-
current_datetime=$(date +"%Y/%m/%d %H:%M")
170+
if [ -n "$seven_pct_raw" ]; then
171+
seven_pct=$(printf '%.0f' "$seven_pct_raw" 2>/dev/null)
172+
seg="${LABEL}7d ${RESET}$(build_bar "$seven_pct")${FG_WHITE} ${seven_pct}%${RESET}"
173+
[ -n "$seven_reset_raw" ] && seg+="${DIM}$(fmt_reset "$seven_reset_raw")${RESET}"
174+
[ -n "$limit_segment" ] && limit_segment+=" "
175+
limit_segment+="$seg"
176+
fi
177+
178+
# Combine: ctx always, limits appended when present.
179+
line2="$ctx_segment"
180+
[ -n "$limit_segment" ] && line2+=" $limit_segment"
150181

151182
# Build output with powerline style
183+
# Line 1: model / directory / git / date-time
152184
# Model: black on green (clean) or yellow (dirty)
153185
echo -n "${model_bg}${FG_BLACK}${BOLD} $model ${RESET}"
154186
echo -n "${model_fg}${BG_BLUE}${SEP}${FG_BLACK} $dir_name ${RESET}"
155187
echo -n "$git_segment"
156-
echo -n "$context_segment"
157188
echo -n "${next_fg}${RESET}${SEP} ${current_datetime}"
189+
190+
# Line 2: context usage, then session/weekly limit usage when available.
191+
printf '\n'
192+
echo -n " ${line2}"

0 commit comments

Comments
 (0)