Skip to main content
A run answers three questions at three different times: what will happen? (before), what is happening? (during), what happened? (after). One command each, all offline-friendly โ€” every frame below is real output from this workflow:
standup-digest.nika.yaml

Before ยท nika inspect

Static anatomy: the DAGโ€™s wave groups (what runs in parallel), plus three planning lines โ€” the parallelism width, the pinch points, and each taskโ€™s blast radius. Zero execution, zero tokens:
Read it before a long run the way you read a query plan before an expensive query.

During ยท the live render

In an interactive terminal, nika run animates a storyboard: tasks light up as their dependencies clear, in the wave order inspect predicted. Piped (CI logs, tee), the same run prints one plain, deterministic frame instead โ€” โˆฅ marks tasks that ran in parallel:
Four render modes, one per audience:

After ยท the waterfall and the verdict card

An interactive run ends with two frames. The waterfall shows where the wall-clock went โ€” overlapping bars are the parallelism you actually got. The verdict card compresses the run into one box: the wave shape (โ—†โ—† โ‡‰ โ—† โ€” two in parallel, then one), tasks, waves, retries, duration, cost (its โ‰ฅ/unpriced grammar is the cost-honesty vocabulary), model, and the typed outputs it produced:
The same frames are recoverable from any recorded trace, hours or machines later:
trace show prints the final frames; trace replay plays the recorded events through the same renderer that drew the original run. Replay calls no model and runs no command โ€” it is a projection of what already happened (Traces & replay).

Export to your tracing stack ยท OTLP

A recorded trace projects to OTLP/JSON lines, so any OpenTelemetry viewer becomes a Nika run viewer โ€” no collector, no daemon, no vendor:
Each verb becomes a span (duration, tokens, the ADR-099 identity hashes). An infer: or agent: task also carries the model it ran on, in the standard OpenTelemetry GenAI semantic conventions: gen_ai.provider.name, gen_ai.request.model, gen_ai.response.model โ€” the current names, so any GenAI-aware viewer (Grafana, Langfuse, Phoenix) reads which model produced which output with no translation. Drag the file into the Jaeger UI (โ‰ฅ 1.60), or POST the lines to any OTLP/HTTP endpoint. Task outputs stay local by default; --include-content widens the exported file to carry them as span attributes when you explicitly want them. The export is a projection of the journal โ€” it never re-runs anything.
Because the model, provider, tokens, cost, and status ride every runโ€™s own trace, your runs are your own analytics store โ€” query them with jq, feed them to any OTel-native eval tool, on files you own, with no vendor and no cloud. (A first-class --format dataset export for fine-tuning / eval sets is on the roadmap โ€” it waits on a content-policy decision, since a training set needs the input prompts a trace does not capture by default.)

Terminals & CI

The live surfaces are TTY-only by design. Piped or NO_COLOR output is the sober register (plain per-line events โ€” what CI logs want). Two render themes ship: unicode (default) and --ascii (every glyph has an ASCII twin โ€” use it on terminals without good box-drawing fonts, including older Windows setups; Windows Terminal renders the default theme fine). --color never (or --plain), NO_COLOR, and CLICOLOR_FORCE follow the standard resolution order โ€” one global colour chain, no per-verb twins.