A Claude Code mod that answers one question after every turn: was Claude slow, or was it your npm test?
It splits each turn's time by cause and shows it under the answer: in the terminal as a coloured line in place of Claude Code's "Worked for" line, elsewhere as a dim log line. /blame opens a panel with the whole session's split as a coloured bar. Claude never reads any of it, and nothing leaves your machine.
A real run on Claude Code 2.1.302, in a small demo project whose test suite takes about 13 seconds. The waits are shown at 6× speed, as the clip says. Of the 47 seconds, Claude took 15 and npm test took 27. Watch it as a video.
The same two outputs in plain text, from an earlier live session. This is what you get outside the terminal, or where nothing can draw a panel:
⏺ time-blame: 26s · Claude 8.0s (7.2s before output) · sleep 4.7s · you 12s · other 0.3s
⏺ time-blame: this session, this machine · 1 turn · 26s of turn time
⏺ time-blame: Claude 8.0s 31% before output 7.2s, writing 0.8s
⏺ time-blame: tools 4.7s 19% sleep 4.7s
⏺ time-blame: you 12s 49% approvals 12s
⏺ time-blame: other 0.3s 1% not attributed: hooks, local work, gaps
⏺ time-blame: longest turn 26s. Times are summed waits measured in this session, not server speed.
Both blocks are copied as text from that session, where a permission dialog was left open for 12 seconds before it was approved.
claude plugin marketplace add karanb192/time-blame
claude plugin install time-blame@time-blameThen restart Claude Code or run /reload-plugins. Mods are on by default from Claude Code 2.1.287; time-blame needs 2.1.302 or later. To try it for one session without installing, clone the repo and run claude --plugin-dir ./time-blame.
Landing page: https://timeblame.aidojo.si/
Every millisecond of a turn lands in exactly one bucket, so the parts always add up to the total.
| Bucket | Measured as | When two overlap |
|---|---|---|
| you | A permission dialog, from the moment it opens to the moment the tool starts. Also the whole of an AskUserQuestion or ExitPlanMode call. |
Wins over everything |
| tools | The tool's own execution time, which Claude Code reports with each result. Named by tool, and by command for Bash. | Wins over Claude |
| Claude, before output | From the moment a model request is sent to its first text, thinking or tool-call chunk. Includes queueing, retries and network. | |
| Claude, writing | From that first chunk to the end of the request. | |
| other | Whatever is left: hooks, local work, the gaps between steps. Always shown, never hidden. |
Two or more tools running at once count as parallel tools, rather than blaming whichever one was slowest to return.
A subagent's time sits inside the Agent call that started it, labelled subagent. Its own steps are not counted again.
The line appears for turns of 3 seconds or more. /blame counts every turn.
- The total is longer than Claude Code's "Worked for". Claude Code's counter leaves out the time a dialog waits for you. In the run above it said 13s, and time-blame said 26s: the 12 seconds of the open dialog, plus rounding.
- Bash commands are named only from a fixed list. Common programs (
npm,pytest,cargo,git,makeand others) and their common subcommands (npm test,cargo build) are shown. A script name, a path or anything else shows asBash, because a command line can carry private names. In a chained command such asgit stash && npm test, the test or build step names the call, since that is where the time usually goes. - On a managed Claude Code install, approvals count as tool time. Approvals are timed with the
PermissionRequestandPostToolUsesettings-hook events. Where an organization manages Claude Code, those events skip plugins a person installs. The dialog wait then falls into the tool's time. - Retries are not a separate bucket. Claude Code logs a retried request only after it gives up, so a retry that succeeds leaves no record a mod can read. Retry time shows up as "before output".
- Colour needs the terminal for the turn line. Only the terminal lets a mod redraw the line that closes a turn. In the Desktop app and VS Code the turn line is a dim log line, and an interrupted turn always is. The
/blamepanel draws in all three. - Totals live in memory. They reset on
/clear,/resumeand when the plugin reloads. - It measures this session on this machine. It says nothing about Anthropic's servers, and it keeps nothing after the session ends.
Validated on Claude Code 2.1.302:
❯ ./register.ts hooks: session.start, command.run{command=blame}, ui.render{component=Pane}, ui.render{component=TurnDuration}, session.end, turn.start, turn.step, classic.PermissionRequest, classic.PostToolUse, classic.PostToolUseFailure, tool.call, turn.complete
❯ ./register.ts calls: $.command.register, $.session.surfaces, $.ui.invalidate, $.ui.log, $.ui.open, $.ui.resolve
Reach L0, draws and remembers. It sees every tool call and every model request, as timing only. It draws in two places: the line that closes a turn in the terminal, and its own /blame panel.
Threat model for time-blame (reach L0, draws and remembers)
1. Reads: which apps are drawing the session (terminal, Desktop, VS Code); timestamps of turns, model requests and tool calls; the kind of each streamed chunk (never its text); the tool name; for Bash, the command line, matched against a fixed list of program names and dropped; the execution time Claude Code reports with each result
2. Runs: nothing
3. Sends: nothing leaves the machine
4. Persists: nothing; the session's totals live in memory and reset on /clear and /resume
5. Hostile input: a crafted command line can only change which allowlisted label is shown or turn it into "Bash"; no input reaches a process, a file, a prompt or the network, and what it shows goes to the screen only, as drawn lines, a panel or dim log lines, none of which Claude reads
If a hook throws, Claude Code skips it and the tool call or request runs as it would without the mod.
claude plugin validate .claude-plugin/plugin.json
claude plugin test .hooks/ledger.ts holds the accounting and the labels as pure functions; hooks/register.ts only collects timestamps. For type checking, run /plugin-types in a session in this folder, then npx tsc -p .. The generated types are git-ignored.
MIT. The landing page uses icons from Lucide (ISC) and the GitHub mark from Simple Icons (CC0).
