Skip to content

fix(stdin): show TTY hint when reading from interactive terminal#146

Open
Emp1500 wants to merge 1 commit into
google-labs-code:mainfrom
Emp1500:fix/stdin-tty-hint
Open

fix(stdin): show TTY hint when reading from interactive terminal#146
Emp1500 wants to merge 1 commit into
google-labs-code:mainfrom
Emp1500:fix/stdin-tty-hint

Conversation

@Emp1500

@Emp1500 Emp1500 commented Jun 28, 2026

Copy link
Copy Markdown

Problem

When a user runs a design.md command with - as the file path from an
interactive terminal, the process hangs silently with no output:

$ design.md lint -
█  ← cursor blocks here indefinitely

There is no indication that the tool is waiting for input, or that the user must press Ctrl+D to signal end-of-file. A user unfamiliar with this Unix convention has no feedback to distinguish a stalled process from one that is working correctly.

Root Cause

readInput reads stdin via for await (const chunk of process.stdin) with no TTY detection. When stdin is an interactive terminal
(process.stdin.isTTY === true), the loop blocks until EOF — but nothing prompts the user to send one.

Solution

Before entering the read loop, check whether stdin is a TTY and, if so, emit a one-line hint to stderr:

Piped usage is completely unaffected — isTTY is false for pipes, so the hint never appears and existing behavior is unchanged:

cat DESIGN.md | design.md lint -   # works exactly as before

Design Decisions

  • readInput now accepts an optional stdin stream parameter that defaults to process.stdin. All existing call sites remain unchanged.
  • The optional parameter makes the TTY code path fully unit-testable via injected mock streams, without any need to mock globals.
  • StdinStream is exported so test files can type mock streams without duplicating the interface definition.

Changes

File Change
utils.ts Export StdinStream type; add optional stdin param to readInput; add TTY check before read loop
utils.test.ts 4 new tests: piped content, empty stream, TTY hint emission, non-TTY silence — all using injected mock streams

When a user runs a command with "-" as the file path from an interactive
terminal (e.g. design.md lint -), the process blocks silently waiting
for EOF with no indication of what to do.

Add a TTY check before the stdin read loop. If stdin is attached to a
terminal, write to stderr:

  Reading from stdin… Press Ctrl+D when done.

The stdin stream is accepted as an optional second parameter on readInput
(defaulting to process.stdin), making the TTY path fully testable via
dependency injection without touching process.stdin directly.

Also exports StdinStream so callers can type mock streams without
duplicating the definition.
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