cmd-usage 0.7.16

Live Command Code (commandcode.ai) usage dashboard: credits, 5-hour and weekly windows, plan limits. Watch mode or one-shot.
Documentation

cmduse

Live Command Code usage dashboard for your terminal.

CI crates.io license

What it is

Plan dashboard, account-wide usage reports (all harnesses), offline local reports, and a customisable statusline.

Install

brew install JeffreyJYZ/tap/cmduse   # macOS (Homebrew)
cargo install cmd-usage              # any platform with Rust

Quick start

Command Code Usage · GOAT · active
Period ends 2026-09-27

Credits $59.12 / $70.00 monthly · $0.00 purchased · $0.00 free

Usage windows
 Monthly   15.1% ━━╱╱╱╱╱╱╱╱  $10.56 / $70.00 · resets in 22d 9h · window 28% elapsed · $19.60 by now
 5-hour     1.5% ╱╱╱╱╱╱╱╱╱╱  $0.21 / $14.00 · resets in 4h 58m · window 0% elapsed
 Weekly    15.8% ━╱╱╱╱╱╱╱╱╱  $5.54 / $35.00 · resets in 6d 5h · window 11% elapsed · on pace to hit cap in 4d 3h

This billing period
 Requests 856 · Cost $10.69 · Tokens 89.5M in / 265.7K out · Success 100%

Usage

cmduse                       # live dashboard, redraws in place (default every 5s)
cmduse -1                    # one-shot fetch, print, exit
cmduse -V                    # print version
cmduse -p -1                 # plain output, no ANSI (for scripts/pipes)
                             # same fields as the colored dashboard: period end,
                             # monthly quota, Monthly row (+ $X by now), window % elapsed
                             # (also what the MCP `usage` tool and the opencode
                             # /cmd-usage dialog render)
cmduse -i 30                 # refresh every 30s
cmduse -i 5m                 # duration suffixes: s, m, h, d
cmduse -w 40                 # 40-char progress bars
cmduse watch                 # explicit watch mode (same as bare cmduse)
man cmduse                   # full behavior spec (brew installs the man page)

cmduse daily --days 14       # account usage by day (all harnesses, from usage API)
                             # --days max 365, fetched 8-at-a-time
cmduse daily --local         # CLI-logs-only (offline, misses other harnesses)
cmduse daily --tz +05:30     # bucket by a fixed UTC offset instead of UTC
cmduse daily --local --tz +05:30  # ...the local-log path honors --tz too
cmduse hourly --hours 6      # account usage by hour (default 24, max 168)
cmduse hourly --local        # hourly from local CLI logs (offline)
cmduse model                 # local usage by model (all time)
cmduse model --days 30       # ...only the last 30 days
cmduse model --since 2026-08-27T00:00:00Z   # ...or since a billing-period start
cmduse session --days 30     # local usage by project, same window flags
cmduse models                # live model list from the Command Code API
cmduse models --gated        # ...only models the current plan allows
cmduse models --gated --json # every model annotated with allowed + reason
cmduse plans                 # plan comparison table (marks your plan)
cmduse statusline            # compact one-liner for prompts/tmux
cmduse mcp                   # MCP stdio server (usage/plans/models/daily/hourly/session/model tools)
cmduse daily --json          # JSON output (daily, hourly, model, session, models, statusline, plans, -1)
cmduse daily --csv           # CSV output for daily/hourly/model/session

Color: SGR escapes are suppressed when stdout is not a terminal or when NO_COLOR is set non-empty. The live dashboard's spinner goes straight to /dev/tty, so it only runs when stdout is a terminal — --plain, or any piped or redirected run, never writes to the terminal at all. --plain forces plain text; reports have no --plain — pipe them or use --json/--csv.

MCP server

cmduse mcp runs an MCP stdio server (newline-delimited JSON-RPC, hand-rolled — no extra dependencies) so any MCP-capable host (Zed, and others) can call Command Code usage as tools:

Tool Body
usage one-shot dashboard (same as cmduse -1)
plans plan comparison table (same as cmduse plans)
models live model list, filtered to the current plan (same as cmduse models --gated)
daily account daily report; args days, tz (±HH:MM), local
hourly account hourly report; args hours, tz, local
session local usage by project/session; args since (ISO-8601), days, tz
model local usage by model; args since (ISO-8601), days, tz

Auth is the same as the CLI (CMD_API_KEY, then ~/.commandcode/auth.json). Tool errors come back as isError: true results; stdout carries protocol traffic only. Zed config:

{
  "context_servers": {
    "cmduse": { "source": "custom", "command": "cmduse", "args": ["mcp"] }
  }
}

-1 --json emits a single dashboard object (plan, credits, both windows, billing summary). --gated --json on models emits every model with allowed and reason instead of filtering.

GNU forms are accepted: --interval=5m, --days=14, and attached short values like -i30 / -w40. --last/-l are aliases for --days.

Cap alerts: in watch mode a desktop notification fires once when a window crosses into overflow (macOS osascript, Linux notify-send). Disable with cmduse config set notify=false.

Burn-rate: windows show on pace to hit cap in … when the current spend rate projects hitting the cap before the window resets, and only once the window is ≥10% elapsed (flat-rate projection is unreliable early). ponytail: assumes flat spend rate; bursty sessions shift the ETA.

Watch mode: a spend bursts (N samples) sparkline shows $ spent per refresh. It is on by default, hidden while idle (all-zero deltas), and its sample count is configurable. Session-only: a fresh run starts a fresh trend (no stale data from earlier runs):

cmduse config set burst_on=false      # turn the sparkline off
cmduse config set burst=80            # 80 samples (5–240)
cmduse -b 120                         # same for one run

Statusline

Template-driven via config. Placeholders:

Placeholder Shows
{plan} plan name (GOAT, Pro, …)
{credits} remaining monthly credits
{cap} monthly cap
{credits_bar} monthly usage bar
{5h_bar} {5h_pct} {5h_used} {5h_cap} {5h_eta} 5-hour window (eta = pace text, empty when none)
{wk_bar} {wk_pct} {wk_used} {wk_cap} {wk_eta} weekly window
cmduse config set sl="{plan} {credits}/{cap} 5h:{5h_pct} wk:{wk_pct}"
cmduse config set sl="{credits_bar}"           # just one bar
cmduse config set sl_colors=false              # strip ANSI
cmduse config set sl_ascii=true                # #--- bars instead of ━╱╱

Multi-line templates work (newlines allowed). Unknown placeholders are dropped. Wire into your shell prompt or tmux status:

# .zshrc / tmux status-right
status() { cmduse statusline 2>/dev/null; }

CMD_API_KEY env var overrides the stored key — point cmduse at any account without touching ~/.commandcode/auth.json.

Config

~/.config/cmd-usage/config.json:

{
  "interval_secs": 5,
  "bar_width": 20,
  "burst_enabled": true,
  "burst_samples": 40,
  "notify_on_cap": true,
  "statusline_template": "{plan} {credits}/{cap} · 5h {5h_bar} · wk {wk_bar}",
  "statusline_colors": true,
  "statusline_ascii": false,
  "check_updates": true,
  "dismissed_update": null
}

CLI flags override config. cmduse config set interval=<s> width=<n> burst_on=<bool> burst=<n> notify=<bool> sl=<tpl> sl_colors=<bool> sl_ascii=<bool> update_check=<bool> dismissed_update=<ver>.

Updates

Once per day (cache at ~/.cache/cmd-usage/last-check) the dashboard checks crates.io; when a newer cmd-usage exists it shows a boxed notice inside the watch frame (stderr for -1). The cached version is replayed every run, so the reminder persists until you upgrade. Dismiss or disable:

cmduse --dismiss-update              # hide this version until a newer one
cmduse config set update_check=false # never check
cmduse config set dismissed_update=<ver>

Data sources

  • Dashboard / daily / hourly / statusline: Command Code API with your account key. Daily and hourly cover every harness that used the key (CLI, Provider API, other agents).
  • model / session / daily --local: local session logs at ~/.commandcode/projects — offline, but only what the CLI recorded. model/session are all-time unless given --days N or --since <ISO>; the usage API has no per-model split, so local logs are the only per-model source.

Requirements

Logged-in Command Code CLI — reads your API key from ~/.commandcode/auth.json (run cmd login if missing), or set CMD_API_KEY.

How it works

ocuse — OpenCode Go/Zen (same crate, second binary)

The same crate ships ocuse, which tracks OpenCode Go (the Go subscription) and OpenCode Zen (pay-as-you-go) at full command/flag parity with cmduse — same subcommands, same --json/--csv, same config keys, same update check and cap notification. The two share one front-end (cli.rs parses both binaries' argv, config.rs owns both config files) and cmduse's render/watch/sparkline helpers; only the data source and the flags it forces differ (--plan/--window/--period-start/--infer-anniversary/--db/--no-api are ocuse-only, --local is cmduse-only).

ocuse                        # watch: live frame (colour, gauges, spend-burst sparkline)
ocuse -1                     # one-shot dashboard
ocuse -1 --json              # one-shot, machine-readable
ocuse daily [--days N] [--json|--csv] [--tz ±HH:MM]
ocuse hourly [--hours N] [--json|--csv] [--tz ±HH:MM]
ocuse model [id] [--since ISO] [--json|--csv]
ocuse session [--since ISO] [--json|--csv]
ocuse models [--gated] [--json]   # the Go catalogue for the selected plan
ocuse plans [--json]              # both plans' limits, rates and window shares
ocuse statusline [--json]         # compact one-liner, or its numbers as JSON
ocuse mcp                    # MCP stdio server (usage/plans/models/daily/hourly/session/model)

Two upstream facts shape it: OpenCode Go does have a usage API (the console's own source), and Go's allowance is per model — each model has a monthly dollar limit with windows of 5h = 20%, weekly = 50%, monthly = 100%. So ocuse reads GET https://opencode.ai/zen/go/v1/usage with the Go key (OCUSE_API_KEY, OPENCODE_API_KEY, else the live credential opencode itself is using — credential in opencode.db, which is where v2 and /connect write it, with the legacy ~/.local/share/opencode/auth.json as the fallback for a v1 install) for the three account meters — rolling (5-hour), weekly, monthly, each a percent and a reset instant, exactly what opencode.ai/console prints — and reports its own local usage from opencode.db (providers opencode-go and opencode, which record real cost) against the limits in core/zen.json (regenerated with bun scripts/extract-zen.ts) for everything the endpoint cannot give: the per-model table, daily/hourly/session, tokens. The endpoint's figures win wherever both exist, because the server's windows are not reproducible locally: its 5-hour window is a block that resets at its own instant, its week is anchored to Monday 00:00 UTC, its month is the subscription anniversary, and it meters with its own price basis (fitted from real rows, opencode's recorded cost sits below it). No key, no network, or --no-api leaves the windows as a local estimate, and the dashboard labels them that way. Go comes in two plans — Go ($10/mo) and Go Plus ($40/mo) — that share the token prices but grant different per-model limits; neither the store nor the endpoint names your plan, so ocuse selects it with --plan go|go-plus (default go). ocuse models lists that catalogue for the plan (--gated keeps only the models the plan grants); ocuse models --json is one object per line, like cmduse models --json.

The billing period comes from the endpoint's own monthly reset instant (the anniversary — hence the dashboard's Go billing month label); without it the default is the calendar month, --period-start YYYY-MM-DD pins a real one, --window all|<n>d reports other ranges, --since <ISO-8601> pins an exact instant for model/session, and --tz shifts day/hour buckets. --days (default 120) bounds the read window; hourly uses --hours (default 24, max 168). ocuse -1 --json carries the period bounds in epoch milliseconds — periodStart (with the alias periodStartAt) and periodEndAt, the resolved end of the period (null for --window all, whose range has no boundary) — plus an account object with the three meters ({status, percent, resetsAt} each, null when the endpoint did not answer) and an accountError string naming why it is null when that is worth saying (HTTP 401 api_key_expired, no Go key, a transport fault — null when the endpoint answered, and for --no-api, where the missing meters are a deliberate choice). The dashboard prints that reason under the GO heading, because "estimated locally" on its own cannot tell a missing key from an expired one, and only the expired one is something to act on (re-login does not renew it: create a new key in the OpenCode console). The dashboard names that end date on its period: line and splits period tokens in / out, mirroring cmduse. cmduse's Success N% is absent: opencode's own store records no per-request success/error signal.

ocuse renders with cmduse's own presentation: the same palette, the same severity-coloured gauges (green <70%, yellow 70–90%, red ≥90%), the same in-place watch redraw (cmduse's redraw_frame, so a shrinking frame never scrolls), and the same spend-burst sparkline. Both watch loop and gauges reuse cmduse/src/render.rs rather than a second copy. Every subcommand takes the colour flag — daily, hourly, session, model, models, plans and statusline included (bold headings, dim secondary text, cyan figures, an over-cap window or model flagged LIMIT EXCEEDED, and a per-model share of period spend). With the account meters in play each window row pairs this store's money with the ceiling the server's own share implies ($20.45 / $40.91 (50.0% of limit) — the pair divides to exactly the share printed beside it, while the two bases need not divide to each other, since the server meters higher), keeping the elapsed share measured against the server's own resets, an on pace to hit cap in … ETA whenever the burn reaches that ceiling before the reset, and the monthly window's $X by now pro-rata figure. No account-wide pool is invented: Go grants limits per model, so summing them into one ceiling is exactly the $215 that read as a budget the console does not have; a share under 0.1% implies no ceiling at all, and its row keeps the share alone. The per-model 5H/WK columns in the table stay local rolling windows and the dashboard says so. statusline's {five_hour_pct}/{weekly_pct}/{month_pct} (and their _bar forms) resolve to the account's shares when it answers and to a dash without it, while money placeholders stay local. --plain, NO_COLOR and a piped stdout drop colour and gauges; scripted output stays escape-free, and --json/--csv are never coloured.

Config lives at ~/.config/ocuse/config.json with the same keys as cmduse's: interval_secs, bar_width, burst_enabled, burst_samples, notify_on_cap, statusline_template, statusline_colors, statusline_ascii, check_updates, dismissed_update. Set with ocuse config set interval=<s> width=<n> burst_on=<bool> burst=<n> notify=<bool> sl=<tpl> sl_colors=<bool> sl_ascii=<bool> update_check=<bool> dismissed_update=<ver>. The statusline template placeholders: {plan} {period} {month} {month_pct} {month_bar} {five_hour} {five_hour_pct} {five_hour_bar} (aliases {5h} {5h_pct} {5h_bar}), {weekly} {weekly_pct} {weekly_bar} (aliases {wk} {wk_pct} {wk_bar}), and {models} (top model by period spend).

Notes

  • Window bars: green <70%, yellow 70–90%, red ≥90%, plus LIMIT EXCEEDED flag.
  • Report tables show cache read and cache write separately; daily is bucketed in UTC unless --tz, and --local honors --tz; token totals in the model/session Tokens column include both cache columns. ocuse's --csv columns are day|hour|model|session,requests,tokens_in,tokens_out,cache_read,cost_usd — its aggregates carry no cache-write, so that column is deliberately absent.
  • Spend-burst sparkline appears in watch mode after 2 refreshes (bars = $ spent between refreshes, ~3 min of history at 5s interval, capped at 40 samples; tall = burst, flat = idle).
  • ocuse's watch frame uses the same three pieces: redraw_frame parks the cursor on the frame's last line (its status/countdown line), the sparkline tracks 5-hour spend deltas at 10s samples (capped at 60), and Ink carries cmduse's palette as empty strings when colour is off.
  • On Monthly caps: monthly pool is the plan total (e.g. $70 on GOAT). Docs describe per-model allowances, but the CLI and API meter one shared pool — verified empirically.

Links