Skip to content

fix(cli): stream stdin/stdout for --run so interactive TUIs work#133

Merged
MelbourneDeveloper merged 1 commit into
mainfrom
fix/run-stdin-interactive-tui
Jun 1, 2026
Merged

fix(cli): stream stdin/stdout for --run so interactive TUIs work#133
MelbourneDeveloper merged 1 commit into
mainfrom
fix/run-stdin-interactive-tui

Conversation

@MelbourneDeveloper
Copy link
Copy Markdown
Owner

Interactive --run was broken for TUIs (instant quit)

osprey <file> --run ran the compiled program through the capture path
(runRunProgram → CompileAndCapture → executeProgramWithCapture, i.e.
exec.Cmd.Output()). That path never wired the child's stdin, so Go pointed
it at /dev/null: the first termReadKey() / input() read hit EOF and any
interactive loop exited immediately. It also buffered stdout until exit, so a
TUI's frames only appeared after the program had already quit.

That's exactly the reported symptom — examples/tui/api_browser.osp printed one
frame and then Goodbye! without accepting a single keystroke.

Fix

Switch CLI run mode to the streaming path (CompileAndRun → executeProgram),
which now wires Stdin, Stdout, Stderr straight through. executeProgram
already streamed stdout/stderr; it was only missing Stdin, now added.

  • Non-interactive use is unchanged: compilation diagnostics stay on stderr, and
    run-mode tests still see an empty CommandResult.Output.
  • The integration harness is unaffected — it drives programs via
    captureJITOutput → CompileAndRunJIT (os.Stdout redirection), not the CLI
    capture functions.

Verification

  • Real PTY E2E (scripted keystrokes Down/Down/Enter/back/Up/q): the selection
    arrow moves across repos (osprey → llvm → antlr), Enter opens the detail
    view (antlr description renders), and the app exits only on q — never
    instantly on startup.
  • make test (lint + integration + unit + coverage) and run-mode unit tests all
    pass.

`osprey <file> --run` executed the compiled program through the capture
path (runRunProgram -> CompileAndCapture -> executeProgramWithCapture,
which calls exec.Cmd.Output()). That path never wired the child's stdin,
so Go connected it to /dev/null: the first termReadKey() / input() read
returned EOF and any interactive loop exited immediately. It also
buffered stdout until exit, so a TUI's frames only appeared after the
program had already quit. That is why api_browser.osp printed one frame
and then "Goodbye!".

Switch the CLI run mode to the streaming path (CompileAndRun ->
executeProgram) which now wires Stdin, Stdout and Stderr straight
through, giving interactive programs a live stdin and incremental
output. executeProgram already streamed stdout/stderr; it was missing
Stdin, now added. Compilation diagnostics stay on stderr, so captured
stdout for non-interactive use is unchanged and the run-mode tests still
see an empty CommandResult.Output.

The integration test harness is unaffected: it drives programs through
captureJITOutput -> CompileAndRunJIT (os.Stdout redirection), not the
CLI capture functions.

Verified end to end by driving examples/tui/api_browser.osp through a
real PTY with scripted keystrokes: arrow keys move the selection across
repos, Enter opens the detail view, and the app only exits on 'q' -
never instantly on startup. Full `make test` and lint pass.
@MelbourneDeveloper MelbourneDeveloper merged commit 76f9f1e into main Jun 1, 2026
2 checks passed
@MelbourneDeveloper MelbourneDeveloper deleted the fix/run-stdin-interactive-tui branch June 1, 2026 10:23
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