SLOPSHOPPER

token-meter

Shows the tokens each Claude turn uses beside the spinner, and adds a /tokens command for the session total

newspinnercommand
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · token-meter
› fix the failing auth test and add an audit log call ● token-meter: 0 requests · in 0 · out 0 · cache read 0 · cache write 0 ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /tokens ✻ Thinking · tokens: 0…… ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

token-meter

A small Claude Code mod that shows where your tokens go, live.

  • While Claude works, the spinner shows the tokens the current answer has used so far, as in Thinking · tokens: 6,000….
  • Type /tokens for the totals since the mod loaded: requests, input, output, cache read and cache write.

Checking is free. The mod never calls a model. /tokens runs with no Claude turn and prints a log line that Claude doesn't read.

Made for the CorporateChittyChat Short "Do Claude Code Mods Eat Your Tokens?".

Before you run it

A mod runs with your own permissions. Read hooks/register.js first. It's 66 lines. Then check what it does without running it:

claude plugin validate ./token-meter

The calls: line should list only $.command.register, $.ui.invalidate and $.ui.log. That means no file access, no network and no model calls.

Run it

Mods need Claude Code v2.1.287 or later. Check your version with claude --version.

claude --plugin-dir ./token-meter

Ask Claude anything and watch the spinner. When it's done, type /tokens.

Test it

The tests need no session, sign-in or network:

cd token-meter
claude plugin test

What the numbers mean

The counts come from the token usage the Claude API reports for each request. They include subagents' requests, and they reset when the mod reloads. These are not billing figures. For your actual usage, check your plan or the Claude Console.

Tested with Claude Code 2.1.296. Mods are new, and their events can change between releases.

Docs: https://code.claude.com/docs/en/plugins/mods/overview

Source 1 files
hooks/register.js 67 lines
1// token-meter: watch where your tokens go, live.
2// The mod itself never calls a model, and /tokens prints with $.ui.log,
3// a transcript line that Claude doesn't read, so checking costs no tokens.
4
5// Totals since the mod loaded, shared by the hooks below
6let requests = 0
7let input = 0
8let output = 0
9let cacheRead = 0
10let cacheWrite = 0
11// Tokens used by the turn that's running now
12let thisTurn = 0
13
14function fmt(n) {
15  return n.toLocaleString('en-US')
16}
17
18// Claude Code calls this once when the mod loads
19export function register(on) {
20  // Runs when the session starts: add the /tokens command
21  on('session.start', async ($, e, next) => {
22    await $.command.register({
23      name: 'tokens',
24      description: 'Show the tokens used since token-meter loaded',
25    })
26    return next(e)
27  })
28
29  // A new turn starts: reset the per-turn count
30  on('turn.start', async ($, e, next) => {
31    thisTurn = 0
32    $.ui.invalidate('ui.render')
33    return next(e)
34  })
35
36  // Each request to the model: let it stream, then read its token usage
37  on('turn.step', async function* ($, e, next) {
38    const result = yield* next(e)
39    const u = result && result.usage
40    if (u) {
41      requests += 1
42      input += u.input_tokens || 0
43      output += u.output_tokens || 0
44      cacheRead += u.cache_read_input_tokens || 0
45      cacheWrite += u.cache_creation_input_tokens || 0
46      thisTurn += (u.input_tokens || 0) + (u.output_tokens || 0) +
47        (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0)
48      $.ui.invalidate('ui.render')
49    }
50    return result
51  })
52
53  // Each time the spinner is drawn: add this turn's count after its word
54  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
55    return next({ ...e, props: { ...e.props, suffix: ' · tokens: ' + fmt(thisTurn) + '…' } })
56  })
57
58  // /tokens: print the totals as a log line Claude doesn't read, and return nothing
59  on('command.run', { command: 'tokens' }, async ($) => {
60    $.ui.log(
61      fmt(requests) + ' requests · in ' + fmt(input) + ' · out ' + fmt(output) +
62      ' · cache read ' + fmt(cacheRead) + ' · cache write ' + fmt(cacheWrite)
63    )
64    return {}
65  })
66}
67