Skip to content

Stream child stdout live so long-running blocks don't look stalled#127

Merged
laat merged 1 commit intomasterfrom
stream-stdout
Apr 12, 2026
Merged

Stream child stdout live so long-running blocks don't look stalled#127
laat merged 1 commit intomasterfrom
stream-stdout

Conversation

@laat
Copy link
Copy Markdown
Owner

@laat laat commented Apr 12, 2026

Summary

  • Add options.stream to run(). When true, each child's stdout chunk is written to process.stdout as it arrives. Captured stdout is still accumulated into result.stdout for programmatic callers — streaming is additive, not destructive.
  • src/cli.js passes stream: true and drops the final process.stdout.write(stdout) since it would just duplicate what's already been streamed. stderr is still captured silently and written at the end (unchanged) so formatError can produce its pretty summary without being interleaved with raw Node error output.

Why

run.js used to buffer every child's stdout into a string, wait for the child to exit, return it, and only then let the CLI print it. Blocks that take a while — a network call, a long loop, or anything waiting on I/O — gave zero feedback until they finished. A user staring at a blank terminal has no way to tell the difference between "this is working" and "this is stuck."

The typical "progress" path is console.log, i.e. stdout. Streaming stderr too would mean raw Node error output followed by the formatted FAIL summary on every failure — extra noise in the common case — so stderr keeps the old buffered behaviour.

Test plan

  • New fixture test/fixtures/stream-delay.md prints STREAM-MARKER, sleeps 500ms, then asserts 1 //=> 1.
  • New test spawns the CLI via child_process.spawn, records when STREAM-MARKER first appears on stdout, then waits for exit. It asserts the marker arrived at least 200 ms before the child exited — i.e., the output is live, not buffered. The test takes ~577 ms locally (500 ms fixture sleep + overhead).
  • Verified the streaming test fails against the pre-fix sources (stashed-src sanity check) — without the change, the marker only appears after the child exits, so markerAt < total - 200 is false.
  • node --test test/*.test.js — 65/65 pass (64 before + 1 new).
  • pnpm -r test — all 10 workspace examples still green.

@laat laat merged commit 5dae262 into master Apr 12, 2026
2 checks passed
@laat laat deleted the stream-stdout branch April 12, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant