Module 01·Developer Tools

ccmon · A live dashboard for Claude Code and Codex

ReleasedAdvancedUpdated 2026-09-06

Claude Code and Codex CLI both write every token they spend to disk. ccmon watches both, in one dashboard, with each tool's own accounts, plans and rate limits. No API key. No telemetry.

Coding agents keep a diary. Every response Claude Code gives you lands in a transcript under ~/.claude; every turn Codex CLI takes lands in a rollout under ~/.codex. Both record exact token counts. ccmon reads both diaries and turns them into numbers you can act on. What today cost. How hot the five-hour window is running. Which model is eating the budget. When your limits reset.

Two tools, one dashboard, side by side — each with its own accounts, plans and rate limits.

No API key. No setup. No telemetry. ccmon finds your data on its own, indexes weeks of history in a few seconds, then follows along. New responses appear within a second.

A scripted tour across a synthetic multi-account dataset, recorded with npm run promo.


Start

Clone and install

git clone https://github.com/iskandarputra/ccmon && cd ccmon && npm install. One runtime dependency. Everything else bundles at build.

Run it

npm run dev. ccmon discovers your Claude and Codex data roots, indexes the history, and goes live.

Walk the views

Eleven views on keys 1 through 9, 0 and -. Ctrl+K opens a command palette that jumps anywhere and runs actions.

Discovery needs no configuration. Claude Code is read from ~/.claude/projects/, any sibling ~/.claude-<name> root from a multi-account setup, and $CLAUDE_CONFIG_DIR. Codex is read from ${CODEX_HOME:-~/.codex}/sessions/ and archived_sessions/.

./build.sh packages a Debian .deb and a portable AppImage on Linux, an NSIS installer and portable exe on Windows, and a .dmg on macOS. Tagged pushes cut a GitHub release with all three platforms attached.


Pulse

The page you leave open. Today's spend, the current five-hour block, live plan limits for every account, and a feed that ticks as you work.

ccmon Pulse view with today's totals, the active 5-hour block, live plan limits and an activity feed
Pulse. Today, the block, the limits, and the feed.

Subscription limits run in five-hour billing windows, and ccmon treats the active one as a first-class object: burn rate, countdown, and a projection of where the block lands by its end. Behind it sit thirty days of block history — idle gaps included — and a utilization histogram that shows whether you fill your windows or graze them.

Your limits, live — and read two different ways

For Claude Code, ccmon reads the same numbers the /usage screen reads, using the login Claude Code already stored, refreshed every sixty seconds. Then it goes one step further: it learns your pace and forecasts the cap. "Caps around Friday 1 AM at this pace" is more useful than a percentage.

For Codex, there is no endpoint to poll — and none is needed. Codex writes its own rate limits into the session log on every turn, so ccmon reads them with no network call and no credentials at all.


Analytics

Economics, ROI and forecasting on one page — plus the daily model stack and cumulative burn.

ccmon Analytics view with month-end projection, plan value, cache economics and what-if re-pricing
Analytics. The month, projected, benchmarked and re-priced.
  • Plan value — what this month would have cost on the API, next to what the subscription costs. The multiple tends to be persuasive.
  • Cache economics — what you paid, what the same traffic would have cost uncached, and the hit rate that separates the two. Including the cost of walking away: leave a session longer than the cache TTL and your next message re-writes context you already paid for. ccmon counts exactly what that habit costs.
  • What-if — every request re-priced onto another model, entry by entry. An all-Sonnet month and an all-Haiku month, next to what you actually spent.
  • The month, projected — with an honest uncertainty band, plus weekday rhythm, week-over-week, spike days, streaks and records.

Projects

Spend per repo in four layouts — split, grid, table, and a force-directed knowledge graph. Every project gets a sparkline, today, seven days, all time, cache hit rate and last active. Subagent spend is tracked separately, so a workflow that fans out twenty agents shows up as exactly that, not as one mysterious session.

ccmon Projects view with per-project spend cards, sparklines and a force-directed knowledge graph
Projects. Forty repos, four ways to rank them.

Accounts

Every login, plan and rate limit side by side, across both CLIs — and how many sessions each one has running right now.

ccmon Accounts view with Claude and Codex accounts, plans, rate limits and live session counts
Accounts. Both tools, every login, one screen.

When one account is about to cap while another sits idle, ccmon says so and hands you the exact claude-cross-resume command to pick up where you left off on the account that still has room. Codex gets the same treatment via codex-cross-resume, and a setup wizard writes the per-shell claude-* and codex-* wrappers for you.

▶Live session counts come from each tool's registry, never from file mtimes

Claude Code keeps <root>/sessions/<pid>.json with a status it maintains; Codex keeps one lock file per running thread and removes it on exit. Liveness is a process.kill(pid, 0) probe everywhere, plus a /proc/<pid> start-time comparison on Linux — that second check is what rules out a reused pid, which would otherwise report a long-dead session as running.

Claude's count is exact. Codex's lock file is empty, with no pid to probe, so its count stays an upper bound — with one refinement that matters: if no Codex process is running at all, every lock is provably stale and the count is zero. Without that, a session that crashes without cleaning up is reported as running forever. Observed at five days.


The 3D canvas

Nine ways to slice your usage. Seven ways to draw each one. Terrain, ridges, surfaces, scatter, contours, a cumulative spend trail that snakes through the calendar. It is mostly for joy, and that's fine.

ccmon 3D canvas rendering usage as terrain, one column per day
3D canvas. Nine modes, seven plots, one orbit.

The AI advisor

Ask questions about your own usage in plain language. It sends aggregates only — never transcripts — reusing the login already on disk, and Ctrl+J opens it as a drawer from any page.

ccmon AI advisor answering a question about usage patterns
Advisor. Your numbers, in sentences.

Make it yours

Seventeen themes, dracula by default, from lofi warm charcoal to daylight. Every theme is the same set of CSS tokens, so adding one is adding an object. Costs display in any of 160+ currencies, the top ten crypto included, with rates refreshed hourly. Ctrl+P blanks every dollar figure on screen — for screen-sharing, and it masks in the tray and the CLI statusline too.

ccmon Settings view with the seventeen-theme grid, cost mode, pricing, currency and data root controls
Settings. Seventeen themes, three cost modes, any currency.

Power users get a config file at ~/.config/ccmon/config.json:

json
{
  "claudeDirs": ["/extra/claude/root"],
  "modelAliases": { "arn:aws:bedrock:.*:profile/abc": "opus (bedrock)" },
  "pricing": {
    "my-custom-model": { "in": 5, "out": 25, "w5m": 6.25, "w1h": 10, "read": 0.5 }
  }
}

claudeDirs adds data roots beyond the defaults. pricing takes per-MTok overrides — case-insensitive regexes that always win, reaching tier rates and context limits, not just the five base rates. modelAliases and projectAliases rename things for display only, never before pricing or grouping.


The command line

Every build ships a headless CLI that runs the whole pipeline under plain Node — same data, same settings, no Electron, no window.

bash
ccmon json --range 30d --section totals,models --pretty
ccmon json | jq '.totals.cost'
ccmon csv days --since 20260101 > days.csv
ccmon statusline

statusline is designed to answer inside a shell prompt, which is a different problem from filling a dashboard: it windows discovery by file mtime and returns in about 0.9 s where a full scan takes 7 s. json and csv never window — a partial index that silently understates lifetime totals is worse than a slow one — and neither is ever masked by privacy mode, because blanking numbers in machine output corrupts it rather than protecting it.


Both tools, priced properly

Counting Claude tokens correctly is a solved problem, and ccmon proves it against ccusage. Codex was the interesting one.

▶Codex tokens are cumulative, and its input count includes the cache

Codex reports a session running total plus a per-turn delta. Take the delta where it exists, else subtract the previous total. Then subtract cached tokens from the input, because Codex folds the cache into input_tokens — billing the raw figure double-charges every cached prompt token, which on a long session is most of the input.

▶A forked rollout replays its parent's turns, rewritten to the fork instant

Same token counts, different timestamps — so a timestamp-keyed dedupe never collides and the parent's entire history gets billed twice. ccmon matches on token values instead, which a timestamp rewrite cannot defeat, anchored either to the parent's own stream or to the burst Codex writes at the fork instant. Both are gated on the session metadata actually declaring a parent, so an ordinary session where you simply took two quick turns in a row can never trip it.

▶The long-context tier is per-model, not a constant

Anthropic's tier starts at 200K. The gpt-5.x models start at 272K. A single global threshold billed a 250K Codex turn at double rate, so the threshold now travels per model, carried through from the catalog. Relatedly, OpenAI prices come from models.dev only — the other catalog doesn't publish those bands, and consulting it first would silently resolve to base rates and drop the tier entirely.

Fitting Codex is also what proved the architecture. A Codex usage line is not self-describing: its model comes from an earlier context event and its speed tier from an earlier settings event, so a line read in isolation cannot be priced at all. No amount of designing against one format would have surfaced that.


Can you trust the numbers?

Yes, and you can check.

npm run parity cross-checks ccmon's token math against ccusage, the established CLI for coding-agent usage reports. Current drift: 0.000 percent — an exact integer match on all four token fields, over roughly 110k entries. A second mode runs the same check against a committed fixture corpus, so CI gates on it with no local history needed.

867 unit tests pin the parsing, pricing, block, forecast, account and shell-integration logic, and CI runs them on Linux, macOS and Windows on every push. That three-OS matrix isn't ceremony: the keychain reader shells out to macOS security(1) and the account setup emits PowerShell, and both used to be exercised only against mocks on Linux.

Where a number is a heuristic, the panel says so — every analysis carries a small "why?" button explaining exactly how it is computed and where it stops being reliable.

Costs are API list prices. On a Pro or Max subscription, read them as API-equivalent value rather than an invoice.


Private by construction

Everything stays on your machine, with six deliberate exceptions you can see and control in settings. All are read-only, all keep the last good value on failure, and none of them send your transcripts anywhere.

WhatWhen
Model prices (LiteLLM, models.dev)daily, cached, optional
Claude plan limitsevery 60 s, using the login Claude Code already stored
Currency rates (open.er-api.com, CoinGecko)hourly
Re-loginonly when you click "Log in"
AI advisoronly when you ask it something — aggregates only
DeepSeek balanceevery 5 min, only if you connect a key

Codex adds none of them. Its rate limits, plan and identity are all read from files it already wrote to your disk — no OpenAI request is ever made.

Flip pricing to offline and ccmon runs entirely from bundled snapshots. Nothing is written anywhere except your own disk. No telemetry, no analytics, no crash reporting.


Under the hood

ccmon is two processes with one contract between them. A pure Node service layer inside the Electron main process turns log files into an immutable snapshot. A sandboxed React renderer draws whatever snapshot arrives. The renderer holds no business logic — which is also why the same service layer runs headless as a CLI.

Two formats in, one immutable snapshot out, two front ends.

The decisions that shaped it:

  • Services never import Electron. That single rule makes the whole pipeline runnable headless and unit-testable against real data — and it is what let the CLI exist at all. Twenty-six services, zero GUI required. A service needing an Electron API takes it injected rather than importing it.
  • An adapter owns what is format-specific; the watcher owns what isn't. Tailing, offsets, per-file state and dedupe live in the watcher. detectRoots, owns and parseLine live in the adapter. Supporting a new coding CLI is appending to a registry.
  • Tail, don't rescan. Per-file byte offsets plus a partial-line remainder make every read O(appended bytes). A byte-level prefilter skips the JSON parse for lines that cannot carry data, which is 41% of the parse path.
  • Best-wins dedupe. Streaming chunks repeat a key with cumulative usage; the largest wins. Subagent usage mirrored into parent transcripts keeps one copy. This is the rule that buys ccusage parity.
  • Full recompute over incremental aggregation. Every derived number stays consistent by construction, with per-entry dollars resolved exactly once. ~610 ms at 110k entries, on a debounce.
  • Entries cost at the rates of their day. A dated pricing archive records every catalog change, so old months keep their old prices instead of silently re-pricing.
  • One day-bucketing function. Every date key, "today", range and heatmap goes through it, driven by a timezone setting. Changing the zone re-stamps history in one pass without a rescan.
  • Locked-down renderer. Context isolation, sandbox on, no Node integration, strict CSP injected at build time. The renderer can only call what the preload exposes.
▶An account is a home, not a source directory

A Codex home contributes two source directories — sessions and archived_sessions — but it is one account. Anything rendering or scoping one row per account has to iterate accounts, not directories; otherwise you get a duplicate card, or a scope that takes the live sessions without the archived ones. Small distinction, and the source of a whole family of bugs before it was named.


By the numbers

~34Klines of strict TS
Source
2Claude Code · Codex
Coding CLIs
11on keys 1–9, 0, -
Views
26pure Node
Services
867on 3 OSes
Unit tests
0.000%vs ccusage
Parity drift
17
Themes
1chokidar
Runtime npm deps
PlatformsLinux (.deb, AppImage) · Windows (NSIS, portable) · macOS (.dmg)
SourcesClaude Code transcripts · Codex CLI rollouts
Pipelinediscover → tail → parse → dedupe → price → aggregate → publish
Pricing sourcesLiteLLM, models.dev, bundled snapshots, dated archive, regex overrides
Front endsElectron app · headless ccmon CLI · tray · shell statusline
Renderer securitysandboxed, context-isolated, no Node, strict CSP
Iconrendered programmatically at build — no binary assets in the repo
LicenseMIT

Get it

→ →