Where your Claude Code sessions actually spent their time.
You know the feeling: a change took twenty minutes and you could not say where the twenty minutes went. tasktrace reads Claude Code's own session transcripts and tells you — how much was a command genuinely running, how much was the model between calls, how much was a stall nobody noticed, and how much was the session sitting finished while it waited for you.
It runs entirely on your machine. There is no network call anywhere in the program.
Pick whichever fits. It is one Python file tree with no dependencies — Python 3.8 or newer and nothing else.
As a Claude Code plugin (gives Claude the skill, so you can just ask):
/plugin marketplace add seyonv/tasktrace
/plugin install tasktrace
That gives you two ways in, and they do different things:
- Type
/tasktraceand it asks the same three questions the CLI menu asks — how far back, which project, what output — then writes the HTML report and opens it, prints the summary inline, or hands you a picture of the explorer. - Or ask in words — "where did the time go in my sessions this week?" — and it skips the questions, infers the scope, and reads you the answer as text.
The navigable explorer only exists in a real terminal. It can be executed
anywhere — --svg does exactly that, and photographs the result — but nothing
outside a terminal can drive it: a tool call is request and response, so a
keypress has no path into the running process. So /tasktrace substitutes
Claude Code's own picker for the menu, and offers you a snapshot of the
explorer's first screen rather than a screen you can move around in. To sort,
drill in and scroll, use the command line install below.
A plugin install does not put a tasktrace command on your PATH. The
tasktrace … examples further down this page therefore do not work from a plugin
install alone — Claude runs the tool on your behalf instead. If you also want to
run it yourself, add the command line install below; the two coexist fine.
As a skill only:
git clone https://github.com/seyonv/tasktrace
cp -r tasktrace/plugins/tasktrace/skills/tasktrace ~/.claude/skills/As a plain command line tool, no Claude Code required:
git clone https://github.com/seyonv/tasktrace
tasktrace/bin/tasktrace --demobin/tasktrace works from wherever the clone lives. To type tasktrace from
anywhere, symlink it onto your PATH:
ln -s "$PWD/tasktrace/bin/tasktrace" /usr/local/bin/tasktracetasktrace --demoSix synthetic sessions with plausible shapes, including the two failures worth seeing: a call that hung for half an hour, and a session that finished and then waited overnight for a person. Every screenshot in this README was made from them, so nothing of anyone's real work is on this page.
Bare, it asks three questions. Move with ↑/↓, choose with ↵ — or type the
number, which still works:
Each answer collapses to the line that records it, so by the end the menu reads back as what you chose rather than what you were offered:
Where there is no terminal to draw on — a pipe, a CI job, another program — it falls back to printing the list and reading a number, so the menu never becomes the reason a script hangs.
Or skip the menu entirely:
tasktrace --days 7 # last week, terminal explorer
tasktrace --days 30 --html out.html # a shareable report
tasktrace --here --plain # this directory's sessions, as text
tasktrace --days 90 --project api # one projectEither way it shows you what it is doing, in five named steps:
This is the part that matters most, and it is why there are tables rather than prose. A trace read in order tells you what happened. The same trace ordered by duration tells you what it cost. Those are different questions, and a tool that only answers the first one leaves you doing the second by eye.
So every table opens chronological — the trace as it happened — and re-sorts on
any column. In the terminal, s cycles the sort column and r flips the
direction. In the HTML report, click any heading.
The same six sessions, by when they happened and then by how long they took:
It works at every level — sessions, the phases inside a session, and the individual calls inside a phase.
Three levels: sessions → phases → the per-call ledger. j/k to move, enter
to drill in, esc back, / to filter, q to quit.
Phases are runs of similar work, inferred from the tool mix and the gaps — reading the code, changing it, running the tests, shipping it, and the two that are not work at all: a stall, and waiting on a person.
The ledger is every call, with the gap before it and the time it took. a
toggles between the notable calls and all of them.
There is also --plain, which prints the whole thing as text — the right choice
when you are piping it somewhere or reading it in a diff.
One self-contained file. No fonts, no scripts, no images fetched from anywhere — you can mail it, commit it, or open it on a plane.
Open a session for its phases and its full ledger. Both sort on any column.
A generated one is committed at docs/demo-report.html —
clone and open it to click around before running anything.
Read this part. It is short, and it is the difference between using the numbers and misusing them.
- Why a gap was slow. A gap is wall-clock between two log lines. Inference, queueing, rate limits and your network are indistinguishable inside it. Treat "think time" as an upper bound on inference, never as a measurement of it.
- What anything cost in money. No pricing is bundled, and none is guessed.
- Anything outside Claude Code. Your editor, your build server, and your own thinking are all invisible here.
- Anything about a transcript that is no longer on disk. If a session was cleared, that time is simply not in the record, and nothing marks the hole. Every total is a floor.
- Whether the time was well spent. It shows you where time went. What that means about the work is a judgement it does not make.
- Durations under about a second, precisely. Timestamps are per-message, so short calls carry the log's own granularity as error.
tasktrace --capabilities prints all of this, plus what it does measure.
Your transcripts contain your prompts, your commands, your file contents and your tool output. So extraction is lossy on purpose: it derives a label and a duration, and throws the content away in the same pass.
Kept per call: a timestamp, a tool name, a duration, token counts, an error flag, and a short label. Labels are deliberately narrow —
| Tool | What survives |
|---|---|
| Bash | the program name only: pytest, git, make. Wrappers and their flag values are peeled off first, so sudo -u root pytest is pytest, not root |
| Read / Write / Edit | the file's basename, never a directory |
| Grep / Glob | the literal word pattern — that a search happened, never what for |
| Agent | the subagent type, never the prompt |
| WebFetch | the URL's host, never the path or query |
| assistant text | a character count, never the text |
Never kept: prompts, command arguments, file contents, tool output, absolute paths, search patterns.
A generated report is shareable as it stands. The test suite asserts this the blunt way: it plants secrets in a synthetic transcript — a credential, a home path, a password argument, a prompt, tool output — generates the full HTML report, and greps the whole file for every one of them.
Two flags move the line, in opposite directions:
--anonymizegoes further, replacing file basenames with stable pseudonyms (file-03.ts) and project names withproject-a. Stability is the point: the same file gets the same name every time, so this one file was rewritten eleven times is still visible. Use it when a filename is itself the thing you cannot share.--include-detailgoes the other way and keeps raw tool inputs. It prints a warning, and output made with it is exactly as sensitive as the transcript.
Everything comes from two subtractions between message timestamps:
exec assistant message requests a tool → the tool_result comes back
gap the previous event ends → this assistant message arrives
From those: think is every gap under five minutes, stall is every gap
over it with nothing running, human is time between the assistant finishing
and a person replying, and active is exec + think + stall. Within one
session they add up — exec + think + stall + human ≤ elapsed, always.
Across several sessions they deliberately do not. Sessions overlap (two agents on two branches, a subagent alongside its parent), so the totals are sums while the headline wall clock is a union — overlap counted once. Adding the elapsed times instead would claim the afternoon was nine hours long. When the sums exceed the clock, the output says so in as many words rather than quietly reconciling them.
reference.md in the skill has the full definitions, the phase-detection rules,
the classification tables, and every threshold.
| Flag | Does |
|---|---|
--days N |
only sessions active in the last N days |
--project NAME |
substring match on the project name |
--here |
only sessions started in this directory |
--html [FILE] |
write a self-contained HTML report |
--json FILE |
write the redacted trace as JSON |
--svg [FILE] |
photograph the explorer's first screen as an SVG |
--plain |
print a text summary instead of the explorer |
--open |
open the report when it is written |
--anonymize |
replace file and project names with stable pseudonyms |
--include-detail |
keep raw tool inputs (disables redaction) |
--demo / --demo-scale N |
run against generated sample data |
--menu |
force the interactive menu |
--capabilities |
what this can and cannot tell you |
python3 -m unittest discover -s plugins/tasktrace/skills/tasktrace/tests
python3 docs/shot.py # regenerate every screenshot in this READMEdocs/shot.py runs tasktrace in a real pty, plays the bytes through a small
terminal emulator, and writes the screen out as SVG. The pictures above are
therefore what the program printed, not mockups — which means they cannot quietly
drift from the code that made them.
padawan-no-more reads the same transcripts to answer the neighbouring question: not where the time went, but how often the agent stopped and asked you something, and which piece of configuration caused each stop. tasktrace measures the clock; padawan-no-more measures the interruptions.
MIT © Seyon Vasantharajan. Not affiliated with Anthropic.

