tokenchit
<img src="./assets/logo-lockup.png" alt="tokenchit" width="330">
[](https://opensource.org/licenses/MIT)
[](https://www.npmjs.com/package/@tokenchit/cli)




**Turn your local AI coding agent logs into a stat card you commit to your README.**
[](https://tokenchit.app/u/iyashjayesh)
That card is this repository's own, generated by the command below and committed as a file.
It is not a screenshot and not a hosted image — which is the whole point.
```bash
npx -y @tokenchit/cli@latest generate
```
One command: it finds your agents, shows your stats, writes the card, and puts you on the
board. Running it often? `npm i -g @tokenchit/cli`, then `tokenchit generate`.
```
┃ [1/3] DETECT AGENTS
┃ ● claude-code ~/.claude*/projects/**/*.jsonl
┃ ✓ 3 agents, written to .tokenchit.json
────────────────────────────────────────────────────────────
┃ [2/3] YOUR STATS
┃ ~16.7B $7,392 27d 51
┃ TOKENS EQUIV. COST STREAK ACTIVE DAYS
┃
┃ 30d ▁··▄▆▆▆▅▁▃▅█▃▂▄▂▁▅█▆▂█▇█▅▄█▇▆▃ 3.07B in 7d
┃ ✓ wrote tokenchit.svg
────────────────────────────────────────────────────────────
┃ [3/3] THE BOARD
┃ ✓ published as @iyashjayesh — tier: verified
```
## Why this one
- **The card is a file, not a URL.** Comparable tools serve cards from a hosted endpoint, so
your README depends on someone else's uptime and rate limits. A committed SVG has none of
that — GitHub serves it directly, and it keeps working if this site goes away.
- **Nothing leaves your machine unless you ask.** `sync` and `recap` make no network request.
`publish` is the only command that uploads, and `--dry-run` prints the exact bytes.
- **Honest about what it cannot see.** Logs get rotated, prices change, and some models have
no public price. Where a number is incomplete, the tool says so.
```bash
npx -y @tokenchit/cli@latest recap # the year in review, a second committable SVG
```
[](https://tokenchit.app/u/iyashjayesh)
## What it reads
| agent | source |
| --- | --- |
| **Claude Code** | `~/.claude*/projects/**/*.jsonl` — every profile directory, not just the default |
| **Codex** | `~/.codex/sessions/**/rollout-*.jsonl` |
| **OpenCode** | `~/.local/share/opencode/opencode.db` |
Copilot CLI and Gemini CLI are detected and reported as unsupported: Copilot records only a
live context gauge, and Gemini's transcripts carry no token counts.
**Your numbers will not match Claude Code's Stats panel.** It counts an API call once per
streaming rewrite, so it reads roughly twice as high. `sync` prints both figures and the gap.
**Equivalent cost is not what you paid** — it is what these tokens would cost at list API
rates. Most agent usage runs under a subscription. See [`docs/internals.md`](./docs/internals.md).
## Commands
```
tokenchit generate detect agents, write the card, join the board
--no-publish stop after the card
tokenchit init detect agents, write .tokenchit.json
tokenchit sync read your logs, show your stats, write the card
tokenchit publish put your row on the public board
tokenchit recap year in review, as a second committable SVG
tokenchit ledger show the local history bank, or rebuild it
tokenchit schedule print a cron or launchd entry; installs nothing
tokenchit login prove your GitHub handle (device flow, no password)
tokenchit logout forget this machine
tokenchit whoami who this machine is signed in as
```
`tokenchit help <command>` explains one command. `NO_COLOR=1` drops colour and animation.
Common flags: `--out`, `--theme auto|light|dark`, `--layout default|compact`, `--json`,
`--dry-run`.
## Keeping the card fresh
The card is a file, which is the point — and a file does not update itself. Re-running
`generate` is the honest answer, but nobody remembers to.
There is an action in this repository for the half a runner can actually do:
```yaml
# .github/workflows/card.yml
name: card
on:
schedule: [{ cron: "0 6 * * *" }]
workflow_dispatch:
jobs:
refresh:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: iyashjayesh/tokenchit@v1
with:
handle: your-handle
```
**It does not read your logs, and it cannot.** An Actions runner has no access to
`~/.claude` or `~/.codex`, so nothing on a runner can regenerate a card from source — you
still run `publish` from the machine that has the logs. What the action does is re-fetch the
card you already published and commit it, so the SVG in your README stops drifting away from
your real numbers while readers keep loading a committed file rather than an endpoint.
It refuses to overwrite a good card with a bad response: a non-SVG body, a non-200, or the
placeholder card the endpoint returns for a handle with nothing on the board. That last one
matters — without it a typo in `handle` commits an empty card on a schedule, silently,
forever.
Inputs are `handle` (required), `output`, `layout`, `theme`, `agents`, `hide`, `commit` and
`commit-message`; `layout`, `theme`, `agents` and `hide` are the same options the embed
endpoint takes. It outputs `changed` so you can gate later steps on a real update.
One thing to know: GitHub disables scheduled workflows in a repository after 60 days with no
activity, and a run that finds an unchanged card makes no commit. On a quiet repository the
schedule can switch itself off. `workflow_dispatch` is there so you can start it again, and
the run summary says which happened.
## From an agent, not a terminal
There is an MCP server in `packages/mcp`, so a model can answer questions about your usage
instead of you reading a table:
```json
{
"mcpServers": {
"tokenchit": { "command": "npx", "args": ["-y", "@tokenchit/mcp@latest"] }
}
}
```
Four tools — `get_usage`, `get_daily_usage`, `get_recap` and `detect_agents`. All of them
read the same logs the CLI reads, and **none of them can make a network request**: the
`net.isolated` test covers `packages/mcp/src` alongside the CLI, and unlike the CLI this
package has no allowlisted module, so every file under it must be clean. Adding a `fetch`
anywhere in it fails the suite.
Two things it does that a plain data dump would not. Every figure ships with its caveat as a
sibling field, because a model handed `equivCostUsd` on its own will report it to you as
money you spent — and it is not. And a tool that fails answers with `isError` rather than a
transport error, so "no logs on this machine" reaches the model as something it can relay
instead of something it has to guess at.
It reads the ledger and never writes it. `sync` banks what it saw because you asked it to; a
tool call is a question, and a question that mutates state on disk is a surprise you cannot
see or undo.
## Privacy
`sync` and `recap` make no network request at all.
`publish` is the only command that uploads anything. It sends daily token totals per agent,
model names, and your handle — never prompts, replies, file paths, branch names, or repository
names. `--dry-run` prints the exact bytes so you can check rather than take our word.
Five tests in `packages/cli/test/privacy.test.js` enforce this on every push, including one
that fails if any file outside `net.ts` can open a socket — across the CLI, the core engine
and the MCP server.
## The board
Opt-in, and only `publish` puts you there. Verified rows rank above unverified ones: signing
in is the only thing that ties a row to a GitHub account. An unverified row still appears.
Submissions are self-reported, so two bands guard them — **rejected** for the arithmetically
impossible, and **held for review** for the possible but far outside anything seen. A held row
is stored and returned to you, and kept off the board until a person looks. Both thresholds
have been raised after real users were refused; the reasoning is in
[`docs/internals.md`](./docs/internals.md).
## Development
```bash
npm install
npm run build # core, then cli, then site
npm test # core, cli and site suites
npm run dev # the site at http://localhost:3000
```
Node 22 or newer — OpenCode support uses the built-in `node:sqlite`. The site and the CLI
render through the same `buildCardSvg()`, so they cannot drift.
## Licence
MIT.
This server cannot be deployed
TDQS
Scored across 4 tools
get_usage (windowed totals), get_daily_usage (per-day series), and get_recap (year-in-review aggregates) have partially overlapping subject matter, but each description clearly states its scope and output shape. detect_agents is cleanly distinct. Minor risk that an agent asks for a time range and picks get_usage over get_daily_usage.
Three tools follow a clean get_<noun> snake_case pattern (get_usage, get_daily_usage, get_recap). detect_agents breaks the prefix convention slightly but is still snake_case and readable, so the set is nearly uniform.
Four focused tools cover the analytics surface without redundancy. It is on the lean side but each tool earns its place; nothing feels padded or missing at the count level.
Covers lifetime/window totals, daily series, yearly recap, and agent discovery/diagnostics, which addresses the core questions of a usage-tracking server. Possible gaps like per-project rollups or cross-agent comparison views are not indicated as supported, but no obvious dead end for the stated purpose.