Kairos
The right moment, caught before it passes.
Kairos (καιρός) is the ancient Greek word for the right moment, as opposed to chronos, which is time merely passing. Personified he is a young god with a long lock of hair over his forehead and nothing at all at the back of his head: you can catch him as he comes toward you, and never once he has gone by.
- Bash
- jq
- Claude Code
- hooks
GitHub (opens in a new tab)README (opens in a new tab)Changelog (opens in a new tab)
What it does
Claude Code enforces a five-hour usage limit and gives no warning before it. Work stops mid-task, at a moment the limit chooses rather than you, and the only notice is the failure itself.
Kairos watches how much of the window you have spent, predicts what your next turn will cost, and refuses the prompt before that turn takes you through the wall. When it stops you it asks what to do: hold until the window resets, send it anyway, or drop it.
Nothing on your machine reports how much of the limit you have used, or where the limit is. Both are reconstructed from the transcripts Claude Code already writes.
Getting started
claude plugin marketplace add https://aranea-development.nl/plugins/marketplace.json
claude plugin install kairos@araneaHooks bind when a session starts, so start a new session before Kairos does anything. An already running session will not pick it up. It needs bash and jq, and if jq is missing it says so once and then does nothing.
If the install stops at ssh: connect to host github.com port 22, git on that machine cannot reach GitHub over SSH, and the plugin is cloned from its GitHub repository. The message points at access rights, but the repository is public and the transport is what failed. One line fixes it, then install again:
git config --global --add url."https://github.com/".insteadOf "git@github.com:"The README has the longer version, including how to undo it.
What it knows, and what it does not
The reconstruction is exact for consumption and inexact for the ceiling, so the ceiling is reported as a range with the evidence behind it. One recorded refusal gives a wide range, three give a tighter one.
An account Kairos has never seen refused gets no range at all, and is never gated. It measures and reports and stays out of the way until it has observed a wall of that account's own.
That is deliberate. An earlier version seeded a plausible range from observed data, and reconstructing every window in the same history showed some reaching 16.5M billable tokens with no refusal at all, while the seed topped out at 5.7M. Those figures described one subscription, not the shape of the limit. A guessed ceiling would interrupt constantly on a larger plan while claiming to know something it had never observed.
Two subscriptions
If you hold more than one Claude plan and switch between them, Kairos keeps them apart. Everything it records is partitioned by account, a session follows the account that is actually paying even when you switch part way through, and a Max 5x is told apart from a Max 20x, because their ceilings differ by roughly four times and telling them apart is the point.
Nothing in the transcripts records which account paid for a message, so this cannot be backfilled. Kairos starts knowing nothing about a new account and gets better as it runs.
How the numbers are arrived at
Consumption counts input tokens, cache creation and output. Cache reads are excluded, and that came out of measurement. Weighting them at zero fits the recorded refusals, and including them at any weight makes the error four times worse. A meter that counted them would be wrong by roughly a factor of a hundred.
The five-hour window is reconstructed from transcript timestamps as chained windows floored to ten minutes. That model reproduces the reset times Claude's own refusals state, to the minute.
The next turn is predicted from the 75th percentile of recent turns, because the long tail is what actually puts a session through a limit. The gate compares against the optimistic edge of the range, so it stays quiet until even a generous ceiling is threatened. Kairos would sooner let a first wall happen than interrupt you wrongly for a month.
What it never does
It never writes your account's email address to disk. It never runs /login for you; it will say when another account looks clear and leave the switch to you. And it never blocks a prompt because something inside it broke. Every uncertain path lets the prompt through, because being wrong about the budget is a nuisance while stopping your work because the meter failed is worse than having no meter.
When something is wrong
Run /kairos. It prints what the plugin currently believes, and that is usually enough to tell the three quiet states apart, because from outside they look the same.
Nothing recorded for this account yet means no consumption has been read. Either jq is missing, or the hooks are not bound because the session was already running when you installed. Start a new session and look again.
No ceiling recorded yet is not a fault. It is the normal state for an account that has never hit a limit while Kairos was watching, and it stays that way until one happens. kairos calibrate rescans your whole transcript history for refusals it has not seen, which is worth doing once after installing.
A figure that looks wrong is worth checking against kairos accounts. If you use more than one plan, the number you are looking at belongs to whichever account is active now, and the other is listed beside it.
How it works
Four hooks, all of them harness-only, so none of this costs model context.
| Hook | Job |
|---|---|
SessionStart | Resolve the paying account, bind the session to it, bring the meter up to date, harvest any refusals |
UserPromptSubmit | Follow a mid-session account switch, predict the turn, block or let it through |
Stop | Record what the turn actually cost, and harvest a refusal if one just happened |
SessionEnd | Print the closing line |
Consumption is read incrementally. A cursor per transcript records how many bytes have been counted, so a refresh reads only what was appended since. Everything is partitioned by account under ~/.claude/kairos/accounts/, which is where the recorded refusals and the turn history live too.
Platform notes
Tested on Linux, macOS and Windows on every push, and again under bash 3.2 on macOS, which is what macOS still ships as /bin/bash.
Two defects in this plugin's history were reachable only on Windows. Git converts line endings on checkout, and a carriage return riding on a numeric field is not a wrong number but an arithmetic error. And replacing a file is not atomic there, so a concurrent writer can make one vanish between an existence check and a read. Both are handled, and both were found by CI.
Requirements
bash and jq. Linux, macOS and Windows. The test suite runs on all three, and again under bash 3.2 on macOS, which is what macOS still ships as /bin/bash.