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

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.

Where the time went 4 demo runs · last 7 days

AGENT WORKING

Model work 1h 04m 21%
Read & search 34m 11%
Code changes 1h 10m 23%
Other tool work 6m 2%

BLOCKED ON COMPUTE

Waiting for build 32m 11%
Waiting for tests 29m 10%
Waiting for CI 24m 8%
Waiting for containers 13m 4%
Waiting for agents 12m 4%
Waiting for packages 7m 2%
Repeated build / test 8m 3%

NOT COUNTED

Outside the agent loop 48m
Agent time · blocked on compute 4h 59m · 2h 05m (42%)

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

HarnessLocal sourceResolution
Codex~/.codex/sessionsper event
Claude Code~/.claude/projectsper event
Cursor~/.cursor/projects/*/agent-transcriptsper turn
Grok Build~/.grok/sessionsper 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