A Windows system tray app that shows your claude.ai usage (5-hour and weekly limits) as live, color-coded gauges — no browser tab required.
How it looks:
When you hover on it:
When you click on it:
- Tray icon renders two concentric rings: inner = 5-hour usage, outer = weekly usage
- Click the tray icon for a bigger popup view with reset times
- Colors shift green → amber → red as usage climbs, with configurable thresholds
- Optionally invert the gauges to count down what's left instead of up what's used
- One-time login (embedded browser, auto-captures the session — no copy/pasting cookies)
- Session persists across restarts; you only see the login window again once it actually expires
- Detailed local logging for troubleshooting
Grab TokenometerSetup.exe from a release (or build it yourself, see below) and run it. It:
- Installs per-user to
%LocalAppData%\Programs\Tokenometer— no admin rights needed - Adds a Start Menu entry and a proper uninstaller
- Optionally registers itself to launch at sign-in
Requires the .NET 8 Desktop Runtime — Windows will prompt you to install it automatically if it's missing.
- Right-click the tray icon → Settings... → Log in to claude.ai... and sign in through the embedded window. It closes automatically once your session is captured — your organization id is captured at the same time (from claude.ai's own
lastActiveOrgcookie), no DevTools required. - That's it — the gauges start updating (polling every 3 minutes by default).
The login window may sit open for a few seconds after you sign in: sessionKey arrives the moment you authenticate, but the lastActiveOrg cookie the organization id comes from only lands once claude.ai redirects into your organization. It waits up to 20s for that, then closes.
There's no manual override for the organization id — if claude.ai ever renames the lastActiveOrg cookie and auto-detection breaks, logging out and back in is the way to retry it (see Troubleshooting below).
Until you log in, the app shows mock/random data so you can see what the gauges look like.
| Item | What it does |
|---|---|
| Check now | Force an immediate refresh instead of waiting for the next poll |
| Settings... | Opens the settings window (below) |
| Exit | Quits the app |
| Item | What it does |
|---|---|
| Log in to claude.ai... | Opens the embedded login window; also auto-detects your organization id |
| Log out | Clears the sign-in marker, the embedded browser's own cookies, and the auto-detected organization id |
| Gauge Display... | Change the amber/red usage thresholds (default 70% / 90%), and toggle showing remaining instead of used |
| View log... | Opens the log file for troubleshooting |
| Verbose logging | Records every poll, not just errors. Off by default; ticking it asks for confirmation, since it's meant for active troubleshooting rather than everyday use |
Everything is stored under %AppData%\Tokenometer\:
| File | Contents |
|---|---|
signed-in.marker |
Empty file; its presence is how the app knows you've logged in. Your session itself lives only in the WebView2\ profile below, never in a second copy |
organization-id.txt |
The organization id, auto-detected during login (not a secret) |
gauge-settings.json |
Your color threshold and gauge-direction preferences |
log.txt / log.old.txt |
Diagnostic logs (rotated at 5MB), never contains the cookie value itself |
log-settings.json |
Whether the log records per-poll detail (verbose) |
WebView2\ |
The embedded browser's profile (cookies, cache) |
Uninstalling the app does not delete this folder, so reinstalling picks up right where you left off. Delete it manually if you want a clean slate.
Requires the .NET 8 SDK.
dotnet build Tokenometer.slnx
To publish a distributable build:
dotnet publish Tokenometer.csproj -c Release -r win-x64 --self-contained false -p:PublishSingleFile=false -o publish
dotnet test Tokenometer.Tests/Tokenometer.Tests.csproj
Tests cover the pure logic (gauge colour thresholds, the used-vs-remaining decision, the claude.ai response parser), UsageClient and UsagePoller's orchestration against fakes, and the file-backed stores against a temp directory — including the session.dat migration and the gauge-settings cache. The stores take an injectable folder for exactly that reason, and a module initializer redirects Logger so a test run never appends fixture data to your real log.txt.
WinForms UI and the WebView2 browser automation aren't unit tested; that would need UI automation rather than xUnit, and is a deliberate boundary rather than a gap that got missed. Worth knowing what that boundary costs: the organization-id race fixed in 0.3.0 lived in the timing between claude.ai setting two cookies, and no unit test would have caught it — only a genuine first-ever login on a clean profile did.
Requires Inno Setup 6.
& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer.issThis produces installer-output\TokenometerSetup.exe.
Note that installer.iss reads the version out of publish\Tokenometer.exe, so publish before compiling the installer. To cut a release, bump <Version> in Tokenometer.csproj — that's the only place it lives, and the installer and the startup log line both follow from it.
Start with View log... in the tray menu — by default it records startup, logins, logouts and anything that failed, and a healthy poll writes nothing. If that's not enough to see what's going on, tick Verbose logging in Settings (it'll ask you to confirm), reproduce the problem, then untick it again — that adds a line for every fetch attempt, HTTP status and timing. Either way the file rotates at 5MB into log.old.txt.
Common issues:
- "Organization ID not set" — auto-detection didn't run yet or failed; log out and back in via Settings to retry it.
- HTTP error / Cloudflare challenge page in the log — claude.ai's private endpoint or protection may have changed; this project may need updating.
- Login window closes instantly without asking you to log in — this is expected if your session is still valid from a previous login (the embedded browser profile persists). Use Settings → "Log out" first if you need to force a fresh login.
Tokenometer/
Program.cs entry point
TrayApplicationContext.cs composition root — tray icon, menu, wiring
Forms/ LoginForm, GaugePopupForm, GaugeSettingsForm, SettingsForm
Usage/ UsageClient, UsagePoller, UsageResponseParser, IUsageFetcher
Browser/ BrowserUsageFetcher, BrowserCookies, SharedBrowserEnvironment (the hidden WebView2 host)
Rendering/ GaugeRenderer (GDI+), GaugeColorSelector, GaugeDisplay, GaugeThresholds
Storage/ SignInState, OrganizationSettings, GaugeSettings, LogSettings + their interfaces
Logging/ Logger
Tokenometer.Tests/ xUnit tests + fakes for the above interfaces
UsageClient doesn't reach for SignInState/OrganizationSettings/BrowserUsageFetcher directly — it takes ISignInState, IOrganizationSettings, and IUsageFetcher through its constructor. TrayApplicationContext is the only place that wires the real implementations together, which is what lets UsageClientTests exercise the actual fetch/parse/error-handling logic against fakes instead of a live browser and network.
Response parsing (UsageResponseParser), the color-threshold decision (GaugeColorSelector), and the used-vs-remaining decision (GaugeDisplay) are all pure functions for the same reason — split out from the networking and GDI+ drawing code they used to live inside, specifically so they don't need a browser or a graphics context to test.
Note that inverting a gauge changes only the arc and the number, never the color: thresholds describe usage, so an almost-spent limit stays red whether it reads "95%" or "5% left". Feeding the inverted value into GaugeColorSelector would paint a brand-new session red, which is why the two decisions are kept apart.


