A Claude Code plugin that compacts the conversation when the work moves on from one sub-task to the next, instead of when the context window fills.
without taskcut with taskcut
─────────────────────────────────── ───────────────────────────────────
you: "do A, B and C" you: "do A, B and C"
... A's work ... ... A's work ...
... B's work ... A done, on to B
... C's work, half-way ── compacted ──
── window full ── ... B's work ...
── compacted ── B done, on to C
C's live detail ── compacted ──
summarised away ... C's work ...
C done: kept, it is yours now
taskcut changes when Claude Code compacts, not how. Once the context is
past a floor, a model judges each step Claude takes for whether the work is
moving on from a finished piece to another, reading what you asked for and
where Claude has got to, never any tool output. If it is, taskcut runs Claude
Code's own compaction, the one /compact runs. A piece being finished is not
enough: when the last thing you asked for is done, nothing has moved on yet,
and what you say next may well be about it. Hand over twenty tasks in one
message and walk away, and it compacts between them, not after the last. Once
a turn has ended the work is back with you, and so is /compact. Below the
floor it does nothing at all and costs nothing, and nothing in your prompts
has to mention it.
Needs Claude Code 2.1.278 or newer; developed and measured on 2.1.280. In a project you work on — installed for you alone, nothing committed:
claude plugin marketplace add wasd96040501/taskcut --scope local
claude plugin install taskcut@taskcut --scope local
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claudeThe install notes that options are "not yet set". That is fine: unset, each takes its default.
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 is needed on every launch while function
hooks are in early access. Without it the plugin is listed as installed but never
runs, and nothing says so. To set it once, put
"CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" under env in
~/.claude/settings.json. To check that taskcut loads:
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude -p ok --debug-file /tmp/taskcut.log >/dev/null
grep 'taskcut@taskcut loaded' /tmp/taskcut.logThen work as usual. Nothing happens until the context passes 35% — the ctx
figure in the status line. From then on, when Claude finishes one of the things
you asked for and moves on to the next, a dim line says so:
taskcut: context at 43%, compacting before the next piece
taskcut ends the turn before Claude's next request, compacts exactly as
/compact would, and sends Continue. in your place — the transcript shows it
as a message from the taskcut plugin — and the work carries on. A step that
asks you something, works on a piece not yet finished, or finishes the last
thing you asked for is left alone. Nothing you type mentions taskcut.
To remove it:
claude plugin uninstall taskcut@taskcut --scope local
claude plugin marketplace remove taskcut --scope localClaude Code compacts when the window fills. On a job that runs for hours or days, that moment almost never lines up with the shape of the work: it fires in the middle of a sub-task, summarising away detail that is still live while keeping detail from work that finished an hour ago.
A long job is a sequence of shorter ones, and the moment when what still matters has a clean answer is the end of a sub-task. taskcut compacts there instead, and leaves what a compaction keeps to Claude Code. The job that needs it most is the one nobody watches: a list of tasks handed over in one message, worked through in one turn that runs for hours.
# this repository, for everyone who clones it
claude plugin marketplace add wasd96040501/taskcut --scope project
claude plugin install taskcut@taskcut --scope project
# every session on this machine
claude plugin marketplace add wasd96040501/taskcut
claude plugin install taskcut@taskcut --scope userStart sessions with CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude, or put
"CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" under env in
~/.claude/settings.json and start them as usual. Off for one session:
TASKCUT=0 claude. To remove it, uninstall with the same --scope.
| Below the floor | Nothing: no model call, no tool, nothing written. |
| Each step Claude explains past it | One sonnet call and a sentence out. What goes in is your messages, Claude's latest messages, what its latest commands touched and the step it is taking, never any output: about two thousand tokens however long the session has run, uncached ($.model.complete marks no cache point) — about half a cent. It runs while the step's tools run, so it rarely adds a wait. |
| Each compaction | Whatever /compact costs, because it is /compact. |
Past the floor is a short stretch: a compaction takes the context back under it, and judging stops until it fills again.
/taskcut says what it has spent so far in the session. /cost does not
count the judge's calls -- Claude Code keeps no ledger of a plugin's model
calls -- so they are counted there, from the token counts the API returns.
Thirty-six real sqlglot changes, handed to Sonnet 5 in one message and left to run: taskcut compacted once inside the turn, after issue 20, from 431,567 tokens to 10,511, and carried on. The peak context fell from 71% of the window to 43%, and the cost by about a fifth; both runs solved all thirty-six. Whether compacting at boundaries makes long sessions work better is what the benchmark is for: docs/measurement.md.
Both have a working default. Set one at install time with --config KEY=VALUE,
or change it later from a session with /plugin. Settings are yours, not a
scope's: Claude Code keeps plugin settings in your user settings, so they apply
wherever taskcut is installed on this machine, whatever --scope it was
installed with.
| Setting | In /plugin |
Default | What it controls |
|---|---|---|---|
floorPercent |
Context % before taskcut acts | 35 |
A number from 0 to 100. Below this context fill, taskcut does nothing: no model is asked and nothing is compacted. Lower compacts sooner and more often; each compaction clears the prompt cache. 0 checks every step. |
model |
Model that spots task switches | sonnet |
The model that decides whether Claude has moved on to the next task — the model auto mode's permission classifier uses by default. haiku costs about a third but misses about half the task switches. An alias or a full model id, resolved the way a --model value is. |
Left empty in /plugin, a setting keeps its default.
After each step of the main conversation, taskcut reads the context fill the
status line shows. Below floorPercent it stops there. Past it, it asks
model, through the hooks API's $.model.complete, whether the work moves on
from a finished piece to another at that step. The judge reads what says so
and little else: every message you sent (the first and the latest few whole,
the rest cut to a line), Claude's latest messages, what its latest calls
touched — a file, or what a command says it does, never the call in full — its
task list if it keeps one, and the step it is judging: what Claude just said
and the calls it is making. Never any tool output, and not CLAUDE.md. A step
that asks you something is never a boundary, and the end of a turn is never
judged.
On a yes, taskcut waits for that step's tools to finish, ends the turn with
$.turn.abort before the next request goes out, calls $.session.compact() —
the call /compact makes — and submits Continue. with $.prompt.submit:
what you would do yourself with Esc, /compact and "continue". On anything
else it leaves the conversation alone.
The source is two files: hooks/register.ts (the hooks) and hooks/judge.ts
(what the judge reads and asks). docs/design.md has the
reasoning, and the designs this one replaced.
- Interactive sessions only.
claude -pand the SDK transport cannot compact, and taskcut never ends a turn there. - The judge never sees tool output, so a reply that claims more than was done can fool it. The cost is a compaction a little early.
- Only a step that says something is judged. A step that finishes one piece and starts the next without a word -- a commit, then the next issue's first command -- is never asked about. Claude usually says so somewhere nearby, but not always: in the benchmark about half the moves between issues were silent, and one turn of four tasks done in silence was not compacted at all (measurement).
- A compaction inside a turn splits it in two. Claude Code compacts only
between turns, so taskcut ends the turn and starts the next with
Continue., which the transcript shows as a message from the plugin. What the compaction keeps is the same as/compactkeeps. - Only inside a turn. taskcut compacts between the pieces of one turn's
work. Once the turn ends, a new piece starts with your next message, and
/compactbefore it is yours to run; Claude Code's own threshold still applies. - Not in a turn's first step. On Claude Code 2.1.280 a compaction right
after a request that ended on your own message —
/compacttyped by hand included — answers that message instead of summarising the conversation, so taskcut compacts only after a step that followed tool results. - Early access. The function-hooks API may change between Claude Code
releases;
make validatereports anything the engine would refuse.
- docs/measurement.md — what the benchmark found.
- eval/README.md — the benchmark itself.
- docs/design.md — why taskcut decides only when, and the designs it replaced.
- docs/troubleshooting.md — what to check when nothing is being compacted.
- docs/compatibility.md — what a Claude Code upgrade can break.
See CONTRIBUTING.md. make check runs everything CI runs.
MIT. See LICENSE.