fix(init): stop specify init hanging on arrow-key pickers in agent harnesses - #4178
Merged
Merged
Conversation
…rnesses Agent harnesses often allocate a PTY so isatty is true, but they cannot send arrow keys. Fail fast when stdin is not a TTY, and add --non-interactive so scripted init applies defaults instead of hanging. Fixes github#4152.
lllakshit
force-pushed
the
fix/4152-noninteractive-init-hang
branch
from
August 17, 2026 19:32
d32f3cb to
8c57587
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Prevents specify init from hanging on arrow-key prompts in automated environments.
Changes:
- Adds non-TTY fail-fast behavior to the shared selector.
- Adds
--non-interactivewith safe defaults and prompt suppression. - Adds tests and user documentation.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/_console.py |
Adds non-TTY detection and actionable errors. |
src/specify_cli/commands/init.py |
Implements non-interactive initialization. |
tests/test_console_imports.py |
Tests selector fail-fast behavior. |
tests/test_live_transient_windows.py |
Adapts selector tests for TTY detection. |
tests/integrations/test_cli.py |
Tests non-interactive initialization scenarios. |
README.md |
Documents automation usage. |
docs/quickstart.md |
Explains non-interactive defaults. |
docs/local-development.md |
Clarifies script selection behavior. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Balanced
mnriem
requested changes
Aug 17, 2026
mnriem
left a comment
Collaborator
There was a problem hiding this comment.
Please address Copilot feedback
…trust Cover the HTTPS --extension confirmation path when stdin is a TTY: deny without --trust-extension-urls, and install with it, both without calling typer.confirm.
Contributor
Author
|
Addressed the requested Copilot feedback in 1cef89a. Added tests that run
Also covered the nonempty Please re-review. |
mnriem
self-requested a review
August 18, 2026 13:16
mnriem
approved these changes
Aug 18, 2026
Collaborator
|
Thank you! |
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.
Description
Fixes #4152.
specify initcan hang forever inselect_with_arrowswaiting for arrow-key input.--forceonly skips the merge confirmation, so an invocation likespecify init --here --integration claude --forcestill reaches the script-type picker when stdin looks interactive.That happens in AI agent harnesses: they often allocate a PTY (
isatty()is true) but cannot send keypresses. The process then blocks with no timeout and no useful error.This change:
select_with_arrowswhen stdin is not a TTY, naming the flag that would supply the choice (--integration/--script) instead of hanging.--non-interactiveso callers can suppress every picker even when stdin is a TTY. Unspecified selections use documented defaults (integration viaSPECKIT_INTEGRATION_DEFAULT/ Copilot; script typepson Windows,shotherwise). Merge into a non-empty directory still requires--force— there is no safe default for overwrite.--extensioninstalls follow the same rule:--non-interactivenever callstyper.confirm. Without--trust-extension-urlsthe URL is denied; with it, the install proceeds without a prompt.--forceis unchanged: it still only means merge/overwrite, not "answer every prompt."Workflows that already run multi-step processes non-interactively are not affected. This is specifically the
specify initarrow-key picker path.Testing
specify init --help(--non-interactiveis listed)Focused pytest:
--non-interactivecases--extensionwith TTY +--non-interactive(deny without--trust-extension-urls; install with it;typer.confirmfails if called)Local smoke test:
completed without prompting, defaulted to Copilot /
pson Windows, and wrote.specify/init-options.json.Test selection reasoning
src/specify_cli/_console.pyspecify initpickerssrc/specify_cli/commands/init.pyspecify init--non-interactiveflag, prompt gating, URLallow_promptRequired tests
specify init --non-interactive— CLI init/scaffolding change;/speckit.specifyis not affected (no command templates or scripts changed)Manual test results
OS/Shell: Windows 10 / PowerShell
specify init --help--non-interactivelistedspecify init <temp> --non-interactive --ignore-agent-toolsAI Disclosure
AI assistance was used to explore the TTY vs PTY hang and to draft tests. I reviewed the existing init prompt paths, chose fail-fast plus an explicit
--non-interactiveflag (without overloading--force), and ran the tests above.