Progress output
monochange writes progress information to stderr so stdout can remain stable for text, markdown, and JSON command results.
Selecting a renderer
Use the global --progress-format <FORMAT> flag or set MONOCHANGE_PROGRESS_FORMAT.
Supported values:
auto: default behavior. Deterministic human progress output is enabled for terminals, CI logs, editor tasks, and captured processes; animation is terminal-only.unicode: force the human renderer with Unicode symbols and spinners.ascii: force the human renderer with ASCII-safe symbols.json: emit newline-delimited JSON progress events on stderr.
--quiet suppresses progress output. MONOCHANGE_NO_PROGRESS=1 also disables the automatic human renderer.
Human progress output
The human renderer is designed for interactive terminal runs:
- configuration loading and validation report their active phase before work begins
- step labels use each step’s
name = "..."value when present, then fall back to the built-in step kind - long-running steps show a delayed spinner so short steps do not flicker
- command stdout and stderr stream under the active step: the step name is written once as a block header, then every captured line is indented beneath it
- stdout and stderr interleave in arrival order without stream tags; use
--progress-format jsonwhen a consumer must tell the two streams apart - completed
PrepareReleaseandDisplayVersionssteps print per-phase timings so slow phases are visible without a separate trace
Captured command output looks like this, with the step named once instead of on every line:
▶ [2/7] format release files (Command)
▶ [2/7] format release files (Command) — running command `devenv tasks run format`
│ format release files
│ • Validating lock
│ • Validating lock in 2.35ms
✔ [2/7] format release files (Command) 398ms
The same events become complete, newline-terminated records when stderr is captured or monochange runs in CI. Lint and publish operations share the workflow reporter, so a nested operation cannot create a second spinner or append text to an active line. Publish progress uses the same symbols, colors, and ASCII fallback as workflow progress.
Built-in commands already attach descriptive step names such as prepare release, publish release, and open release request. Custom commands can override those names per step.
JSON event stream
--progress-format json is intended for machines, not humans. It writes one JSON object per line to stderr.
Common lifecycle events:
phase_startedphase_finishedphase_failedcommand_startedstep_startedcommand_outputstep_finishedstep_failedstep_skippedcommand_finishedcommand_failedlint_planning_startedlint_planning_finishedlint_suite_startedlint_suite_finishedlint_fix_startedlint_fix_appliedlint_fix_finishedlint_summarypublish_run_startedpublish_registry_check_startedpublish_package_startedpublish_package_skippedpublish_package_plannedpublish_package_publishedpublish_package_failedpublish_run_finished
Shared fields:
sequence: monotonically increasing event sequence number for the command runcommand: CLI command name, such asreleasedry_run: whether the command is running in dry-run modetotal_steps: total step count for the commandstep_index: 1-based step index for step eventsstep_kind: built-in step kind, such asPrepareReleasestep_display_name: rendered human label for the stepstep_name: explicit configuredname, ornullwhen omitted
Event-specific fields:
command_outputaddsstreamandtextphase_finished,step_finished, and command completion events addduration_msstep_finishedaddsphase_timingsstep_failedaddsduration_msanderrorstep_skippedmay add the backward-compatibleconditionfield for conditional skips andreasonfor a human-readable explanationcommand_failedaddsduration_msanderror
Example:
{"sequence":0,"event":"phase_started","phase":"Loading workspace configuration"}
{"sequence":1,"event":"phase_finished","phase":"Loaded workspace configuration","duration_ms":12}
{"sequence":2,"event":"command_started","command":"release","dry_run":true,"total_steps":2}
{"sequence":3,"event":"step_started","command":"release","dry_run":true,"step_index":1,"total_steps":2,"step_kind":"PrepareRelease","step_display_name":"plan release","step_name":"plan release"}
{"sequence":4,"event":"step_finished","command":"release","dry_run":true,"step_index":1,"total_steps":2,"step_kind":"PrepareRelease","step_display_name":"plan release","step_name":"plan release","duration_ms":243,"phase_timings":[{"label":"discover release workspace","duration_ms":97}]}
Failure diagnostics and maintainer tracing
Normal failures use a stable diagnostic code and put the useful recovery information first:
error[cli.json_required]: --jq requires explicit JSON output
command: monochange step config
help: Add `--format json` or `--format json-min` before using `--jq`.
Use the code when searching CI logs or reporting a recurring failure. The diagnostic includes command or path context when monochange knows it and a next action when it can recommend one.
--log-level <FILTER> enables local maintainer tracing, for example --log-level debug or --log-level monochange=trace. Tracing is opt-in, may include internal spans and implementation detail, and is not the normal user-facing explanation for a failure. Animation is disabled while tracing is active so trace records and progress lines remain readable. This flag does not enable remote telemetry.
Benchmark integration
The binary benchmark workflow uses --progress-format json to extract PrepareRelease phase timings for both monochange run release --dry-run and monochange run release.
Those timings are summarized and compared against scripts/benchmark-phase-budgets.json, which lets pull requests fail when real release-path regressions exceed the configured budget.
For hosted-provider analysis outside CI, pnpm node scripts/benchmark-cli.ts run-fixture can benchmark an existing repository checkout and render the same markdown summary against a real hosted fixture. See Hosted release benchmarks.