Skip to content

Commands

m4bwav edited this page Oct 31, 2026 · 6 revisions

Every command prints plain text and exits 0, except where noted. Token counts are estimates: characters divided by four.

changed

Answers "did this file change since I last read it?" so the agent can skip a re-read, or read only the diff.

python tw.py changed notes.md

The first time, there is no snapshot yet:

new       notes.md (3 lines, ~5 tokens): no snapshot yet; read it once

Run it again with no change:

unchanged notes.md
all unchanged: skip the re-read

After the second line was changed to TWO:

changed   notes.md (2 changed lines; diff ~15 tokens vs whole file ~5)
  @@ -1,3 +1,3 @@
   one
  -two
  +TWO
   three

A path that does not exist prints missing nope.md and the command still exits 0.

Options:

  • --snapshots DIR: where snapshots live. Default .tokenwright/snapshots in the current folder, or $TOKENWRIGHT_SNAPSHOTS.
  • --context N: diff context lines.
  • --quiet: status lines only, no diff.
  • --dry-run: compare without updating the snapshot.

Details the help does not say:

  • A file counts as unchanged when its size and modification time match the snapshot. If only the time moved, the content hash decides, and it prints unchanged ... (touched, same content).
  • Files over 2,000,000 bytes get a hash but no stored copy, so a later change says changed ... (binary or no stored copy; read it). Binary files say the same.
  • Every run updates the snapshot, unless --dry-run.
  • For a tiny file the diff can cost more than the file, as above. Before 0.2.1 the line count was one too high for a file ending in a newline.

score

Ranks the token sinks in a Claude Code session transcript (JSONL). With no path it reads the newest file under ~/.claude/projects.

Example on a synthetic 5-turn transcript that read a 500-line file whole on turns 1 and 3:

python tw.py score session.jsonl
transcript session.jsonl
turns 5, tool calls 2; input 50, cache read 5000, cache write 500, output 100 tokens
estimated avoidable ~2196 tokens entered context; carried on later turns as ~6588 cache-read tokens (billed at about a tenth of input)
  whole-read             ~1098
  repeat read            ~1098
top sinks:
  ~  1098  #1    whole-read             Read src/app.py whole (500 lines)
          fix: search first (Grep -n with -C), then Read offset/limit; or readwright outline
  ~  1098  #2    repeat read            Read src/app.py again (2 reads)
          fix: keep the facts from the first read, or `tw.py changed` to skip unchanged files

The second read counts once, as a repeat read. Before 0.2.1 it was also listed as a whole read and counted twice.

The sinks it looks for:

Kind When
whole-read the first Read of a file with no offset or limit returned 400 lines or more
repeat read the same file read again
re-read after own edit read again after the agent's own edit
shell-cat cat, type or Get-Content printed a whole file
big-output any other tool returned 20,000 characters or more
retry a call failed again with the same input
poll the same shell command ran a third time or more
debug-loop three failed calls in a row to one tool (once per streak)

"Carried" is each sink multiplied by the turns left after it. An image Read counts as about 1,600 tokens.

Options: --top N (how many sinks to list), --json (includes waste_per_100_calls), --compare N.

--compare N (0.2.7) scores the newest N other transcripts in the same project folder, skips those with fewer than 10 tool calls, and adds one line: this session's estimated waste per 100 tool calls next to the best and the median of those sessions.

compare: ~6171 waste tokens per 100 calls; best of 16 past sessions here ~0 (2cd2f88d-....jsonl), median ~8449

repeats

Finds shell command sequences that repeat across sessions: candidates for a script.

python tw.py repeats s0.jsonl s1.jsonl s2.jsonl --top 1
3 transcripts scanned; 1 sequences repeat in >= 3 sessions

3 sessions, 3 steps -> script candidate (see tokenwright-script):
    git status
    python -m unittest discover -s tests
    git push

Before comparing, it drops a leading cd DIR &&, and replaces quoted strings with <s>, numbers with <n> and paths with <path>. Only Bash and PowerShell calls count. With no paths, --last N scans the newest N transcripts. --min-len, --max-len and --min-sessions set the sequence lengths and how many sessions must share one.

skill

Prices a skill, or every skill under a folder, in three parts.

python tw.py skill demo-skill
skill                          always on-trigger  on-demand
demo                               10        29       1003
always = name + description, paid by every session where the skill is listed

savings, largest first:
  ~    0  demo: references/orphan.md (~1000 tokens) is never named in SKILL.md, so the agent cannot find it on demand: link it with when to read it, or delete it

It flags a description over 600 characters, a body over 500 lines, code blocks of 25 lines or more, 15 or more numbered steps with no scripts/ folder, and files over 2,000 bytes that nothing links. --json prints the report as JSON.

estimate

python tw.py estimate --total-only big.log notes.md
   10505  total (~tokens)

A folder is walked recursively.

catalog

Forwards everything after catalog to skills/tokenwright-catalog/scripts/tidy.py. Added in 0.2.0. Its commands: scan, lint, conflicts, triggers, check, apply, sections, split, park, unpark, parked, budget, usage, offload, startup, harvest, version. Each takes --agent claude|codex|copilot|cursor|all, --project PATH and --json.

With two user skills whose descriptions overlap:

python tw.py catalog conflicts
1 pairs at TF-IDF cosine >= 0.45 among 2 listed skills (0.65+ near-duplicate)
0.63 feed-scan ~ radar-sweep
     shared words: feed, track, news, articl, databas, note, summarise, trend
     boundary for feed-scan: To sweep news feeds every morning for articles about databases, use radar-sweep.
     boundary for radar-sweep: To collect articles from news feeds about databases, use feed-scan.
python tw.py catalog lint
WARN  ST019 feed-scan: description is 0.63 similar to radar-sweep and has no boundary clause; end with one short 'Not for <their job> (that is radar-sweep)'
WARN  ST019 radar-sweep: description is 0.63 similar to feed-scan and has no boundary clause; end with one short 'Not for <their job> (that is feed-scan)'
2 skills checked; 2 findings at warn or above

apply writes a checked description and keeps a backup under ~/.skill-tidy/backups/; when that folder cannot be written, the backup goes beside the file as SKILL.md.bak-<time>. The state folder keeps its skill-tidy name so parking lots made with skill-tidy keep working.

meter

Added in 0.2.7. Prints the tool output the post hook has counted for a session, short enough for a status line. With no argument it reads the most recent session. It stays at tw ~0k unless TOKENWRIGHT_HOOKS is set, because only the hook counts.

python tw.py meter
tw ~30k

The counts live in tokenwright-meter/ under the system temp folder, one small JSON file per session; set TOKENWRIGHT_METER_DIR to move them.

hook

The entry point for the plugin's hooks. It reads the hook event as JSON on stdin. See Hooks.

Clone this wiki locally