fix(loop): guard the top-level --interactive shortcut with a TTY check - #529
Merged
cobusgreyling merged 1 commit intoAug 17, 2026
Conversation
cmdWizard() (loop wizard --interactive) already gates the interactive path on process.stdin.isTTY, falling back to printNonInteractiveHelp() otherwise. The top-level `loop --interactive` shortcut in main() never had the same guard: it called runInteractiveWizard() unconditionally. readline's question() resolves immediately with '' when stdin isn't a TTY (a CI step, piped input, `< /dev/null`) instead of waiting for a human. Without the guard, `loop --interactive` in a non-interactive context silently ran the wizard with every prompt answered blank, then executed the resulting default plan -- `loop init . --pattern daily-triage --tool grok` against the current directory -- scaffolding files nobody asked for instead of printing help. Mirrors cmdWizard()'s existing gate at the other --interactive entry point. Test plan: added a regression test that runs `loop --interactive` with stdin closed (execFileSync's input: '', matching `< /dev/null`) in an empty temp directory, asserting it prints the non-interactive help and the directory stays empty. Confirmed it fails with the wizard prompts printed and no help text when the guard is stripped, and passes with the fix. Full clean rebuild + npm test: 11/11 passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cobusgreyling
approved these changes
Aug 17, 2026
cobusgreyling
left a comment
Owner
There was a problem hiding this comment.
Mirrors the existing cmdWizard() TTY gate. Non-TTY loop --interactive was silently running the default init plan; help + no scaffold is the right behavior. Regression test with closed stdin is solid.
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.
Problem
cmdWizard() (loop wizard --interactive) already gates the interactive
path on process.stdin.isTTY, falling back to printNonInteractiveHelp()
otherwise. The top-level
loop --interactiveshortcut in main() neverhad the same guard: it called runInteractiveWizard() unconditionally.
readline's question() resolves immediately with '' when stdin isn't a
TTY (a CI step, piped input,
< /dev/null) instead of waiting for ahuman. Without the guard,
loop --interactivein a non-interactivecontext silently ran the wizard with every prompt answered blank, then
executed the resulting default plan --
loop init . --pattern daily-triage --tool grokagainst the current directory -- scaffoldingfiles nobody asked for instead of printing help.
Fix
Mirrors cmdWizard()'s existing gate at the other --interactive entry
point.
Test plan
Added a regression test that runs
loop --interactivewith stdinclosed (execFileSync's input: '', matching
< /dev/null) in an emptytemp directory, asserting it prints the non-interactive help and the
directory stays empty. Confirmed it fails with the wizard prompts
printed and no help text when the guard is stripped, and passes with
the fix. Full clean rebuild + npm test: 11/11 passing.