Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

Marmot — Catch token waste while you can still act

A local Claude Code usage tool that tells you what is wasting tokens—and what to do next.

Status: alpha License: MIT Join the Discord

Marmot reads the session records Claude Code already keeps on your machine and points out where tokens are being wasted: context that has outlived the task, MCP tools that are loaded but never called, poor cache reuse, repeated tool failures, or a costly model doing light work.

The useful part is timing. Marmot can nudge you at the end of a turn, while starting fresh, compacting, or changing course can still save the next one.

Install

Requires Node.js 18 or newer.

npm install -g github:DrDroidLab/marmot
marmot init --hooks

Restart Claude Code, then verify it:

marmot doctor

Want to look first? marmot --demo uses synthetic data and reads none of your sessions.

What a nudge looks like

Marmot notification recommending compacting or starting a new session because each turn is re-sending 140K tokens of history

The desktop notification stays short. Claude Code gets the evidence and the next action in its transcript. Marmot speaks once per rule instead of repeating the same warning after every turn.

Both nudges and the daily digest arrive as a dialog: it carries the marmot, has room for what to do about it as well as what it cost, and stays until you dismiss it. A banner you were not looking at is a banner you missed, which is the whole failure this is here to fix.

marmot test-notification # see a nudge
marmot test-notification --digest # see the daily digest
marmot test-notification --banner # see the other shape

The two are set separately, so you can keep the interruption for the thing that is costing you money and let the once-a-day summary stay out of the way:

marmot config set notify.style.digest=banner # summary as a banner
marmot config set notify.style.nudge=auto # dialog only near a limit
marmot config set notify.style=banner # both, as banners
alert A dialog. Marmot icon, room for the action, waits for a click. Default for both.
banner The ordinary desktop notification. No marmot on it, and it dismisses itself.
auto A dialog only near a limit — the last mark, or a window burning faster than it refills. Banner otherwise.

The banner caveats are not Marmot's choice. On macOS a notification's icon is whichever app posted it, and how long it stays is that app's Alert style in System Settings — neither is something display notification can set. The dialog exists because it can do both.

On Linux none of this applies and nothing needs setting: a critical notification there already shows the marmot and already never expires.

See where the tokens went

marmot
×ばつ ~4.1K tokens to load MCP servers github ×ばつ 26 tools · ~4.0K tokens sentry ×ばつ 14 tools · ~2.3K tokens ▲ never called 18,825 tokens on every request, 16,262 of them idle">
 Marmot · your last 30 days
 40 sessions · everything below was read from ~/.claude/projects on this machine
 Spend 4,184ドル modelled at published rates
 Prompts you typed 926 per session: 23.1 mean · 16 median · 79 p99
 Tokens 5.8B input, output and cache
 Cache hit rate 98% higher is cheaper
 Tool calls 13,768 3% failed
 Baseline context 35.3K median, before you type
 Daily ▃▂▄▁▃█▆▃▂▁▄▁▂▆▁▅▄▅ peak 671ドル · median 233ドル
 Where it went
 claude-opus-5 4,092ドル 98% · 5.7B tokens
 claude-sonnet-5 92ドル 2% · 104M tokens
 Tools failing
 Bash 126 of 4,933 3%
 mcp__supabase__execute_sql 3 of 3 100%
 Skills
 dataviz ×ばつ ~4.1K tokens to load
 MCP servers
 github ×ばつ 26 tools · ~4.0K tokens
 sentry ×ばつ 14 tools · ~2.3K tokens さんかく never called
 18,825 tokens on every request, 16,262 of them idle

The report gives every figure a window and a source. Run marmot browse when you want to open a session and follow a number down to the turn that caused it. The browser is one local HTML file with no network calls; browse --no-text leaves prompts and replies out.

Commands

marmot # report for the last 30 days
marmot --days 7 # choose a window
marmot browse # open the local session browser
marmot nudges # show only actionable findings
marmot sessions # list sessions one per line
marmot mcp-audit # measure MCP tool-definition weight
marmot config # open your thresholds
marmot remind # show or change when nudges fire
marmot doctor # check readers, hooks and notifications
marmot test-notification # send a test nudge

Useful options:

marmot --sessions # include every session in the report
marmot --json # machine-readable output
marmot --no-audit # do not start MCP servers for measurement
marmot --no-refresh # do not refresh plan-limit data
marmot browse --no-text # exclude prompts and replies from the page

Run marmot --help for the complete reference.

What Marmot catches

Signal What it means
Long session Old turns keep travelling into new ones
Stale session Work resumes days later in a different area
Idle MCP server Tool definitions are loaded but never used
Premium model on light work A costly model handles a small task
Low cache hit rate Context is rebuilt instead of reused
Tool failures Calls fail and then need another turn to recover
Usage spike Today is far beyond your own normal
Limit pace Your allowance is disappearing faster than its window

Every check is deterministic. No model decides whether to nudge you.

Most of those are causes rather than alarms. What interrupts you is a threshold—half, three quarters, then nine tenths of a plan window—and the nudge carries whichever cause best explains getting there:

×ばつ. It resets in 2.1d. Each turn re-sends 603K tokens of history, over 57 prompts and 3.8d. Run /compact, or start a new session for the next distinct piece of work.">
さんかく 75% of your weekly limit
 76% of your weekly limit is gone on Max ×ばつ. It resets in 2.1d.
 Each turn re-sends 603K tokens of history, over 57 prompts and 3.8d.
 Run /compact, or start a new session for the next distinct piece of work.

Causes are ranked by how much each explains, how confident Marmot is, and how cheaply it can be fixed. marmot prints that ranking under Why it is going, scores included. When nothing scores highly enough, the threshold still fires without an invented reason.

When Marmot speaks

Rules in the live list can interrupt at the end of a turn—the moment you can still change the session in front of you. Everything else waits for the daily digest.

A rule speaks once per session. Cost warnings can return when the cost doubles, and plan warnings return at the configured marks. One live nudge also buys 20 minutes of quiet before another can interrupt; held findings remain in the report and digest.

Dollars or allowance

On a subscription, the dollar figure is labelled Modelled spend. It is what the tokens would cost at published API rates, not what you pay. Marmot also shows the plan limits Claude Code exposes locally:

×ばつ 5-hour session limit 5% resets in 1.2h Weekly limit 19% resets in 3.0d Usage credits 0ドル.00 of 50ドル.00 real money, beyond the plan">
 Modelled spend 1,825ドル at API rates — not what you pay on Max ×ばつ
 5-hour session limit 5% resets in 1.2h
 Weekly limit 19% resets in 3.0d
 Usage credits 0ドル.00 of 50ドル.00 real money, beyond the plan

On pay-as-you-go usage, the same dollar figure is labelled Spend because it is the bill. Marmot uses percentage limits when the plan reports them and dollar caps when it does not.

Configuration

The defaults are deliberately quiet, so configuration is optional. If a rule fires on most of your sessions, it is describing how you work rather than flagging something unusual—raise the threshold instead of learning to ignore it.

marmot config set session.costCap=50 # change one threshold
marmot config set 'limits.steps=[25,50,75]' # values are JSON
marmot config set notify.bell=false mcp.autoAudit=false

Marmot prints what changed, creates the file from the defaults when needed, and leaves every other setting alone:

 /Users/you/.claude/marmot.json
 session.costCap: 25 → 50

This is the form to use from a script or coding agent. To edit or inspect the whole file:

marmot config # open it in your editor
marmot config --print # print it in the terminal

Nothing needs restarting—the next run reads it. Marmot uses $VISUAL, then $EDITOR, then the platform default. A terminal editor is used only when there is a terminal to attach it to.

Reminders

marmot remind # show what fires and when
marmot remind --at 50,75,90 # set quota marks
marmot remind --cap 100 # set a dollar ceiling
marmot remind --off # turn reminders off

Marmot chooses the useful ceiling from the plan it can read:

Plan Ceiling
Pro, Max, most Team seats Reported quota, at 50%, 75% and 90% by default
Enterprise or a plan reporting no quota Daily dollar cap; session cap is half
Pay-as-you-go API Dollar cap, because the figure is the bill

Only one live nudge interrupts at a time. After one fires, Marmot leaves 20 minutes of quiet before another (interrupt.minGapMins). Held findings remain in marmot and the daily digest.

What Claude Code says is eating your limits

Refreshing limits also captures Claude Code's own attribution of your usage:

 What is driving your limits · last 7d
 Claude Code's own attribution, over 2,594 requests in 19 sessions.
 96% of your usage was at >150k context
 80% of your usage came from sessions active for 8+ hours
 top skills: claude-api 1%
 top mcp servers: sprinto 1%

This is not inferred from transcripts. limit-drivers quotes Claude Code's attribution when a share passes limits.driverMinPercent—60% by default.

The source is human-formatted text with no stability guarantee. Every line is optional; unrecognised lines are skipped, so a format change costs this section rather than the whole report.

Limit thresholds

limit-reached speaks at marks on the way to a limit, so you hear half gone before nearly out. Each mark speaks once.

limit-pace compares the percentage used with the percentage of the window that has passed. It warns only when the allowance is on course to run out before it resets:

×ばつ the pace that would last. At this rate it runs out in about 20.3h, 3.2d before it resets.">
 さんかく Spending your weekly allowance faster than it refills
 43% through the weekly window with 78% of it gone — ×ばつ the pace that
 would last. At this rate it runs out in about 20.3h, 3.2d before it resets.
×ばつ": [50, 75, 90], "Max ×ばつ": [50, 75, 90], "Team": [50, 75, 90], "Enterprise": [50, 75, 90], "API": [] } }">
"limits": {
 "enabled": true,
 "steps": [50, 75, 90],
 "byPlan": {
 "Pro": [50, 75, 90],
 "Max ×ばつ": [50, 75, 90],
 "Max ×ばつ": [50, 75, 90],
 "Team": [50, 75, 90],
 "Enterprise": [50, 75, 90],
 "API": []
 }
}

The nudge thresholds

Rule Fires when Default
session-cost One session's modelled cost > 25ドル
daily-cost Today's total > 50ドル
daily-baseline Today against your trailing average > 2.5σ over 14 days
session-topics A long session resumed in a different area > 1 day gap, ≥ 2 areas
limit-reached A plan window crosses a configured mark 50%, 75%, 90%
limit-pace Allowance is disappearing faster than the window > ×ばつ pace, ≥ 15% elapsed, ≥ 20% used
limit-drivers Claude Code attributes a large share to one behavior > 60%

Idle MCP servers, subagent burn, carried history, quiet premium-model work and failing tools are now ranked as causes behind these thresholds. They explain a nudge instead of creating a second, duplicated alarm.

The keys people actually change

{
 // Rules allowed to interrupt at the end of a turn.
 "live": ["limit-reached", "session-cost", "daily-cost",
 "daily-baseline"],
 "interrupt": { "minGapMins": 20, "maxPerNudge": 1 },
 // style: "alert" is a dialog that waits for you — the default for
 // both. "banner" is the ordinary notification: no marmot on it, and
 // it dismisses itself. "auto" is a dialog only near a limit.
 "notify": { "desktop": true, "bell": true, "app": null,
 "sound": "Ping", "persist": true,
 "style": { "nudge": "alert", "digest": "alert" } },
 "digest": { "cadence": "daily" },
 "limits": { "enabled": true, "causeFloor": 0.08,
 "steps": [50, 75, 90],
 "autoRefresh": true, "paceRatio": 1.5,
 "paceMinElapsed": 15, "paceMinUsed": 20,
 "driverMinPercent": 60 },
 "browse": { "keep": 5 },
 "mcp": { "enabled": true, "autoAudit": true,
 "auditMaxAgeDays": 7 },
 "session": { "costCap": 25, "turnCap": 20, "costFloor": 1 },
 "daily": { "costCap": 50, "baselineSigma": 2.5,
 "baselineDays": 14 },
 // USD per million tokens for negotiated pricing.
 "rateOverrides": { "claude-opus-5": { "in": 5, "out": 25 } }
}

marmot config writes every key with its default; these are the ones most people need.

Optional statusline:

marmot init --statusline
12ドル.40 · 57 prompts · 41% ctx · 97% cache · Opus さんかく

The statusline is separate because installing it replaces an existing Claude Code statusline.

Why you can trust the numbers

Claude Code writes one JSONL entry per response content block, and each entry repeats the same usage object. Marmot counts usage once per API response; summing every entry would inflate a tool-heavy session by roughly ×ばつ.

Cache writes are also priced at their recorded lifetime: ×ばつ input price for five minutes and ×ばつ for one hour. Treating every write as the cheaper kind can understate a heavy session by about a fifth.

A turn means a prompt you typed. Tool results also appear as user entries in Claude Code's records, but Marmot does not count them as human prompts. These cases are pinned by the test suite.

Good to know

  • Local by design. Marmot has no account or hosted service.
  • The report avoids conversation text. It reads counts, identifiers and tool names.
  • The browser includes prompts and replies. Use browse --no-text to leave them out. The generated page stays local either way.
  • MCP audit starts configured servers. Use --no-audit or set mcp.autoAudit=false if you do not want that.
  • Subscription dollars are estimates. Modelled spend is the API-rate value, not your invoice; the plan-limit percentage is the useful ceiling.
  • Marmot is alpha. Claude Code's session format is internal and can change. marmot doctor shows what remains readable.

Nudges wrong, or not arriving?

The hooks run in a process Claude Code starts and reaps, so there is normally nothing to look at. Marmot keeps a log of what they did and why:

marmot logs # newest first, and whether the hooks are installed at all
marmot logs --json # raw JSONL, oldest first — attach this to a bug report

Each run records the plan it read, the caps it compared against, and every rule's outcome:

×ばつ · weekly_all 6% rule quiet session-cost — the rule did not match">
 2026年09月03日 09:45:58 Stop nothing to say
 session 6d16e4fb · 33ドル.75 · 30 turns · 30 prompts
 plan Max ×ばつ · weekly_all 6%
 rule quiet session-cost — the rule did not match

That is usually enough to tell a wrong threshold from a rule that never ran. It is capped, and local like everything else; marmot config set log.hooks=false turns it off.

Notifications not appearing?

marmot test-notification

If the test does not appear, check Focus or Do Not Disturb first. Then run marmot doctor to see which notification path Marmot is using.

Both are dialogs by default, which sidesteps this. If you have set notify.style to auto or banner, note that banners fade on their own — and on macOS how long they last is the Alert style of whichever app posts them, not something Marmot can set. Either set that app to Alerts in System Settings → Notifications, or go back to dialogs:

marmot config set notify.style=alert

Keep transcript nudges while disabling desktop notifications or sound:

marmot config set notify.desktop=false
marmot config set notify.bell=false

Update or remove

npm install -g github:DrDroidLab/marmot # update
marmot init --hooks --remove # remove Marmot hooks

Contributing

npm test

Please work on a branch and open a pull request. main moves through reviewed pull requests only.

License

MIT

About

Get notifications about leaks that might be bleeding your tokens. Based on real-time analysis on your coding agent sessions.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /