@@ -188,7 +188,16 @@ function Get-OAuthToken {
188188 return $null
189189}
190190
191- # ===== Usage limits with caching =====
191+ # ===== Usage limits =====
192+ # First, try to use rate_limits data provided directly by Claude Code in the JSON input.
193+ $builtinFiveHourPct = $data.rate_limits.five_hour.used_percentage
194+ $builtinFiveHourReset = $data.rate_limits.five_hour.resets_at
195+ $builtinSevenDayPct = $data.rate_limits.seven_day.used_percentage
196+ $builtinSevenDayReset = $data.rate_limits.seven_day.resets_at
197+
198+ $useBuiltin = ($null -ne $builtinFiveHourPct ) -or ($null -ne $builtinSevenDayPct )
199+
200+ # Fall back to cached API call only when Claude Code didn't supply rate_limits data
192201$cacheDir = Join-Path $env: TEMP " claude"
193202$cacheFile = Join-Path $cacheDir " statusline-usage-cache.json"
194203$cacheMaxAge = 60 # seconds between API calls
@@ -198,37 +207,45 @@ if (-not (Test-Path $cacheDir)) { New-Item -ItemType Directory -Path $cacheDir -
198207$needsRefresh = $true
199208$usageData = $null
200209
201- # Check cache
202- if (Test-Path $cacheFile ) {
203- $cacheMtime = (Get-Item $cacheFile ).LastWriteTime
204- $cacheAge = ((Get-Date ) - $cacheMtime ).TotalSeconds
205- if ($cacheAge -lt $cacheMaxAge ) {
206- $needsRefresh = $false
207- $usageData = Get-Content $cacheFile - Raw
210+ if (-not $useBuiltin ) {
211+ # Check cache — shared across all Claude Code instances to avoid rate limits
212+ if (Test-Path $cacheFile ) {
213+ $cacheMtime = (Get-Item $cacheFile ).LastWriteTime
214+ $cacheAge = ((Get-Date ) - $cacheMtime ).TotalSeconds
215+ if ($cacheAge -lt $cacheMaxAge ) {
216+ $needsRefresh = $false
217+ $usageData = Get-Content $cacheFile - Raw
218+ }
208219 }
209- }
210220
211- # Fetch fresh data if cache is stale
212- if ($needsRefresh ) {
213- $token = Get-OAuthToken
214- if ($token ) {
215- try {
216- $headers = @ {
217- " Accept" = " application/json"
218- " Content-Type" = " application/json"
219- " Authorization" = " Bearer $token "
220- " anthropic-beta" = " oauth-2025-04-20"
221- " User-Agent" = " claude-code/2.1.34"
222- }
223- $response = Invoke-RestMethod - Uri " https://api.anthropic.com/api/oauth/usage" `
224- - Headers $headers - Method Get - TimeoutSec 10 - ErrorAction Stop
225- $usageData = $response | ConvertTo-Json - Depth 10
226- $usageData | Set-Content $cacheFile - Force
227- } catch {}
228- }
229- # Fall back to stale cache
230- if (-not $usageData -and (Test-Path $cacheFile )) {
231- $usageData = Get-Content $cacheFile - Raw
221+ # Fetch fresh data if cache is stale
222+ if ($needsRefresh ) {
223+ # Touch cache immediately (stampede lock)
224+ if (Test-Path $cacheFile ) {
225+ (Get-Item $cacheFile ).LastWriteTime = Get-Date
226+ } else {
227+ New-Item - ItemType File - Path $cacheFile - Force | Out-Null
228+ }
229+ $token = Get-OAuthToken
230+ if ($token ) {
231+ try {
232+ $headers = @ {
233+ " Accept" = " application/json"
234+ " Content-Type" = " application/json"
235+ " Authorization" = " Bearer $token "
236+ " anthropic-beta" = " oauth-2025-04-20"
237+ " User-Agent" = " claude-code/2.1.34"
238+ }
239+ $response = Invoke-RestMethod - Uri " https://api.anthropic.com/api/oauth/usage" `
240+ - Headers $headers - Method Get - TimeoutSec 10 - ErrorAction Stop
241+ $usageData = $response | ConvertTo-Json - Depth 10
242+ $usageData | Set-Content $cacheFile - Force
243+ } catch {}
244+ }
245+ # Fall back to stale cache
246+ if (-not $usageData -and (Test-Path $cacheFile )) {
247+ $usageData = Get-Content $cacheFile - Raw
248+ }
232249 }
233250}
234251
@@ -245,9 +262,39 @@ function Format-ResetTime([string]$isoStr, [string]$style) {
245262 } catch { return $null }
246263}
247264
265+ # Format Unix epoch reset time to compact local time
266+ function Format-EpochResetTime ([object ]$epoch , [string ]$style ) {
267+ if ($null -eq $epoch -or " $epoch " -eq " null" -or " $epoch " -eq " " ) { return $null }
268+ try {
269+ $dt = [DateTimeOffset ]::FromUnixTimeSeconds([long ]$epoch ).LocalDateTime
270+ switch ($style ) {
271+ " time" { return $dt.ToString (" h:mmtt" ).ToLower() }
272+ " datetime" { return $dt.ToString (" MMM d, h:mmtt" ).ToLower() }
273+ default { return $dt.ToString (" MMM d" ).ToLower() }
274+ }
275+ } catch { return $null }
276+ }
277+
248278$sep = " ${dim} |${reset} "
249279
250- if ($usageData ) {
280+ if ($useBuiltin ) {
281+ # ---- Use rate_limits data provided directly by Claude Code in JSON input ----
282+ if ($null -ne $builtinFiveHourPct ) {
283+ $fiveHourPct = [math ]::Floor([double ]$builtinFiveHourPct )
284+ $fiveHourColor = Get-UsageColor $fiveHourPct
285+ $out += " ${sep}${white} 5h${reset} ${fiveHourColor}${fiveHourPct} %${reset} "
286+ $fiveHourReset = Format-EpochResetTime $builtinFiveHourReset " time"
287+ if ($fiveHourReset ) { $out += " ${dim} @${fiveHourReset}${reset} " }
288+ }
289+
290+ if ($null -ne $builtinSevenDayPct ) {
291+ $sevenDayPct = [math ]::Floor([double ]$builtinSevenDayPct )
292+ $sevenDayColor = Get-UsageColor $sevenDayPct
293+ $out += " ${sep}${white} 7d${reset} ${sevenDayColor}${sevenDayPct} %${reset} "
294+ $sevenDayReset = Format-EpochResetTime $builtinSevenDayReset " datetime"
295+ if ($sevenDayReset ) { $out += " ${dim} @${sevenDayReset}${reset} " }
296+ }
297+ } elseif ($usageData ) {
251298 try {
252299 $usage = if ($usageData -is [string ]) { $usageData | ConvertFrom-Json } else { $usageData }
253300
0 commit comments