Measure what your AI coding sessions actually cost, then get advice derived from your numbers instead of someone else's.
On the first real machine this was run against, 98% of every token billed was a cache read — the same content, re-read on the next turn, and the next. Each token of content created was billed 355 times; on a second machine, 132×.
That number is the whole point. Most token-saving advice assumes the cost is the size of what you send. It usually is not. The cost is how long it stays.
cost of a token ~= its size x the number of turns that re-read it
The tools in this space compress tool output. That is a reasonable thing to do and it is measurably the wrong priority on many machines:
- A compressor cannot touch the fixed preamble — system prompt, tool schemas, memory files — which is re-read on every turn. On the machine above that was 10.3% of all cache reads, from 24,748 tokens of text.
- A compressor cannot touch command text. On that machine the Bash commands cost 79% of what their output cost. Half the surface, invisible to every tool that only looks at results.
- A compressor cannot shorten a 173-turn session whose context grew from 27K to 716K, and session length is superlinear: every turn re-reads all the turns before it.
- And where outputs are small — a median of 113 tokens — there is simply nothing in them to compress.
None of that is knowable without measuring. So this measures first.
scripts/bootstrap --check # what is present; installs nothing
scripts/ts audit # where the tokens went
scripts/ts advise # what to do about it, ranked
scripts/verify # checks against fixtures with known answerspython3 (3.9+) is the only hard requirement. tiktoken is optional — without
it, content sizes are estimated from character counts using a divisor per
content class, which lands within about 1% of the real tokenizer in
aggregate and within 5% on any single class. The aggregate figure is
mix-dependent, so treat it as ~2% rather than a guarantee. Every report names
which counter produced its numbers.
Nothing is sent anywhere. There is no network code in this repository.
| Question | Command |
|---|---|
| What is this session costing me right now? | ts now |
| Where did my tokens go? | ts audit |
| ...one project only | ts audit --project '*myrepo*' |
| ...heaviest sessions | ts audit --sessions |
| What is installed and configured here? | ts doctor |
| What fills my preamble? | ts doctor --memory |
| What should I change? | ts advise |
| ...and what did not apply | ts advise --all |
| What can be changed mechanically? | ts fixes list |
| Preview / apply / undo one | ts fixes show|apply|revert <id> |
| Send my numbers to someone, safely | ts share |
--json on audit, advise and doctor for machine-readable output.
ts audit is a post-mortem: by the time it reports a 1,126-turn session, the
money is spent. ts now answers the same question early enough to act on, plus
one a post-mortem cannot — would clearing right now pay for itself?
context now 370,438 tokens carrying 11x its preamble
growth +490 tokens/turn median of the last 20 turns
of that 64% was re-reading
clearing now breaks even after 1.2 turns
That is arithmetic, not a rule of thumb. Carrying C tokens costs C*r every
turn; clearing costs the preamble once as a write (F*w) and then F*r per
turn. New work costs the same either way, so the growth term cancels and the
whole comparison is:
N* = (w * F) / (r * (C - F)) Anthropic w=1.25, r=0.10 -> 12.5F / (C-F)
A session carrying ten times its preamble breaks even in under two turns — much sooner than most people's instinct, which is the point of showing it live.
What the arithmetic cannot know is whether the context is still needed. That is your call, and the output says so every time instead of pretending otherwise.
ts now --statusline # -> 423K +530/t clear>1tReads Claude Code's session JSON on stdin, so it follows the right transcript
when several sessions are open. It never hangs, never writes to stderr and
never exits non-zero, whatever it is handed — three things scripts/verify
checks, because a statusline that can fail breaks the prompt.
scripts/statusline-snippet.sh is a ready-made version. Use it as your whole
statusline:
{ "statusLine": { "type": "command",
"command": "~/.claude/plugins/cache/agent-toolworks/token-save/*/scripts/statusline-snippet.sh" } }or copy the block between the BEGIN/END markers into the statusline you
already have, call ts_segment "$input" where you want it, and append the
result only when it is non-empty. It resolves ts itself, bounds the call, and
prints nothing at all if the plugin is absent, the transcript is unknown, or
anything goes wrong — so adding it cannot break a prompt that works today.
This is the design, not a side effect. Two detectors:
bash-bulkfires when Bash output has real bulk (p90 above 5,000 tokens). It tells you to install a compressor such as rtk.bash-chatterfires when the cost is call volume with small outputs. It tells you, in as many words, not to install one — and points at batching, which cuts the command text and the turn count too.
They are mutually exclusive, and scripts/verify proves it against two
deliberately opposite fixture fleets. A tool that recommends the same thing to
everyone has not read anything.
Full catalogue: preamble, session-length, subagent-cost, bash-chatter,
bash-bulk, output-verbosity, mcp-schema, repeat-reads, images,
reasoning-cost, attachments.
Each finding also reports how far past its own threshold this machine sits. Firing is not one bit of information: a machine at 1.05x a threshold and one at 3.7x are different facts, and the margin is what separates "you are unusual here" from "everybody trips this".
Every threshold here was calibrated against one machine. That is the tool's biggest weakness, and the only cure is other people's numbers.
ts share --show # read it first
ts share # writes ts-profile.jsonThe profile is shape only: distributions, counts, ratios, and which detectors
fired. It contains no file paths, no project or session names, no commands, no
tool arguments or results, no MCP server names, and no file contents. The
redaction is a whitelist — a field that is not named cannot appear — and
scripts/verify plants a canary through paths, commands, results and MCP tool
names, then fails if any of it survives.
A typical profile is about 3KB.
If you run this and the advice looks wrong for your machine, that is the most useful bug report this project can get. Two of the ten detectors have never fired on real data.
Two machines measured 355× and 132×. The thesis holds at both — persistence beats size either way — but the factor is largely a function of how long sessions run before a clear, which is a habit rather than a property of the tool. Do not read one machine's figure as a constant.
It also changed twice as bugs were fixed, both times downward: recursive discovery added denominator (subagent transcripts), and per-message usage accounting removed inflated numerator. A figure from before v0.9.0 is not comparable with one after it.
BILLED exact — read verbatim from the usage records the provider wrote
into your transcript. Nothing is estimated.
CONTENT estimated — the transcript stores text, not tokens.
The headline metric is the ratio between them, so the exact side is kept exact and the estimated side is labelled. Every finding also carries a confidence:
| label | meaning |
|---|---|
derived |
the projection follows from this machine's own measured shape |
estimated |
measured inputs, plus a stated assumption about what you change |
heuristic |
the direction is right, the magnitude is not |
The label qualifies the saving, not the evidence — evidence is measured or
it is not reported. No saving is ever labelled measured: a saving is always a
projection. An earlier version did label three of them that way, while
multiplying exact billed figures by an invented constant. That is the failure
this tool exists to catch, so verify now rejects it, and every non-derived
finding must print the assumption behind its number:
HIGH Long sessions dominate spend [session-length]
derived worth ~30.6% of spend
assuming: each heavy session split once at its midpoint
Severity is derived from estimated impact rather than declared separately, so the ranking can never disagree with its own numbers.
- It will not shorten your sessions for you. The largest finding on most
machines is behavioural, and
ts fixesdeliberately contains only changes that are mechanical, reversible, and idempotent. Everything else is advice with the evidence attached. - It will not add up its own percentages. Findings overlap. The report prints the largest single item and says so.
- It will not tell you a compressor is worth installing unless your distribution says it is.
- It does not estimate money. Prices change and vary by plan; it reports cost units (tokens re-weighted by what each kind costs relative to one input token), which is what actually ranks the findings.
| id | what it does |
|---|---|
terse-output |
appends a response-style block to CLAUDE.md |
tool-search |
sets ENABLE_TOOL_SEARCH=true when a custom base URL has disabled tool deferral |
Every one shows a diff first (--dry-run is the default), writes a timestamped
backup, is idempotent, and reverts. A fix that cannot meet all four does not
belong there.
/plugin marketplace add agent-toolworks/plugins # every tool in the org
/plugin install token-save
Or add this repository directly, if you only want this one:
/plugin marketplace add agent-toolworks/token-save
/plugin install token-save
This registers the token-save skill, which teaches the agent to measure
before it advises — the failure mode being that a model answers "how do I
reduce tokens?" from general knowledge, which is close to a coin flip.
scripts/verifyTwenty-seven checks in seven groups: arithmetic against hand-derived
expectations in fixtures/EXPECTATIONS.json (not recorded from program output
— a self-test that records its own output tests nothing), routing across the
two opposite fleets, detector coverage (every entry in the catalogue must have
a fixture that fires it, or a named exemption), version agreement, redaction
against a planted canary, the live signal's arithmetic, and safety — including
that an unparseable settings.json is refused rather than overwritten and that
the statusline cannot hang.
Apache-2.0. See LICENSE.