# quotch
`quotch` ("quota + check") prints subscription-plan usage for AI coding agents — fast, cross-platform, and JSON-native.
It reads the usage limits your agents actually enforce. For Claude Code that means the **authoritative numbers from Anthropic's OAuth usage endpoint** — the same figures the service bills against — not a log-file estimate that drifts from reality. Credentials are read-only: quotch reads each agent's own credential store and never writes or refreshes tokens. One provider failing never affects the others. Humans get colored bars; AI agents get a frozen JSON contract.
## Providers
| Claude Code | ✅ shipped | 5h session, 7d weekly (all models), 7d per-model |
| GitHub Copilot | ✅ shipped | monthly premium interactions (request counts) |
| Codex | ✅ shipped | 5h + weekly (Plus/Pro) or monthly (Go) |
| Antigravity | ✅ shipped | 5h + weekly per model group (via agy / Google) |
## Install
From crates.io:
```bash
cargo install quotch
```
From git or a local checkout:
```bash
cargo install --git https://github.com/sibincbaby/quotch
cargo install --path .
```
Prebuilt binaries are attached to each GitHub Release: static musl on Linux, macOS (arm64 + x64), and Windows — produced by the release workflow on version tags.
## Usage
### Humans
```
$ quotch
claude · max 5h ████░░░░░░░░░░░░░░░░ 19% resets 3h 26m
7d ████░░░░░░░░░░░░░░░░ 21% resets 5d 22h
7d:fable ███████░░░░░░░░░░░░░ 35% resets 5d 22h
```
On a TTY the bar and percentage are colored by utilization: green < 70, yellow < 90, red ≥ 90. Non-`ok` snapshots render as a single line:
```
claude (stale 2m) ... # header suffix when serving cached data after a failed fetch
codex ⚠ not logged in (run: codex) # auth_missing — log in with the provider's own CLI
codex ✗ network: timed out # error — the message comes from the provider
```
### AI agents
Always use `--json`. The envelope is a **frozen v1 contract**:
```bash
quotch --json # machine output, served from cache (≤ 60s old)
quotch --json --raw # also include each provider's untouched response under "raw"
quotch --refresh # bypass cache and failure backoff, force a live fetch
```
```json
{
"v": 1,
"generated_at": "2026-07-19T10:00:00Z",
"snapshots": [
{
"provider": "claude",
"account": "default",
"plan": "max",
"status": "ok",
"fetched_at": "2026-07-19T09:59:59Z",
"windows": [
{ "key": "5h", "kind": "rolling", "unit": "percent",
"used_pct": 51.0, "used": null, "limit": null,
"unlimited": false, "resets_at": "2026-07-19T13:20:00Z" }
]
}
]
}
```
Full JSON Schema: [`docs/schema.json`](docs/schema.json). Agent-facing usage notes: [`skill/SKILL.md`](skill/SKILL.md).
**Frozen-schema guarantee.** `v` is `1`. Evolution is additive only: new fields, new `status` values, and new window `key`s may appear, but existing fields never change meaning or type. **Consumers must ignore unknown fields** and never assume a fixed set of window keys.
- Window `key`: `5h` rolling session, `7d` weekly all-models, `7d:<model>` weekly per-model, `5h:<group>` / `7d:<group>` per-model-group (Antigravity), `monthly` / `monthly:<key>` calendar month (Copilot premium requests), plus unknown keys.
**Exit codes.** Always `0` — per-provider problems are data in `status`, never process failures. Parse stdout; do not branch on exit code. `2` is returned only for CLI usage errors.
## For AI agents
quotch ships an agent skill inside the binary. Install it so an agent knows when and how to check quota:
```bash
quotch skill install # ~/.claude/skills/quotch/SKILL.md (user scope)
quotch skill install --project # ./.claude/skills/quotch/SKILL.md
quotch skill install --dir <path> # <path>/quotch/SKILL.md
quotch skill print # write the skill to stdout
```
`SKILL.md` tells an agent to prefer `quotch --json`, how to read the envelope, when to warn before token-heavy work (highest `used_pct` is the binding constraint; ≥ 90 means warn and cite `resets_at`), and useful `jq` one-liners. The skill is embedded in the binary, so **reinstall after upgrading quotch** to pick up the latest version.
## Architecture
```
src/
main.rs arg parsing, warm-cache fast path, concurrent per-account fetches, output
model.rs Account, Snapshot, Window, Status — the internal + wire types
registry.rs all() — the list of active providers
cache.rs atomic on-disk cache, 60s TTL
render/ json.rs (frozen v1 envelope) + line.rs (human bars)
providers/ mod.rs (Provider trait) + claude.rs, codex.rs, copilot.rs, antigravity.rs
```
Each provider reads credentials inline today; a shared `creds.rs` helper may be
extracted once multi-account discovery lands.
Every provider implements one trait:
```rust
pub trait Provider: Sync {
fn id(&self) -> &'static str;
fn discover(&self) -> Vec<Account>; // offline, cheap, no network
fn fetch(&self, acct: &Account) -> Result<Snapshot, FetchError>; // blocking, own timeout
}
```
An `Account` is a **credential source, not a person** — its `id` is offline-derivable (`default`, a label, or a hash of a path). The cache is keyed `provider:account`, so multiple accounts of one provider never collide. Discovery is offline and cheap; only stale/missing pairs hit the network, each on its own thread, so a slow or broken provider can't stall the others.
### Adding a provider
1. Implement `Provider` in `src/providers/<name>.rs`.
2. Return one `Account` per credential source from `discover()` (offline, no network).
3. In `fetch()`, call the provider's usage endpoint and map its native response into one or more `Window`s.
4. Register it in `registry.rs::all()`.
## Roadmap
- multi-account per provider (Codex `--auth` files, multiple Google/GitHub logins)
- statusline / tmux renderers
- usage history and burn-rate
## License
MIT