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.