Skip to content

os dev still prints its supervisor output to stdout, so the diagnostics-on-stderr policy stops at two of three server commands #8066

Description

@hotlong

Observation found while fixing #7915 (PR #8065). Filed rather than folded in — nothing a user
hits today, recorded so the boundary is deliberate rather than accidental.

What is true after #7915

os serve and os start install redirectStdoutToStderr() at the top of run(): every human
line those two commands print — and everything the kernel or any package writes to
process.stdout — goes to stderr, unconditionally, because their stdout may be the MCP stdio
transport's JSON-RPC channel.

os dev does not. Its own supervisor lines (packages/cli/src/commands/dev.ts — the rebuild
notices, ↪ server bound to port …, ✓ server restarted — the new build is live, the MCP
connect hint) still go to stdout, and it spawns the serve child with stdio: ['inherit', 'inherit', 'inherit', 'ipc'] — so the parent's stdout IS the fd the child's transport would
write frames to.

Why it was left out of #7915

os dev is not a viable stdio MCP host with or without this: its restart supervisor tears the
child down on every successful rebuild, which drops any live stdio session. So there is no
configuration in which the parent's lines corrupt a session that would otherwise have worked —
the inconsistency is real, the user-visible defect is not.

What remains is a smaller thing worth a decision rather than a silent divergence:

  • three server-shaped commands, two of which treat their output as diagnostics and one of which
    does not — the next person to read dev.ts has no way to tell whether that is a decision;
  • os dev | grep and os serve | grep behave differently for the same-looking lines.

Options

  1. Install the same one-line redirect in dev.run() — consistent, and cheap. Costs: os dev's
    output moves to stderr for everyone who pipes it today.
  2. Leave it, and say so in dev.ts with a comment naming this issue.

No recommendation attached; both are defensible, and option 1 is a behaviour change for a
command people watch interactively all day.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions