Local wall-clock profiler
WASTED CYCLES
Find the machines your coding agent is waiting on.
A wasted cycle is time your agent spends blocked on compute it does not control — compiling, running tests, waiting on CI, provisioning containers, fetching packages, joining sub-agents. Wasted Cycles reads the traces already on your machine and shows how much of a run was that, and which machine to fix first. Time spent waiting on a person is not a wasted cycle: it is reported separately and never enters the metric.
$ curl -fsSL https://raw.githubusercontent.com/zozo123/wasted-cycles/main/run | sh
No install, no account, no daemon, no API key, no upload. The runner downloads a checksum-verified binary into a temporary directory, runs it, and deletes it on exit.
What it answers
- How much of a run was spent blocked on a machine instead of coding?
- Which one — the compiler, the test suite, CI, containers, or the registry?
- Is the same build or test running more than once?
- Which coding harness keeps moving on your workload?
- What is the highest-impact bottleneck to fix next?
What the output looks like
Built-in demo dataset — not a real measurement
Every number below comes from the synthetic dataset shipped with
--demo, reproduced here in HTML. It exists to show the shape of the
report, not to make a claim about any harness. Run the profiler on your own traces
to get real figures.
AGENT WORKING
BLOCKED ON COMPUTE
NOT COUNTED
Blocked on compute is what this tool measures · human time is shown but never counted
In the terminal the same data is a live TUI: arrow keys switch views, 7 /
0 / y change the lookback window, and q quits.
Supported trace roots
| Harness | Local source | Resolution |
|---|---|---|
| Codex | ~/.codex/sessions | per event |
| Claude Code | ~/.claude/projects | per event |
| Cursor | ~/.cursor/projects/*/agent-transcripts | per turn |
| Grok Build | ~/.grok/sessions | per session |
JSONL trace files modified inside the selected window are read in place. Nothing is written back, and nothing leaves the machine.
Cursor only stamps wall-clock time on user turns, so each segment spans a whole turn. Scheduled Cursor agents that tick on a fixed interval are detected and dropped — the pattern that once inflated a session to 120 hours of phantom work.
Method & limits
The profiler reconstructs elapsed segments between timestamped trace events and classifies each segment by the structured action that opened it: the tool that was called, the command that tool ran, or the message that ended a turn. It reads parsed event structure rather than matching text, so a pasted log or a quoted command in a prompt cannot be mistaken for real activity. It measures wall-clock time, not tokens and not cost.
Every segment lands in one of three groups: agent working (model work, reads, edits, other tool calls), blocked on compute (build, tests, CI, containers, packages, sub-agents, repeated work), and not counted (waiting on a human). Agent time is working plus blocked; throughput is the share of agent time that was not spent waiting on a machine. Human time sits beside those numbers so you can see it, and outside them so it cannot distort them.
A build, test, or CI command that runs more than once in a session is reclassified as repeated work, because the machine did the same job twice.
Harnesses record different things. Codex and Claude Code stamp individual events.
Cursor transcripts only stamp wall-clock time on user turns, so its runs are
reconstructed per turn and each turn takes the most blocking tool category observed
inside it; those runs are marked turn on the Runs screen and carry lower
confidence. Scheduled Cursor agents that tick on a fixed interval are dropped. Grok Build
records only a session start and end, so a Grok run collapses to a single coarse block
and is marked turn too. Records the profiler cannot identify are skipped
instead of guessed.
Idle time uses two thresholds, because a wait and a walk-away are not the same thing.
A gap longer than 2 hours is a session break and is not counted at
all, so closing the laptop overnight cannot show up as “waiting for human”.
A shorter gap is capped at 30 minutes, and those segments are marked
clamped with low confidence. The clamped share of the report is stated
on the Method screen and emitted as inferred_ns, so measured time and
inferred time stay distinguishable.
“Model work” is an inference proxy, not measured GPU compute
time. It is the interval after a user message or a tool result and before the next
emitted action. Harnesses expose different levels of timing detail; unless a trace
records an exact duration, that interval is an upper bound on real inference. The
Method screen in the TUI states this outright, and --json emits a
per-segment confidence score so you can weigh each classification
yourself.
Prompt text and source code are never stored, rendered, or uploaded. Only timestamps, event kinds, and coarse labels are used.
Flags
--demoOpen the TUI with the built-in synthetic dataset — no traces required.--days NSize of the window to scan, in days. Defaults to 7.--ytdScan from January 1 of this year.--jsonPrint the report as JSON instead of opening the TUI.--plainPrint a plain-text summary instead of the TUI. Used automatically when output is piped or redirected.--no-alt-screenRender without the terminal alternate screen.--versionPrint the version and exit.
In the TUI, press 7, 0, or y (or [ /
]) to switch between 7d, 30d, and YTD without restarting.
$ curl -fsSL https://raw.githubusercontent.com/zozo123/wasted-cycles/main/run | sh -s -- --demo $ curl -fsSL https://raw.githubusercontent.com/zozo123/wasted-cycles/main/run | sh -s -- --days 30 $ curl -fsSL https://raw.githubusercontent.com/zozo123/wasted-cycles/main/run | sh -s -- --ytd --plain $ curl -fsSL https://raw.githubusercontent.com/zozo123/wasted-cycles/main/run | sh -s -- --json