Listing runs
spektacular stats list reads the project’s logs, .spektacular/logs/ by
default, and shows each workflow run they hold, newest first: its kind,
workflow name, agent, start time, wall time and status.
RUN KIND NAME AGENT STARTED WALL STATUS4bf92f35 implement 000075_run-stats-command claude 2026-10-25 09:10:02 41m12s in_progress9d0e0e47 plan 000075_run-stats-command claude 2026-10-25 08:20:50 41m21s finishedA run is finished once it reached its last step, and interrupted when it
was restarted or a later run of the same workflow started after it. Any
other run is in_progress. The RUN column shows the first 8 characters of
the run id, which the other commands accept with --run as long as no
other run’s id starts the same way. Commands
run outside any workflow are not runs, and are not listed.
Reporting on one run
spektacular stats run reports on the run that started most recently. It
gives the run’s totals, then a table per step, the plan tasks of an
implement run, every helper agent launched, every wait on the user, and the
longest gaps between recorded events.
plan 000075_run-stats-command agent claude run 9d0e0e47 finished started 2026-10-25 08:20:50
Wall time 41m21sWorking time 30m03sUser wait 11m18s (2 waits)Helper wait 8m32s (3 helpers)Command time 4.1s (61 commands)Gaps over 5m00s 25m20s (3 gaps)
StepsSTEP ENTRIES DURATION COMMANDS CMD TIME HELPER WAIT USER WAIT INCOMPLETEdiscovery 2 13m32s 9 0.6s 6m42s 0.0s 1architecture 1 9m10s 6 0.4s 1m50s 4m05s 0The report goes on with the Tasks table (when the run has plan tasks, as
an implement run does), Helpers
and User waits (each with its name, step, start, duration and how it
ended) and Longest gaps (each with its length, its step and the events
either side). --gaps 3 lists 3 gaps instead of the default 10, and
--gap-threshold 30m totals only the gaps longer than 30 minutes instead of
5.
To see the same run as a picture, render it with the run viewer.
Choosing runs
Every stats command takes the same selection, and the options combine:
a run must match all of them.
--name <workflow name>: runs of one spec, plan or implement workflow.--kind spec|plan|implement|repo: runs of one kind.--agent <agent>: runs by one agent.--run <id>: one run, by its full id or its first 8 or more characters. Repeat it to name several runs.
With no selection, stats run takes the latest run, stats compare takes
the two latest runs of that run’s kind, and stats summary takes every run
of that kind. A prefix that matches more than one run is refused, with the
runs it matches.
Comparing runs
spektacular stats compare --run 9d0e0e47 --run 4bf92f35stats compare puts runs side by side in the order given, the first one as
the base. Each step’s duration is shown for every run, with each later run’s
difference from the base in time and percent, and the totals follow in the
same form.
STEP 9d0e0e47 2026-10-25 4bf92f35 2026-10-25interview 13m32s 10m01s (-3m31s, -26.0%)split 4m10s -A step a run never entered shows as -, not 0s, and a difference against
a base of zero has no percentage.
Summarising runs
spektacular stats summary --kind implementstats summary gives, for each step and for the totals, the number of runs
it covers, the median and the 90th percentile. A step only some runs entered
shows how many did. The 90th percentile is the nearest-rank value, so it is
always a figure one of the runs had.
Listing commands and spans
spektacular stats commands lists every command Spektacular recorded,
oldest first: the run and step it ran in, the step it moved to, its
arguments, how long it took and its exit code. spektacular stats spans
lists every helper agent, wait on you and other marked span, with its step
and how it ended.
spektacular stats commands --name 000075_run-stats-commandspektacular stats spans --kind implement --format jsonspektacular stats commands --outside-runs # what an epic orchestrator ranBoth take the same --name, --kind, --agent, --run, --dir and
--format as the other commands. With no selection they list everything,
in runs and outside them; with a selection, only the selected runs’ records.
--outside-runs shows only what was recorded outside any workflow run, such
as the commands an epic orchestrator ran and the helpers it launched, and
cannot be combined with a run selection. In JSON, commands carry run_id,
step, step_to, args, exit_code and message, and spans carry
type, run_id, workflow, step, task_id, end_reason and
complete. Outside any run, run_id and workflow are null. A span that
was never stopped is marked as unfinished, as in a run report.
What each figure means
- Wall time: from the run’s first recorded event to its last. A run still in progress is measured to its latest event.
- Working time: wall time less user wait.
- User wait: time a
user-waitspan was open and finished, counted once when several overlap. - Helper wait: time at least one
sub-agentspan was running, counted once when helpers overlap, so it never adds up to more than the wall time. - Command time (
CMD TIMEper step): the time Spektacular’s own commands took. It stands in for tool time; the logs hold no model time. - Commands: how many Spektacular commands ran, in the whole run or, per step, in that step. The count beside command time in the totals is the same figure.
- Duration (per step or task): the time from entering it to leaving it, added up over every entry.
- Entries: how many times a step was entered.
- Incomplete: how many of a step’s entries did not end by moving on to
the next step or finishing the run, such as an
interruptedentry or the step a run is still in. - Helpers and waits (the counts beside helper wait and user wait): how many helper agents were launched and how many times the user was waited on, finished or not.
- Gap: a stretch with no recorded event and no command running,
labelled with the step it fell in and the events either side. Time spent
waiting on a helper or on you is often a gap whose earlier event is the
startof that span, so the labels tell waiting from idle. - Gap total: the time in gaps longer than the threshold (5 minutes by default). The count beside it is how many gaps were longer than the threshold.
Unfinished work and damaged lines
Work that was interrupted, abandoned or never closed (open) is
flagged. In the tables, a task, helper or wait on the user that did not
finish has a * after how it ended, and a step’s INCOMPLETE column counts
its entries that did not finish. In the JSON, every step entry
(step_entries), task, helper and wait carries its end_reason and
"complete": false when it did not finish, and each step row carries the
same incomplete count. An interrupted step counts up to where it was cut
off. Unfinished helpers and waits are listed, but left out of every
wait total. A restart that closes a run’s leftover spans days later does not
stretch the run: those closing times are not counted as events.
A line that cannot be read, such as a partly written last line, is skipped
and counted, and leaves every other figure unchanged. The JSON gives the
count as skipped_lines, and the tables end with Skipped lines: N when any
line was skipped.
Tokens and cost
For runs driven by Claude Code, every report also shows how many tokens each part of the run used, what that cost, and how much working time it took per thousand output tokens, beside its timings. The Steps, Tasks and Helpers tables gain three columns:
STEP ENTRIES DURATION ... OUT TOKENS COST TIME/1K OUTdiscovery 1 12m04s ... 48,210 3.12 15.0sarchitecture 1 9m40s ... 2,105 0.41 4m35sArchitecture took almost as long as discovery but did a twentieth of the
work, so it is the step to look at. After the Steps table, a Tokens block
gives each step’s input, output, cache-read and cache-write tokens and cost,
the tokens used outside any step and the run total, then a Models table
with each model’s tokens and cost, and any unmatched helpers.
How a run finds its session
Spektacular never sees the agent’s model requests, so it reads Claude
Code’s own session files in ~/.claude/projects (or
$CLAUDE_CONFIG_DIR/projects when that is set). It looks at the sessions
active since the run started, finds the one whose shell commands ran this
run’s own new and goto commands, and reads that session’s helper agents
too. A run resumed in a second session is read from both, and an
orchestrated run is found in its own helper’s transcript. The agent does
nothing extra for this, and nothing has to be set up.
spektacular stats run --sessions-dir path/to/claude/projects--sessions-dir reads Claude Code session files from another folder, such
as a copy taken from another machine. Reading never changes those files.
What the token figures mean
- Input, output, cache read, cache write: the four token types the model reports for each request.
- Steps and tasks: each request counts in the step and task that were running when it was made.
- Helpers: a helper’s tokens, including any helper it started, count on its launch and in the step and task it was launched in, even if it finishes after that step has ended. A helper whose launch Spektacular never recorded is still counted, in the step it started in, and listed as unmatched.
- Outside any step: requests made before the run’s first step, between the steps of an interrupted run, or after it finished, in the same session. The step figures plus this figure always equal the run total.
- Session overhead: tokens Claude Code counted for the session that its transcript never records, such as permission checks. It is known only when the session drove just this run and Claude Code has written its own totals, and it is part of “outside any step”. Otherwise the run total covers the recorded requests only, and the report says why.
- Models: each model the run used, with its tokens and cost.
- Time per 1k output tokens (
TIME/1K OUT): working time per thousand output tokens. For the run it is the run’s working time; for a step or a task, its duration less any wait on you; for a helper, its own duration, so helpers running at the same time do not affect each other. It shows asn/afor a part with no output tokens.
Setting prices
No prices are built in, because they change. Give each model’s price per
million tokens in .spektacular/config.yaml:
stats: prices: # per million tokens claude-opus-5-5: input: 5.00 output: 25.00 cache_read: 0.50 cache_write: 6.25Keys are model names exactly as the report lists them, and all four prices
are required. Costs are in whatever currency the table uses. A model with
no price has an unknown cost, never zero. A total that includes one shows
the cost of the priced part, marked incomplete: in the tables with a *
after the cost and a note naming the unpriced models, and in the JSON with
"cost_complete": false and the models in unpriced_models. With no price
table at all, every cost is unknown.
When tokens are unknown
When token use cannot be read, the token figures show as unknown with a
reason, and every timing stays as it would be without them. The tables show
? in the token columns and one line in the Tokens block; the JSON gives
"status": "unknown" with reason_code and reason. The reasons are:
agent_unsupported: only Claude Code sessions are read today, so runs by Bob, Codex and other agents show unknown.sessions_dir_missing: the Claude Code projects folder does not exist.session_not_found: no session ran this run’s commands.session_unreadable: a session file could not be read.format_unrecognised: a session file holds nothing Spektacular recognises, for example after a change to Claude Code’s format.
Comparisons show such a run as - for the token figures, and summaries
leave it out of their counts.
Reports for programs
--format json gives the same report in the standard envelope. An abridged
example:
{ "error": false, "log_dir": "/home/me/project/.spektacular/logs", "skipped_lines": 0, "run": { "run_id": "9d0e0e4736a3ce929d0e0e47364bf92f", "kind": "plan", "wall_ms": 2481277, "status": "finished" }, "totals": { "wall_ms": 2481277, "working_ms": 1803000, "user_wait_ms": 678277, "helper_wait_ms": 512000, "gap_ms": 1520000 }, "steps": [{ "step": "discovery", "entries": 2, "duration_ms": 812000 }]}Durations are integer milliseconds, times are RFC 3339 in UTC, and a figure
that does not apply is null, never 0. These field names are stable, and
--fields narrows the output as for any other command.
Token use comes as a tokens object on each steps, tasks and helpers
row, and a top-level tokens section:
"tokens": { "status": "known", "total": { "input": 1442, "output": 641466, "cache_read": 158466888, "cache_write": 2554795, "cost": 43.18, "cost_complete": true, "unpriced_models": [], "working_ms_per_1k_output": 2811 }}The section also holds sessions, outside_steps, session_overhead,
models and unmatched_helpers. Comparisons and summaries add
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
cost and working_ms_per_1k_output after their existing figures, for the
totals and each step.
stats commands and stats spans give their records the same way, in a
commands or a spans array whose field names are stable too.
Logs from elsewhere
spektacular stats run --dir path/to/collected/logs--dir reads a logs directory copied from anywhere, such as a test run’s,
from any folder, with no project needed, and gives the same figures as
reporting in the project. Reporting never changes a log, and stats
commands are never logged themselves.