llm-watcher
kanshi · to watch

llm-watcher

Answers one question: am I burning this coding plan too fast?

One run against three providers
PLAN         5H WINDOW                        WEEKLY
minimax        4% used   0.1x  resets 1h 57m   49% used   0.8x  resets 2d 10h
minimax-max    4% used   0.1x  resets 1h 57m   39% used   0.6x  resets 2d 10h
glm            0% used     --                  20% used   0.5x  resets 4d 10h
claude        10% used   0.1x  resets 1h 37m   80% used   1.3x  resets 2d 14h
  └ Fable                                      61% used   1.0x  resets 2d 14h
cargo install --path .
Source on GitHub

Runs, prints, exits.


01

Reading a row

Every plan gets at least two windows — the rolling five-hour cap and the weekly cap. Each window prints the same three things.

49% used

Share spent

How much of this window’s quota is already gone.

0.8x

Pace

Quota spent measured against clock elapsed.

resets 2d 13h

Countdown

How long until this window rolls over.

Countdowns show the two largest units with zeros dropped — 2d 13h, 45m, plain 7d for a week untouched. Minutes remaining in a two-day window is not a number anyone acts on, so it is not shown.


02

Pace

Providers report quota in incompatible units, so raw numbers do not compare. Pace normalises them.

pace = fraction of quota consumed ÷ fraction of window elapsed

The notch marks how much of the window has elapsed; the bar is how much quota is gone.

-- means there is no honest signal yet: a window that just reset, or one whose boundaries the provider did not report. A fabricated ratio there reads as an emergency, so none is printed.


03

What it does not do

The absences are the point.

The Claude credential store is read, never modified. API keys are referenced by environment variable name and read at runtime — never stored in the config, never accepted as a command-line argument, because argv is visible in ps and lands in shell history.


04

Install

cargo install --path .

Usage

llm-watcher                            # table
llm-watcher --json                     # machine-readable
llm-watcher --account minimax-max      # one plan (repeatable)
llm-watcher --threshold 1.5            # exit 1 at or past 1.5x
llm-watcher --no-color

Configure

With no config file it falls back to MINIMAX_API_KEY, MINIMAX_MAX_API_KEY and ZHIPU_API_KEY / ZAI_API_KEY, so it works before you write anything. If Claude Code is logged in on this machine, a claude row appears too — no variable needed, because a Claude subscription has no API key to set.

For more than one account per provider — the case this tool exists for — write ~/.config/llm-watcher/config.toml:

[[account]]
name     = "minimax-work"
provider = "minimax"
key_env  = "MINIMAX_API_KEY"

[[account]]
name     = "glm"
provider = "zai"        # aliases: glm, zhipu
key_env  = "ZHIPU_API_KEY"

[[account]]
name     = "claude"
provider = "anthropic"  # aliases: claude, claude-code
                        # no key_env — the token comes
                        # from Claude Code

Exit codes

CodeMeaning
0Ran fine
1--threshold was given and some window reached it
2Every account failed, or the configuration is unusable

A single failing account does not abort the run — the error lands on its own row and the other plans still report. --threshold plus --json is enough for a cron job or a waybar module without any of this needing a daemon.


05

Provider status

When a row’s provider has a public status page, llm-watcher fetches it and appends a marker the moment the indicator is anything other than none. A healthy run reads exactly as it did before — the marker is the only difference.

A degraded run — Claude row carries the status page
PLAN         5H WINDOW                        WEEKLY
minimax       10% used   0.1x  resets 4h 50m   49% used   0.8x  resets 2d 10h
glm           15% used   0.2x  resets 4h 45m   20% used   0.3x  resets 4d 13h
claude         0% used     --  resets 4h 55m    3% used   0.1x  resets 4d 18h  ⚠ minor — Degraded performance for Claude Opus 5 and Claude Haiku 4.5

The marker carries the Statuspage.io indicator (none · minor · major · critical · maintenance · custom), a colour band by severity, and the first open incident name. none produces no marker, so a row that is healthy prints nothing new — the same shape it had before this feature landed.

Only Anthropic is wired up today (status.claude.com). Adding another provider is one match arm on Provider::status_page_url() plus a fixture in tests/fixtures/. MiniMax and Z.ai return None until their pages are verified.

The status fetch only runs when the quota fetch succeeds, so the hermetic test suite never reaches the network. Failures collapse to an absent field, not an error — the quota row is unaffected.


06

Providers

ProviderEndpointAuth
MiniMax Token Plan GET api.minimax.io/v1/token_plan/remains Authorization: Bearer <key>
Z.ai / GLM Coding Plan GET api.z.ai/api/monitor/usage/quota/limit Authorization: <token> no Bearer prefix
Anthropic Claude
Pro, Max 5x, Max 20x
GET api.anthropic.com/api/oauth/usage Authorization: Bearer <OAuth token>

The asymmetry in that last column is real, not a typo. Getting it backwards produces a 401 that reads exactly like a bad key.

Anthropic’s is the subscription surface, not the pay-as-you-go API — the same data Claude Code’s /usage command shows. No provider documents its response body, so the README records what was captured live, with the real documents kept as fixtures.

Read the full documentation