Non-interactive output
Use one-shot mode in scripts and scheduled jobs:
# Human-readable rows
ai-usage-tui --once
# One compact aggregated document (see "Ask an LLM about your usage" above)
ai-usage-tui --summary-json --month
# Every request as JSON to stdout -- large; narrow it first
ai-usage-tui --json --week
# CSV to a file, or to stdout with `-`
ai-usage-tui --csv usage.csv --days 14
ai-usage-tui --csv - --week | head
# Exact filters: provider and model ignore case; project and session are
# spelled exactly as the exports print them
ai-usage-tui --json --all --provider opencode --model gpt-5.6-sol
ai-usage-tui --json --month --project /home/me/work/api
ai-usage-tui --summary-json --session 0198f4c2-7d1e-7a3b-9c11-3e5a6b7c8d90
--json and --csv imply --once, and so does --summary-json. Every JSON document carries
"schema_version": 1, and ai-usage-tui --schema defines every key and every enum value in
them; what the version promises is in docs/stability.md. --json includes
the source description, selected range, usage rows, a limits array of subscription
windows (see Subscription limits; empty when
there are none), an escalations object, and a provenance object; each usage row also carries
billing (per_token or subscription), and project and session_id (null when unknown).
escalations is the routing panel’s derived block — which sessions moved to a
pricier model, and what that cost — for scripts:
"escalations": {
"sessions_examined": 12,
"sessions_escalated": 1,
"escalation_rate": 8.33,
"unclassified_changes": 0,
"transitions": [
{ "from": "claude-sonnet-5", "to": "claude-opus-5",
"from_input_rate": 3.0, "to_input_rate": 5.0, "sessions": 1,
"cost_after": 9.7265, "unpriced_after": 0, "quota_after": 0 }
]
}
provenance answers the question the header’s coverage percentage only gestures
at — not how much of this is priced, but how much of it a provider actually
reported:
"provenance": {
"reported_share": 62.4,
"billable_cost": 41.83,
"quota_requests": 3128,
"unpriced_requests": 0,
"by_cost_status": [
{ "cost_status": "reported", "rows": 812, "requests": 812,
"tokens": 41203991, "cost": 26.10, "api_equivalent_cost": null },
{ "cost_status": "quota", "rows": 3128, "requests": 3128,
"tokens": 669003161, "cost": null, "api_equivalent_cost": 571.93 }
]
}
Every one of the seven cost statuses is present whether or not it has rows, so a
consumer can key on the shape. cost is null — never 0.00 — for quota and
unavailable, because those rows have no per-token price and a zero would
assert they were free. reported_share is a percentage of billable_cost
only: quota-billed work has no dollars to be a share of, and the field is null
rather than 0 when there is no billable spend at all.
ai-usage-tui --doctor prints the same split as a PROVENANCE block.
Both blocks are derived from the same rows the export reports, so a --provider
filter narrows them with it. escalation_rate is null rather than 0 when no session had
enough information to examine, and cost_after is a floor rather than a total
whenever unpriced_after or quota_after is non-zero. These are inferred
from usage and are deliberately kept apart from the recorded routing events
--routing-json exports.
Usage CSV columns are:
provider,model,category,cost_status,requests,input_tokens,output_tokens,
reasoning_tokens,cache_read_tokens,cache_write_tokens,cost,created,project,
session_id,api_equivalent_cost
Configuration
The optional TOML file defaults to:
${XDG_CONFIG_HOME:-~/.config}/ai-usage-tui/config.toml
Use --config PATH to select another file. An explicitly selected file must
exist. Command-line values override config values; for data paths, config
values override environment variables and defaults.
refresh_interval = 30
days = 7
# claude_dir = "/home/user/.claude/projects"
# codex_dir = "/home/user/.codex"
# copilot_dir = "/home/user/.copilot"
# Off by default: this is the only setting that would let the tool reach the network
# outside an explicit --refresh-* or --check-update command. See "Privacy and network
# behavior".
[update]
check = false
[collectors.opencode]
enabled = true
interval = 30
[collectors.claude_code]
enabled = true
interval = 30
billing = "auto" # auto | subscription | api
# config_json = "/home/user/.claude.json"
[collectors.codex]
enabled = true
interval = 30
billing = "auto" # auto | subscription | api
[collectors.copilot]
enabled = true
interval = 30
billing = "auto" # auto resolves to subscription: a seat, not a rate
[collectors.journal]
enabled = true
interval = 60
[omarchy]
# dir = "/home/user/.local/state/omarchy/agents/usage"
limits = true # read Omarchy's agents-panel records
# records = ["opencode"] # what --omarchy-record writes (opencode, ollama)
# balance = false # also draw a budget as the panel's prepaid ledger
# balance_budget = "global/monthly" # which budget, as <scope>/<period>
[[budgets.entry]]
scope = "global"
period = "monthly"
limit = 50.0
warn and critical are percentages of limit; they default to 75 and 90.
An entry that could never fire is refused when the config loads rather than
shown as OK forever: a provider or model scope needs a name, global
takes none, limit must be above zero, and warn must sit below critical.
The complete annotated example — including data paths, filters, collectors, and
budget scopes — is in examples/config.toml, and every
install carries it: ai-usage-tui --print-config prints it. Its budgets are live
samples, so edit or remove them before saving it as your config.
Budget checks
Configured budgets appear in the TUI. To check them non-interactively:
ai-usage-tui --check-budgets
The command prints JSON and exits with status 1 when any budget is at the
warning, critical, or exceeded threshold. It exits with status 0 when all
budgets are below their warning thresholds or no budgets are configured.
Only usage with a reported, calculated, or estimated cost contributes to
spend. Subscription-billed Claude Code usage is quota and does not count, so
a budget scoped to global, provider = "anthropic", or a Claude model counts
none of it toward spend; [collectors.claude_code] billing = "api" restores
the per-token accounting.
Not counted is not the same as not shown. Usage that should carry a price and
does not is left out of spend and reported beside it as unpriced_requests,
and quota-billed usage as quota_requests. The panel renders such a spend as
≥ $2.00 and ≥ 4%, or on quota when the period’s work is all plan quota,
and the burn panel’s projection becomes ≤ 2h 14m left. A floor presented as
a total is how a budget gets trusted past the point it should be.
The exit code and the webhook still act on the floor: a budget that is OK on
its priced spend is not reported, however much of it is unpriced or on quota,
because reporting it would change what alerts means for every script that
reads it. The panel is where the floor is visible.
A daily budget period begins at local midnight — the same boundary the
dashboard’s TODAY range uses, so those two always agree. A monthly budget
period begins on the first day of the current local month, which is
deliberately not the dashboard’s 3 / --month trailing-30-day range, so
a monthly budget’s spend differs from the 30 DAYS total.
When --webhook URL (or webhook in the [budgets] table) is set, actionable
alerts are POSTed as JSON with this shape:
{tool, timestamp, alerts: [{scope, period, level, spend, limit, pct,
unpriced_requests, quota_requests}]}
spend and pct are floors when unpriced_requests is non-zero.
--check-budgets prints the same per-alert object.
A webhook URL is treated as the credential it usually is: no message, log line or --doctor row
repeats it, only its host. Over plain http:// to another machine the alert – scope, limit and
spend – can be read on the way, and --check-budgets, --doctor and the log each say so;
http://localhost is exempt, and nothing is refused, because the usual plain-HTTP target is a
notifier on your own network.
--check-budgets exits 1 when a budget is over and 2 when the check itself failed, so a
scheduled run can tell the two apart. It posts synchronously before exiting 1 and prints
warning: budget webhook dispatch failed: … on stderr if the POST fails. The
dashboard posts from a background thread on every refresh and logs a failed
POST when AI_USAGE_LOG is set. A repeat alert at the same level for the same
scope and period is suppressed for one hour, but that suppression is in-memory
only: a cron-driven --check-budgets re-POSTs on every run while a threshold
is breached, so run it no more often than you want to be notified.
CLI reference
| Option |
Meaning |
-h, --help |
Print help |
-V, --version |
Print the version |
--once |
Collect once, print plain text, and exit |
--json |
Collect once and print usage JSON, one object per request (large: megabytes on a busy machine) |
--summary-json |
Collect once and print a compact aggregated summary as one line of JSON: totals and efficiency metrics by model, project, session and day, with sources, budgets, limits, escalations and routing |
--top N |
With --summary-json, list the N largest models, projects and sessions and fold the rest into other (default 10; 0 lists all) |
--csv PATH |
Collect once and write usage CSV; - writes it to stdout |
--config PATH |
Load a specific TOML config file |
--doctor |
Report where each data source was looked for, what was found there, and how billing was decided, then exit |
--completions SHELL |
Print a shell completion script (bash, zsh, fish, elvish, powershell) and exit |
--man |
Print the man page in roff and exit |
--print-config |
Print the annotated example config.toml and exit (its budgets are samples to edit) |
--schema |
Print a JSON glossary of every key and every enum value in the JSON outputs, with its meaning, and exit |
--agent-guide [TOPIC] |
Print a guide for LLM agents and exit. Bare, or read: how to read the JSON outputs, the rules for null, quota and floors, and what to look for. setup: how to configure the tool on someone’s behalf — config and budgets, the Claude Code hook and status line, timers, and what each command writes or sends. recipes: scripts over the JSON and CSV outputs — a status-bar module, alerts, a digest, a report. extend: covering a tool it does not read, through --record-event, or a change to the source |
--db PATH |
Override the OpenCode database path |
--journal PATH |
Override the local journal path |
--claude-dir PATH |
Override the Claude Code session-log directory |
--claude-billing MODE |
How Claude Code usage is billed: auto (default), subscription, or api; overrides [collectors.claude_code] billing |
--codex-dir PATH |
Override the Codex home ($CODEX_HOME, else ~/.codex); sessions/ and archived_sessions/ are read beneath it |
--codex-billing MODE |
How Codex usage is billed: auto (default), subscription, or api; overrides [collectors.codex] billing |
--copilot-dir PATH |
Override the Copilot home ($COPILOT_HOME, else ~/.copilot); its CLI store and session-state/ logs are read beneath it |
--copilot-billing MODE |
How Copilot usage is billed: auto (default, resolves to subscription), subscription, or api; overrides [collectors.copilot] billing |
--gemini-dir PATH |
Override the Gemini CLI home (default ~/.gemini); its telemetry log is read from beneath it |
--gemini-billing MODE |
How Gemini CLI usage is billed: auto (default), subscription, or api; overrides [collectors.gemini] billing |
--omarchy-dir PATH |
Override where Omarchy’s agents panel keeps its usage records (default $XDG_STATE_HOME/omarchy/agents/usage) |
--omarchy-record |
Write usage and budgets as a record for Omarchy’s agents panel ([omarchy] records, default opencode) and exit |
--today |
Use today (local calendar day) |
--week |
Use the trailing 7 days (default) |
--month |
Use the trailing 30 days |
--days N |
Use the trailing N days; N must be greater than zero |
--all |
Use all available history |
--provider NAME |
Filter by exact provider name, ignoring case |
--model NAME |
Filter by exact model name, ignoring case |
--project PATH |
Filter by project: the working directory exactly as the exports spell it ((unattributed) for usage with none) |
--session ID |
Filter by exact session id |
--refresh-interval N |
Refresh the TUI every N seconds |
--record-ollama |
Read an Ollama response from stdin and journal it |
--record-usage PROVIDER |
Read an OpenAI-compatible response from stdin and journal it under PROVIDER |
--record-event |
Read usage events from stdin, one JSON object per line in this tool’s own terms, and journal them — for a tool with no collector, through an adapter |
--refresh-zen |
Refresh the cached Zen model catalog and exit |
--refresh-pricing |
Refresh the Zen pricing cache and exit |
--check-update |
Ask GitHub for the latest release tag, cache it for the dashboard header, and exit (needs the network; the command is the consent) |
--check-budgets |
Print actionable budget alerts as JSON |
--webhook URL |
POST budget alerts to this URL (overrides budgets.webhook) |
--record-routing |
Read one routing event from stdin and journal it |
--claude-code-hook |
Read a Claude Code PostToolUse/PostToolUseFailure hook payload from stdin and journal a routing event when it observed a test run |
--statusline |
Read Claude Code’s statusline JSON from stdin, print a one-line rate-limit readout for its status bar, and cache the windows for the l panel |
--install-hook |
Register the Claude Code hook (PostToolUse and PostToolUseFailure, for Bash) in Claude Code’s settings.json, appending to what is there, and exit; the command is the consent |
--uninstall-hook |
Remove this tool’s hook from Claude Code’s settings.json, leaving every other entry, and exit |
--install-statusline |
Register ai-usage-tui --statusline as Claude Code’s status line in its settings.json, unless another program’s is there, and exit |
--uninstall-statusline |
Remove this tool’s status line from Claude Code’s settings.json, if it is this tool’s, and exit |
--uninstall |
Remove the hook, the status line and this tool’s caches; print how to delete the journal and config, which are kept; and exit |
--prune-journal DAYS |
Delete journal rows older than DAYS (31 or more), reclaim the space, report what was deleted and what was kept, and exit; nothing else ever deletes from the journal |
--routing-json |
Print aggregated routing analytics as JSON — all history unless a range flag is given |
--routing-csv PATH |
Write aggregated routing analytics as CSV |
Recording, refresh, budget, usage export, and routing export modes are
single-purpose actions; do not combine action flags.
Environment variables:
| Variable |
Meaning |
OPENCODE_DB_PATH |
OpenCode SQLite database path |
AI_USAGE_JOURNAL_PATH |
Usage and routing journal path |
CLAUDE_PROJECTS_DIR |
Claude Code session-log directory |
CLAUDE_CONFIG_DIR |
Claude Code config directory; logs are read from $CLAUDE_CONFIG_DIR/projects (CLAUDE_PROJECTS_DIR wins when both are set) |
CODEX_HOME |
Codex home; session logs are read from sessions/ and archived_sessions/ beneath it |
COPILOT_HOME |
Copilot home; its CLI store and session-state/ logs are read beneath it |
GEMINI_TELEMETRY_OUTFILE |
Gemini CLI’s own telemetry output path; when set, it is read from there rather than ~/.gemini/telemetry.json |
AI_USAGE_LOG |
Write diagnostics to a file — 1 for the default location, or a path. Off when unset. Past 5 MiB the file is renamed to <name>.old, replacing the previous one, and started again. |
NO_COLOR |
Any non-empty value draws the dashboard without colour (no-color.org); the selected row is shown in reverse video instead |
COLORTERM |
truecolor or 24bit draws the dashboard’s palette as designed. Without it the colours are mapped down to what TERM names; ssh and sudo often drop this variable, so set it again there if the terminal does support 24-bit colour |
TERM |
Read for colour depth when COLORTERM says nothing: a name ending -direct is 24-bit, one containing 256color gets the nearest of 256 colours, anything else gets the terminal’s own sixteen and its own background. --doctor prints which was chosen and why |
WT_SESSION |
Set by Windows Terminal, which draws 24-bit colour without declaring it in COLORTERM |
XDG_CONFIG_HOME |
Base directory for the default config path |
XDG_DATA_HOME |
Base directory for default database, journal, and cache paths |
XDG_STATE_HOME |
Base directory for Omarchy’s agents-panel records (omarchy/agents/usage beneath it) |
On Windows, USERPROFILE (or HOMEDRIVE + HOMEPATH) stands in for HOME,
LOCALAPPDATA for XDG_DATA_HOME, and APPDATA for XDG_CONFIG_HOME.