@@ -106,115 +106,41 @@ build_bar() {
106106 printf " %b%s%b%s%b" " $bar_color " " $filled_str " " $C_DIM " " $empty_str " " $C_RESET "
107107}
108108
109- # API usage data (cached)
110- CACHE_FILE=" /tmp/claude-statusline-usage-cache.json"
111- CACHE_MAX_AGE=60
112-
113- get_oauth_token () {
114- # macOS: credentials stored in Keychain
115- if [ " $( uname) " = " Darwin" ]; then
116- local keychain_data
117- keychain_data=$( security find-generic-password -s " claude-code-credentials" -w 2> /dev/null ||
118- security find-generic-password -s " Claude Code-credentials" -w 2> /dev/null)
119- if [ -n " $keychain_data " ]; then
120- echo " $keychain_data " | jq -r ' .claudeAiOauth.accessToken // empty' 2> /dev/null
121- return
122- fi
123- fi
124- # Linux/Windows: credentials in file
125- local creds_path=" $HOME /.claude/.credentials.json"
126- if [ -f " $creds_path " ]; then
127- jq -r ' .claudeAiOauth.accessToken // empty' " $creds_path " 2> /dev/null
128- fi
129- }
130-
131- fetch_usage_data () {
132- local token
133- token=$( get_oauth_token)
134- if [ -z " $token " ]; then return 1; fi
135- local tmp_file=" ${CACHE_FILE} .tmp"
136- if curl -s --max-time 5 \
137- -H " Accept: application/json" \
138- -H " Content-Type: application/json" \
139- -H " Authorization: Bearer $token " \
140- -H " anthropic-beta: oauth-2025-04-20" \
141- -H " User-Agent: claude-code/2.1.34" \
142- " https://api.anthropic.com/api/oauth/usage" > " $tmp_file " 2> /dev/null; then
143- # Only update cache if we got valid JSON with utilization data
144- if jq -e ' .five_hour.utilization' " $tmp_file " > /dev/null 2>&1 ; then
145- mv " $tmp_file " " $CACHE_FILE "
146- else
147- rm -f " $tmp_file "
148- fi
149- else
150- rm -f " $tmp_file "
151- fi
152- }
153-
154- needs_refresh=true
155- if [ -f " $CACHE_FILE " ]; then
156- if [ " $( uname) " = " Darwin" ]; then
157- cache_mtime=$( stat -f %m " $CACHE_FILE " 2> /dev/null)
158- else
159- cache_mtime=$( stat -c %Y " $CACHE_FILE " 2> /dev/null)
160- fi
161- now=$( date +%s)
162- if [ -n " $cache_mtime " ] && [ $(( now - cache_mtime)) -lt $CACHE_MAX_AGE ]; then
163- needs_refresh=false
164- fi
165- fi
166-
167- if $needs_refresh ; then
168- fetch_usage_data
169- fi
170-
171- usage_data=" "
172- if [ -f " $CACHE_FILE " ]; then
173- usage_data=$( cat " $CACHE_FILE " 2> /dev/null)
174- fi
175-
176- # Parse usage data
109+ # Rate limit data — available from stdin as of Claude Code v2.1.80+
110+ # resets_at is a Unix timestamp
177111five_hour_pct=0
178112five_hour_reset=" "
179113seven_day_pct=0
180114seven_day_reset=" "
181115
182- format_reset_time () {
183- local iso=$1 style=$2
184- if [ -z " $iso " ]; then return ; fi
185- # Strip fractional seconds and Z suffix to get bare datetime
186- local bare=" ${iso%% .* } "
187- bare=" ${bare%% Z} "
116+ format_reset_time_epoch () {
117+ local epoch=$1 style=$2
118+ if [ -z " $epoch " ]; then return ; fi
188119 if [ " $( uname) " = " Darwin" ]; then
189- # Parse as UTC to get epoch, then format as local time
190- local epoch
191- epoch=$( TZ=UTC date -jf " %Y-%m-%dT%H:%M:%S" " $bare " " +%s" 2> /dev/null)
192- if [ -z " $epoch " ]; then return ; fi
193120 if [ " $style " = " time" ]; then
194121 date -r " $epoch " " +%-l:%M%p" 2> /dev/null | tr ' [:upper:]' ' [:lower:]'
195122 else
196123 date -r " $epoch " " +%b %-d, %-l:%M%p" 2> /dev/null | tr ' [:upper:]' ' [:lower:]'
197124 fi
198125 else
199- # Linux: date -d handles ISO with Z natively
200126 if [ " $style " = " time" ]; then
201- date -d " $iso " " +%-l:%M%p" 2> /dev/null | tr ' [:upper:]' ' [:lower:]'
127+ date -d " @ $epoch " " +%-l:%M%p" 2> /dev/null | tr ' [:upper:]' ' [:lower:]'
202128 else
203- date -d " $iso " " +%b %-d, %-l:%M%p" 2> /dev/null | tr ' [:upper:]' ' [:lower:]'
129+ date -d " @ $epoch " " +%b %-d, %-l:%M%p" 2> /dev/null | tr ' [:upper:]' ' [:lower:]'
204130 fi
205131 fi
206132}
207133
208- if [ -n " $usage_data " ] ; then
209- five_hour_pct= $( echo " $usage_data " | jq -r ' (.five_hour.utilization // 0) | round ' 2> /dev/null )
210- [ -z " $five_hour_pct " ] && five_hour_pct=0
211- five_hour_reset_iso =$( echo " $usage_data " | jq -r ' .five_hour.resets_at // empty' 2> /dev/null)
212- five_hour_reset=$( format_reset_time " $five_hour_reset_iso " " time" )
134+ five_hour_pct_raw= $( echo " $input " | jq -r ' .rate_limits.five_hour.used_percentage // empty ' 2> /dev/null )
135+ if [ -n " $five_hour_pct_raw " ] ; then
136+ five_hour_pct= $( echo " $five_hour_pct_raw " | awk ' {printf "%d", int($1 + 0.5)} ' )
137+ five_hour_reset_epoch =$( echo " $input " | jq -r ' .rate_limits .five_hour.resets_at // empty' 2> /dev/null)
138+ five_hour_reset=$( format_reset_time_epoch " $five_hour_reset_epoch " " time" )
213139
214- seven_day_pct =$( echo " $usage_data " | jq -r ' (. seven_day.utilization // 0) | round ' 2> /dev/null)
215- [ -z " $seven_day_pct " ] && seven_day_pct=0
216- seven_day_reset_iso =$( echo " $usage_data " | jq -r ' .seven_day.resets_at // empty' 2> /dev/null)
217- seven_day_reset=$( format_reset_time " $seven_day_reset_iso " " datetime" )
140+ seven_day_pct_raw =$( echo " $input " | jq -r ' .rate_limits. seven_day.used_percentage // empty ' 2> /dev/null)
141+ seven_day_pct= $( echo " $seven_day_pct_raw " | awk ' {printf "%d", int($1 + 0.5)} ' )
142+ seven_day_reset_epoch =$( echo " $input " | jq -r ' .rate_limits .seven_day.resets_at // empty' 2> /dev/null)
143+ seven_day_reset=$( format_reset_time_epoch " $seven_day_reset_epoch " " datetime" )
218144fi
219145
220146# Format cost as $X.XXXX (4 decimal places), dropping trailing zeros after 2
@@ -293,7 +219,7 @@ printf "%b" "$SEP"
293219printf " effort: %b%s%b" " $effort_color " " $effort_level " " $C_RESET "
294220
295221# Line 2: Current (5h) bar | Weekly (7d) bar
296- if [ -n " $usage_data " ]; then
222+ if [ -n " $five_hour_pct_raw " ]; then
297223 printf " \n"
298224 printf " %bcurrent:%b " " $C_WHITE " " $C_RESET "
299225 build_bar " $five_hour_pct " 10
0 commit comments