Repository navigation
fix(wizard): refuse to run without a terminal instead of crashing - #73
Merged
Merged
Conversation
Piping the wizard — a CI job, `seam wizard < /dev/null`, any
non-interactive shell — printed a React stack trace out of Ink's
internals:
ERROR Raw mode is not supported on the current process.stdin, which
Ink uses as input stream by default.
at commitHookPassiveMountEffects (react-reconciler.development.js)
…
The welcome splash already handles a missing TTY (it skips straight to
init), but `ink-select-input` calls `useInput` unconditionally, and Ink's
`setRawMode` throws when stdin is not a TTY. So the run died on the first
prompt it rendered, mid-render, after having done real work: it had
already connected the workspace and started installing the SDK.
Nothing can be done about that at the prompt, because every run reaches
one — the connect method, the SDK, the offer to write the integration —
so there is no headless path through the wizard to preserve. Check for a
TTY at the entrypoint instead and explain what is wrong:
Seam Wizard needs an interactive terminal.
It asks which account to connect, which SDK to install, and whether
to write the integration, so it cannot run with stdin redirected —
in a pipe, a CI job, or a non-interactive shell.
Run 'seam wizard' in your terminal instead.
To stderr, with a non-zero exit code, and no render at all. The check
sits after `--help` and `--version`, which are the documented ways to run
this without a terminal and keep working.
Only stdin is required: a run with stdout redirected still works, since
render.ts already skips the alternate screen there and leaves its frames
in the log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
itelo
force-pushed
the
itelo/wizard-requires-tty
branch
from
September 3, 2026 17:28
3970038 to
ad802ed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found while verifying analytics end to end: running the wizard with stdin redirected prints a React stack trace out of Ink's internals instead of anything a developer can act on.
The failure
The welcome splash already handles a missing TTY — it skips straight to
init— butink-select-inputcallsuseInputunconditionally, and Ink'ssetRawModethrows when stdin is not a TTY (ink/build/components/App.js:211). So the run died mid-render, on the first prompt, after doing real work: in my repro it had already verified the key, connected the workspace, and started installing the SDK before it blew up.The fix
There is no headless path through the wizard worth preserving: every run reaches a prompt — the connect method, the SDK, the offer to write the integration — so a non-interactive run can never finish, whatever we do at the prompt. Check for a TTY at the entrypoint and say what's wrong:
To stderr, with exit code 1, and no render at all. The message uses the host's
commandName, so mounted under the Seam CLI it saysseam wizard.Two deliberate boundaries:
--helpand--version, which are the documented ways to run this without a terminal and keep working (covered by tests).seam wizard | tee logstill runs:render.tsalready skips the alternate screen when stdout is redirected and leaves its frames in the log.Testing
test/wizard.test.ts— the refusal (message, no render, exit code), and--help/--versionstill working without a TTY. The suite now makes stdin look like a terminal inbeforeEach, since that is the normal case for the eight existing tests that expect a render; the refusal tests opt out.process.exitCodeis reset inafterEachso the refusal doesn't fail the run.exit 1with no stack trace; under a pty (script -q /dev/null npm run wizard) the guard doesn't fire and the wizard renders as before.npm run typecheck,npm run lintandnpm test(197 tests) pass.🤖 Generated with Claude Code