Skip to content

feat(daemon): add Anthropic rate limit quota to cc statusline#213

Merged
AnnatarHe merged 2 commits intomainfrom
feat/cc-statusline-anthropic-quota
Feb 6, 2026
Merged

feat(daemon): add Anthropic rate limit quota to cc statusline#213
AnnatarHe merged 2 commits intomainfrom
feat/cc-statusline-anthropic-quota

Conversation

@AnnatarHe
Copy link
Contributor

@AnnatarHe AnnatarHe commented Feb 6, 2026

Summary

  • Add 5-hour and 7-day Anthropic session quota utilization display to cc statusline output (🚦 5h:45% 7d:23%)
  • Daemon fetches and caches rate limit data from Anthropic OAuth usage API with 10-minute TTL, using async background refresh to avoid blocking the timer loop
  • OAuth token read fresh from macOS Keychain on each fetch; non-macOS platforms gracefully omit quota fields

Statusline Output Order

🌿 main | 🤖 claude-opus-4 | 💰 $1.23 | 📊 $4.56 | 🚦 5h:45% 7d:23% | ⏱️ 1h1m | 📈 75%

Files Changed

  • New: daemon/anthropic_ratelimit.go — Keychain token retrieval, Anthropic API call, types
  • New: daemon/anthropic_ratelimit_test.go — API parsing, keychain JSON, cache tests
  • Modified: daemon/cc_info_timer.go — Rate limit cache + async fetch in timer loop
  • Modified: daemon/socket.go — Rate limit fields in CCInfoResponse
  • Modified: commands/cc_statusline.go — Quota display with color coding (green/yellow/red)
  • Modified: commands/cc_statusline_test.go — Quota format, propagation, and color tests
  • Modified: daemon/cc_info_handler_test.go — Rate limit response field tests

Test plan

  • go build ./... compiles cleanly
  • go vet ./daemon/... ./commands/... passes
  • All daemon tests pass (go test ./daemon/...)
  • All CC statusline tests pass (27 tests including 8 new ones)
  • Manual: run daemon on macOS, verify cc statusline shows quota data
  • Verify non-macOS: quota fields omitted gracefully (nil pointers, 🚦 - display)

🤖 Generated with Claude Code


Open with Devin

Display 5-hour and 7-day session quota utilization from the Anthropic
OAuth usage API in the cc statusline output. The daemon fetches and
caches this data (10-minute TTL) using the same lazy-fetch background
refresh pattern as git branch and today cost. OAuth token is read fresh
from macOS Keychain on each fetch; non-macOS platforms gracefully omit
these fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @AnnatarHe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement by integrating Anthropic API rate limit utilization directly into the cc statusline command. This feature provides users with immediate, color-coded feedback on their current API usage against defined quotas, improving transparency and helping to manage API consumption. The underlying implementation ensures efficient and secure data retrieval, leveraging asynchronous operations and platform-specific security mechanisms.

Highlights

  • Anthropic Rate Limit Display: The cc statusline output now includes a display for Anthropic's 5-hour and 7-day session quota utilization, formatted as 🚦 5h:45% 7d:23%.
  • Daemon-side Rate Limit Fetching and Caching: A new daemon component fetches and caches Anthropic rate limit data from the OAuth usage API. This data is refreshed asynchronously in the background with a 10-minute TTL to avoid blocking the main timer loop.
  • Secure OAuth Token Retrieval: The Anthropic OAuth token is securely read fresh from the macOS Keychain on each fetch. Non-macOS platforms gracefully omit the quota fields from the display.
  • Color-coded Quota Display: The quota utilization display in the statusline is color-coded (green, yellow, red) based on the highest utilization percentage across the 5-hour and 7-day buckets, providing quick visual feedback.
Changelog
  • commands/cc_statusline.go
    • Added FiveHourUtilization and SevenDayUtilization fields to ccStatuslineResult struct to hold new rate limit data.
    • Updated formatStatuslineOutput function signature and calls to accept and display the new utilization fields.
    • Introduced formatQuotaPart function for formatting and color-coding the rate limit display based on utilization levels.
    • Modified outputFallback to include a placeholder for the new quota display when data is unavailable.
    • Updated getDaemonInfoWithFallback to propagate the new utilization data received from the daemon.
  • commands/cc_statusline_test.go
    • Updated existing formatStatuslineOutput tests to accommodate the new function signature.
    • Added new tests for formatQuotaPart to verify correct formatting and color-coding for various utilization scenarios (nil, low, medium, high).
    • Added new tests for formatStatuslineOutput to confirm correct display with and without quota data.
    • Implemented a new test for getDaemonInfoWithFallback to ensure proper propagation of rate limit fields from the daemon response.
  • daemon/anthropic_ratelimit.go
    • New file: Implements the core logic for fetching Anthropic rate limit data.
    • Defined AnthropicRateLimitData and anthropicRateLimitCache structs for data storage and caching.
    • Introduced structs (anthropicUsageResponse, anthropicUsageBucket, keychainCredentials, keychainOAuthEntry) for API response and Keychain JSON parsing.
    • Implemented fetchClaudeCodeOAuthToken to securely retrieve the OAuth token from macOS Keychain.
    • Implemented fetchAnthropicUsage to call the Anthropic OAuth usage API and parse its response.
  • daemon/anthropic_ratelimit_test.go
    • New file: Contains unit tests for the Anthropic rate limit functionality.
    • Added tests for fetchAnthropicUsage covering successful API calls and non-OK HTTP responses.
    • Included tests for parsing Keychain JSON, including cases with missing or empty access tokens.
    • Added tests for the AnthropicRateLimitCache to verify caching behavior and data immutability.
  • daemon/cc_info_handler_test.go
    • Added new tests to handleCCInfo to verify that rate limit data is correctly included in the CCInfoResponse when cached and omitted when not available.
  • daemon/cc_info_timer.go
    • Added rateLimitCache field to CCInfoTimerService to manage Anthropic rate limit data.
    • Initialized rateLimitCache in NewCCInfoTimerService.
    • Modified stopTimer to clear the rate limit cache upon service shutdown.
    • Integrated asynchronous calls to fetchRateLimit within the timerLoop for background data refresh.
    • Implemented fetchRateLimit to periodically fetch and cache Anthropic rate limit data, with macOS-specific token retrieval and TTL checks.
    • Added GetCachedRateLimit method to safely retrieve a copy of the cached rate limit data.
  • daemon/socket.go
    • Added FiveHourUtilization and SevenDayUtilization fields to the CCInfoResponse struct to include rate limit data in daemon responses.
    • Updated handleCCInfo to populate these new fields from the ccInfoTimer service's cached rate limit data.
Activity
  • A new feature is introduced to display Anthropic rate limit quotas in the cc statusline.
  • New Go files (daemon/anthropic_ratelimit.go, daemon/anthropic_ratelimit_test.go) were added to handle the Anthropic API integration and testing.
  • Existing commands and daemon files were modified to integrate the new rate limit data into the statusline display and daemon's information handling.
  • Comprehensive unit tests were added for the new rate limit fetching, caching, and display logic, covering various scenarios including macOS Keychain interaction and API response parsing.
  • The pull request description includes a detailed test plan, outlining both automated and manual verification steps for the new functionality across different operating systems.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a useful feature to display Anthropic API rate limit utilization in the cc statusline. The implementation is well-structured, with caching, asynchronous background updates, and graceful degradation for non-macOS platforms. The code is generally high quality. My review includes a few suggestions to improve maintainability and testability by replacing magic numbers with constants, using a native library for keychain access, and improving the test strategy for the API fetching logic. Overall, great work on this feature.

Comment on lines 195 to 198
case maxUtil >= 80:
return color.Red.Sprint(text)
case maxUtil >= 50:
return color.Yellow.Sprint(text)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The utilization thresholds 80 and 50 are used as magic numbers. To improve readability and maintainability, consider defining them as named constants at the package level. This makes their purpose clear and simplifies future updates.

For example:

const (
	quotaHighUtilizationThreshold  = 80.0
	quotaMediumUtilizationThreshold = 50.0
)

return "", nil
}

out, err := exec.Command("security", "find-generic-password", "-s", "Claude Code-credentials", "-w").Output()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using exec.Command to interact with the macOS keychain creates a dependency on an external tool and makes testing difficult. Consider using a native Go library for keychain access, like github.com/keybase/go-keychain. This would make the implementation more robust, testable, and idiomatic.


// fetchAnthropicUsage calls the Anthropic OAuth usage API and returns rate limit data.
func fetchAnthropicUsage(ctx context.Context, token string) (*AnthropicRateLimitData, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.anthropic.com/api/oauth/usage", nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Anthropic API URL is hardcoded here. It's a good practice to define URLs and other similar magic strings as constants at the package level. This improves readability and makes the code easier to maintain.

For example:

const anthropicUsageAPIURL = "https://api.anthropic.com/api/oauth/usage"

Comment on lines +14 to +55
func TestFetchAnthropicUsage_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
assert.Equal(t, "oauth-2025-04-20", r.Header.Get("anthropic-beta"))

resp := anthropicUsageResponse{
FiveHour: anthropicUsageBucket{
Utilization: 0.45,
ResetsAt: "2025-01-15T12:00:00Z",
},
SevenDay: anthropicUsageBucket{
Utilization: 0.23,
ResetsAt: "2025-01-20T00:00:00Z",
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()

// We need to test with the real function but override the URL.
// Since fetchAnthropicUsage uses a hardcoded URL, we test the parsing logic
// by calling the test server directly.
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil)
assert.NoError(t, err)
req.Header.Set("Authorization", "Bearer test-token")
req.Header.Set("anthropic-beta", "oauth-2025-04-20")

resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()

var usage anthropicUsageResponse
err = json.NewDecoder(resp.Body).Decode(&usage)
assert.NoError(t, err)

assert.Equal(t, 0.45, usage.FiveHour.Utilization)
assert.Equal(t, "2025-01-15T12:00:00Z", usage.FiveHour.ResetsAt)
assert.Equal(t, 0.23, usage.SevenDay.Utilization)
assert.Equal(t, "2025-01-20T00:00:00Z", usage.SevenDay.ResetsAt)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test is named TestFetchAnthropicUsage_Success, but it doesn't call the fetchAnthropicUsage function. Because the URL is hardcoded in fetchAnthropicUsage, this test re-implements the HTTP request logic instead of testing the actual production function.

To enable proper unit testing, consider refactoring fetchAnthropicUsage to accept the URL as a parameter. This would allow you to pass the test server's URL during testing.

Example:

// In anthropic_ratelimit.go
func fetchAnthropicUsage(ctx context.Context, token string, url string) (*AnthropicRateLimitData, error) { /* ... */ }

// In test
data, err := fetchAnthropicUsage(context.Background(), "test-token", server.URL)
// assertions...

@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 46.95652% with 61 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
daemon/anthropic_ratelimit.go 0.00% 34 Missing ⚠️
daemon/cc_info_timer.go 48.93% 24 Missing ⚠️
commands/cc_statusline.go 90.32% 3 Missing ⚠️
Flag Coverage Δ
unittests 37.72% <46.95%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
daemon/socket.go 65.57% <100.00%> (+0.86%) ⬆️
commands/cc_statusline.go 67.64% <90.32%> (+6.03%) ⬆️
daemon/cc_info_timer.go 85.65% <48.93%> (-9.93%) ⬇️
daemon/anthropic_ratelimit.go 0.00% <0.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

Wrap the quota section (🚦) in OSC 8 hyperlink escape sequences so it
becomes a clickable link to https://claude.ai/settings/usage in
terminals that support it (iTerm2, Kitty, WezTerm).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 8 additional findings in Devin Review.

Open in Devin Review

// Fetch immediately on start
s.fetchActiveRanges(context.Background())
s.fetchGitInfo()
go s.fetchRateLimit(context.Background())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Untracked goroutines in fetchRateLimit can cause race conditions and goroutine leaks

The fetchRateLimit function is spawned as a detached goroutine (go s.fetchRateLimit(context.Background())) but is not tracked by the wg WaitGroup.

Root Cause and Impact

At daemon/cc_info_timer.go:159 and daemon/cc_info_timer.go:173, fetchRateLimit is spawned without being added to the WaitGroup:

go s.fetchRateLimit(context.Background())

However, when Stop() is called at line 109, it only waits for the timerLoop goroutine:

s.wg.Wait()

This creates two problems:

  1. Race condition: After Stop() clears the rate limit cache (lines 143-147), a still-running fetchRateLimit goroutine can write back to the cache (lines 406-409), leaving stale data in a "stopped" service.

  2. Goroutine leak: If the HTTP request in fetchAnthropicUsage is slow (up to 5 second timeout at daemon/anthropic_ratelimit.go:85), the goroutine continues running after Stop() returns, potentially accessing a service that the caller believes is fully stopped.

Impact: The race condition could cause inconsistent state if the timer is restarted, and the goroutine leak wastes resources.

Prompt for agents
To fix this issue, you need to track the fetchRateLimit goroutines in the WaitGroup. Modify the code as follows:

1. In timerLoop() at lines 159 and 173, wrap the goroutine call to track it:
   - Add s.wg.Add(1) before spawning the goroutine
   - Modify fetchRateLimit to call s.wg.Done() when it completes (using defer)

2. Alternatively, consider using a context with cancellation so that when Stop() is called, any in-flight HTTP requests can be cancelled. Pass a cancellable context derived from a service-level context instead of context.Background().

Example fix for lines 159 and 173:
   s.wg.Add(1)
   go func() {
       defer s.wg.Done()
       s.fetchRateLimit(context.Background())
   }()
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@AnnatarHe AnnatarHe merged commit ee1737c into main Feb 6, 2026
5 checks passed
@AnnatarHe AnnatarHe deleted the feat/cc-statusline-anthropic-quota branch February 6, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant