Metadata-Version: 2.4
Name: tokenmaxxing
Version: 0.1.0
Summary: Menu bar app showing your live Claude Code session and weekly usage as a colored progress bar.
Project-URL: Homepage, https://github.com/alvations/tokenmaxxing
Project-URL: Repository, https://github.com/alvations/tokenmaxxing
Project-URL: Issues, https://github.com/alvations/tokenmaxxing/issues
Author-email: alvations <alvations@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: anthropic,claude,claude-code,menu-bar,rate-limit,usage
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Desktop Environment
Classifier: Topic :: Software Development
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: rumps>=0.4.0
Description-Content-Type: text/markdown

# tokenmaxxing

A menu bar app that shows your live Claude Code session and weekly usage as a colored progress bar — sitting next to the system clock so you always know how close you are to a rate-limit reset.

```
Session ███░░░░░ 38% · resets at Fri 07:00
Weekly  ███████░ 87% · resets at Fri 09:00
```

The bar is tinted by utilization:

| color | range  | meaning                             |
| ----- | ------ | ----------------------------------- |
| ⚪    | n/a    | no active window / data unavailable |
| 🟢    | <50%   | plenty of headroom                  |
| 🟡    | 50–79% | getting close                       |
| 🔴    | ≥80%   | reaching the limit very soon        |

Only the filled `█` segment is colored; the empty `░` segment stays in the menu bar's default text color (white on dark, dark on light).

## Install

Requires **Python 3.9+**.

```sh
pip install tokenmaxxing
```

This pulls in [`rumps`](https://github.com/jaredks/rumps) (which in turn pulls in PyObjC).

## Run

```sh
tokenmaxxing
```

Or as a module:

```sh
python -m tokenmaxxing
```

Detached (so the menu bar app survives the launching shell):

```sh
nohup tokenmaxxing >/dev/null 2>&1 </dev/null &!
```

## Autostart

Pick **one** of the two options below. Don't enable both — you'll get two instances of the app.

### Option 1 — Launch at login

Open the menu bar dropdown → **Preference** → check **Launch at login**.

This writes a `~/Library/LaunchAgents/com.tokenmaxxing.app.plist` and a tiny wrapper script at `~/.tokenmaxxing/tokenmaxxing` (so System Settings → Login Items shows the friendly name, not "python3.9"). The system's `launchd` reads the plist on every login and starts the app. Uncheck the menu item to remove both files.

> **Heads up — Gatekeeper warning.** Your system will show "tokenmaxxing is an item from an unidentified developer" the first time it's added to login items. This is unavoidable for any unsigned tool (code signing requires a paid Apple Developer account). To allow it: **System Settings → Privacy & Security → scroll to the bottom → click "Allow Anyway"**. Subsequent boots skip the prompt. If that friction bothers you, use Option 2 instead — launching from a shell is treated as user-initiated and never triggers Gatekeeper.

### Option 2 — Launch on first interactive shell (terminal-driven)

Append this guarded launcher to `~/.zshrc`:

```sh
# tokenmaxxing — menu bar app showing Claude Code session/weekly usage
if [[ -o interactive ]] && ! pgrep -f "tokenmaxxing" >/dev/null 2>&1; then
    nohup tokenmaxxing >/dev/null 2>&1 </dev/null &!
fi
```

The `pgrep` guard keeps it singleton across nested shells. The `&!` (zsh) detaches and disowns. The first interactive shell of the day starts it; subsequent shells skip via the guard.

## How it works

It polls Anthropic's OAuth usage endpoint — the same call Claude Code makes internally:

```
GET https://api.anthropic.com/api/oauth/usage
Authorization: Bearer <token>
anthropic-beta: oauth-2025-04-20
```

The OAuth access token is read from your system keychain (service `Claude Code-credentials`) with `~/.claude/.credentials.json` as fallback. Polling consumes **zero tokens** — it returns metadata only:

```json
{
  "five_hour":         {"utilization": 38, "resets_at": "2026-05-01T11:00:00Z"},
  "seven_day":         {"utilization": 87, "resets_at": "2026-05-01T13:00:00Z"},
  "seven_day_opus":    null,
  "seven_day_sonnet":  {"utilization": 100, "resets_at": "2026-05-01T13:00:00Z"},
  "extra_usage":       {"is_enabled": false}
}
```

Refresh runs on a background thread every 5 minutes (the OAuth endpoint has aggressive undocumented rate limits, so polling more frequently triggers `429`s). Token refresh is handled by Claude Code itself — when `claude` runs, it rotates the keychain entry and the next poll picks it up automatically.

## Dropdown menu

```
✓ 🟢 Session (5h)    ███░░░░░ 38%   resets at Fri 07:00
  🔴 Weekly (7d)     ███████░ 87%   resets at Fri 09:00
  🔴 Weekly Sonnet   ████████ 100%  resets at Fri 09:00
  ─────────────
  Plan: Claude Max · Tier 2
  Extra usage: off
  ─────────────
  Preference                  ▸     ← reset-time vs elapsed-time, opt-in 7-day history
  Open Claude dashboard…
  ─────────────
  Updated: 14:32:01
  Refresh now
  Quit
```

Click any view row to switch which window the menu-bar bar tracks. The `✓` marker shows the active view.

## Preferences

Open the **Preference** submenu:

- **Show "resets at Fri 07:00"** / **Show "resets in 4h10m"** — radio toggle for time format
- **Show 7-day history** — opt-in checkbox; when on, two extra rows appear in the dropdown:
  - `7d: ▁▂▃▄▅▆▇█ ↑` — sparkline of utilization over the last 7 days, plus a trend arrow comparing the last 24h vs the prior 24h (±3pp threshold)
  - `7d: min 12% · avg 47% · max 89%` — min / avg / max for the same window
- **Launch at login** — opt-in checkbox; installs/removes a LaunchAgent plist at `~/Library/LaunchAgents/com.tokenmaxxing.app.plist`. Takes effect on next login.

The history is recorded continuously to `~/.tokenmaxxing/history.json` (one snapshot per successful poll, capped at 2000 rolling entries) regardless of whether you display it — so when you turn it on, data is already there.

When the most recent poll has failed (network blip, transient error) but a previous successful poll's data is still cached, every row shows a `(stale)` suffix. When the OAuth endpoint is rate-limiting, you see `[rate limited]` instead.

## Configuration (env vars)

- `CLAUDE_DASHBOARD_URL` — URL for the "Open Claude dashboard…" item (default `https://claude.ai/settings/usage`)

## Implementation notes

- **Why not parse `~/.claude/projects/*.jsonl`?** Tried first, scrapped. Even with `os.scandir` + per-file mtime cache, parsing was lossy (no auth signal, no plan-aware caps) and stale (only sees what Claude Code chose to log). The OAuth endpoint is authoritative and free.
- **Why two threads?** AppKit calls (setting menu/title) must happen on the main thread; HTTP calls shouldn't block it. A daemon worker fetches usage every 5 min and stages the result; a 1s `rumps.Timer` on the main thread applies whatever's staged so countdowns tick smoothly.
- **Coloring:** `NSMutableAttributedString` with `NSForegroundColorAttributeName` applied only to the leading `█` chars. `NSColor.system{Green,Yellow,Red}Color` adapt to dark/light mode. The empty `░` segment is left untouched so it inherits the default `labelColor`.
- **Backoff:** consecutive 429s back off exponentially up to 30 minutes between polls before retrying.

## License

Apache-2.0
