Independent AI intelligence Two editions daily · ET
Fervor AI

Analysis · August 30, 2026 · repo

tokentabClaude CodeCodexGemini CLIagent-infrastructureclaude-codecodexlocal-ai

tokentab Prices Your Coding Agents Offline, From a Table Someone Maintains by Hand

A local-only CLI turns the session logs Claude Code, Codex, and Gemini CLI already leave on your disk into a monthly statement. The tokens in that statement are measured. The dollars are estimated. Knowing which is which changes what you can decide from it.

Every coding agent you run writes a detailed record of itself to your home directory and then never mentions it again. Claude Code drops JSONL into ~/.claude/projects/. Codex writes rollout files into ~/.codex/sessions/. Gemini CLI leaves session JSON in ~/.gemini/tmp/. Those files contain per-call token counts, which means the raw material for an exact usage picture has been sitting on your machine the entire time you have been guessing at your spend.

tokentab reads them and prints the statement. Local only, no account, no API key, nothing leaves the machine. MIT licensed. The only third-party dependency is rich, for terminal tables. The web dashboard runs on Python's standard-library HTTP server, binds to localhost, and does not even pull fonts from a CDN, so it works with the network cable out.

That is a genuinely good design, and I want to spend most of this piece on the part the README tells you plainly and most people will skim past: two of the three numbers on that statement are not measurements.

Where the numbers come from

The tokens are real. Every one of these tools records its own token counts per call, so tokentab is adding up vendor-reported figures rather than inferring anything. There is one piece of actual arithmetic, and it is the right piece: Claude reports cache reads and cache writes separately while Gemini reports input including the cached portion, so tokentab subtracts the cached tokens back out before pricing to avoid charging you twice for the same context. Somebody thought about that carefully.

The dollars are a table. Prices live in tokentab/pricing/prices.py as dollars per million tokens, hand-maintained, and the tool never reaches the network to price anything. The README defends this choice directly, and I agree with the defense: a slightly stale number beats a crash when a vendor renames a model overnight. Matching is fuzzy, so claude-opus-4-6-20260514 still finds claude-opus-4-6. When a model name misses the table entirely, the row shows $0.00 and the CLI says so out loud rather than counting it as free.

Sit with that for a second. The failure mode of a hand-kept price table is not a wrong total. It is a total that is too low by exactly the cost of whatever model you use most, with no warning attached, if that model's string is new enough to have missed the table.

The activity labels are a guess, and the README calls them a guess. Coding, debugging, refactor, testing, and so on get assigned from which tools ran and the wording of your first message in the session. It is deterministic, no model calls, so it is fast and you can eyeball whether it is roughly right. "Treat it as a hint, not gospel," says the README, and that is the correct instruction. Any session you opened with "continue" is going to be filed somewhere arbitrary.

The thing worth arguing about

Cost tooling for agents keeps getting built as a dashboard, and a dashboard implies reconciliation. You look at a number, you believe the number, you make a decision.

tokentab is not reconciliation. It is a reconstruction assembled from logs, and it is more honest about that than most tools in this category. But honesty in a README does not survive contact with a chart. Once those totals render as a monthly statement with a big figure at the top, your brain files them under "bill," and the caveats stop applying.

This matters most if you are on a subscription. A Max plan or a Copilot seat does not charge per million tokens at all, so the dollar figure tokentab shows you is a shadow price for work you already paid a flat fee to do. That is still useful. It tells you what the same work would cost on the API, which is exactly the number you want before you move a workflow into CI. It is not, in any sense, what you spent.

The place where the estimate becomes actionable is comparison, not absolutes. A model dominating your cost on lots of tiny calls means the same thing whether the price table is current or six weeks old. A cache hit rate sitting under roughly 80% for weeks means your context is not stable between calls, and that diagnosis does not depend on a dollar figure at all. The README's "Reading the output" section is the best part of the project, and none of it requires the prices to be right.

The provenance question you should settle first

Here is the detail that stopped me, and I would rather hand it to you than pretend it is not there.

The README's install instructions clone github.com/damejan80/tokentab. The "From source" block, six sections later in the same file, clones github.com/wzchav/tokentab. Two different accounts, one document.

Both repositories exist. Both carry a byte-identical one-line description. Both have exactly six commits. GitHub records neither as a fork of the other, and lists each as its own network root.

The two READMEs are the same document with one systematic difference. In wzchav's version every invocation is the installed command: tokentab today, tokentab month, tokentab --from 2026-06-01, tokentab web. In damejan80's version every one of those has been rewritten to python cli.py, and damejan80's repository has a top-level cli.py that wzchav's does not.

That rewrite is where the errors came from. tokentab --from became python cli.pyb --from, which is a botched substitution rather than a stray keystroke. The line promising that pip install . puts a tokentab command on your path survived the rewrite while every example under it stopped using that command. And the From-source block is the one place the find-and-replace missed, which is why it still names the other owner.

wzchav's repository also carries the lower GitHub repository ID, which is assigned monotonically, so it was created first. damejan80's was created on August 27, 2026, two days before it started trending.

None of that is proof of anything improper, and I want to be careful not to imply it is. Plenty of people restructure someone else's MIT-licensed tool, and MIT permits exactly this. What it does mean is that the file you are being asked to trust is a modified copy of an older one, the modification introduced documentation errors, and the version you install depends on which of two clone URLs in a single README your eye landed on.

For most repositories that would be a footnote. For this one it is the first question, because of what the tool reads. Claude Code session JSONL is not a metrics file. It contains your prompts, the file contents the agent read, and the output of every tool it ran. Pointing a parser at that directory means pointing it at your source code and your conversations.

Nothing in this README suggests bad intent, and the design choices point the other way: localhost binding, no network calls for pricing, no CDN fonts, standard library only. Those are the choices of someone who thought about exactly this concern. The codebase is also small enough that you can verify the claim yourself in an afternoon, which is the actual answer.

Put this into practice

Read it before you run it. Six commits, one cli.py, one tokentab/ package, one dependency. Open tokentab/providers/ and confirm the parsers only read. Confirm the only socket that opens is the localhost dashboard. This is a twenty-minute review, and the fact that a twenty-minute review is sufficient is the strongest thing about the project.

Pick your clone URL deliberately. The two repositories are not identical. One ships a top-level cli.py and invokes everything through it; the other installs a tokentab entry point and has no cli.py. Choose, rather than pasting whichever block your eye landed on.

Start with --json, not the dashboard. python cli.py --json | jq . gives you the actual product. The tables and charts are presentation over that structure, and looking at the structure first keeps you from trusting the presentation more than it has earned.

Hunt for $0.00 rows immediately. That is the tool telling you a model name missed the price table, and it is the single most likely reason your total is wrong in the direction that feels good.

Add your own price rows for anything behind a gateway. If you run models through Bedrock, Vertex, or a custom base URL, those model strings will not match a hand-kept table built around vendor-native names. Adding a line is a one-line PR, and the maintainer explicitly asks for them.

Reconcile once against a real invoice. Run python cli.py -month for a month you have an actual bill for. If they land within a few percent, keep the tool and trust it for trend work. If they do not, you have just located a stale price row, and finding it is more valuable than the dashboard.

Use it for shape, not for accounting. Which projects, which models, which days, what the cache is doing. Those answers are load-bearing. The grand total is a strong estimate wearing a bill's clothing.

Honest limitations

It estimates rather than reconciles, and that is by design rather than by oversight. Read the previous section as the workaround, not as a complaint.

There is nothing to pin. Six commits, zero releases, zero tags. pip install . from main gets you whatever main happened to be that morning, which is a real problem for anyone who wants reproducible tooling in a team.

The top level of the repository is assets/, tokentab/, LICENSE, README.md, cli.py, and pyproject.toml. No tests directory. No SECURITY.md. No visible CI. For a tool that reads your session transcripts, a security contact would be a reasonable thing to want.

The Cursor row in the table is a stub, wired up but unfinished, and the repository's own GitHub description lists only Claude Code, Codex, and Gemini CLI while the README body adds Cursor. If Cursor is where your money actually goes, your cross-tool total is missing a leg.

The documentation has small errors that undercut a tool whose entire pitch is care with numbers. One example line reads python cli.pyb --from 2026-06-01, with a typo in the filename. The install section says pip install . puts a tokentab command on your path, and then every example under it calls python cli.py instead, except the From-source block, which is also the one that names a different owner.

I read the star and fork counts from a GitHub HTML page, and those pages have been serving stale values lately, so I have left the numbers out. What is verifiable from the repository page is the commit count and the absence of releases, and both of those tell you more about maturity than a star badge does.

What to do with this

Spend on coding agents has become the line item nobody can explain, and the data required to explain it has been on your disk this whole time. That alone makes this worth an afternoon.

Install it, run one month, compare against one invoice. You will learn two things: roughly what your agents cost, and roughly how far off the reconstruction is. The second number is the one that determines what you are allowed to decide with the first.

And if the price table turns out to be missing your model, fix it upstream. That is the entire maintenance burden of this project, and it is distributed across everyone who runs it.


Sources: the tokentab README, read from a cache-busted raw fetch on August 30, 2026, the repository page for commit count, license, fork status, and file listing, and the wzchav/tokentab repository for the comparison of the two READMEs and their creation order.