fix(init): stop specify init hanging on arrow-key pickers in agent harnesses - #4178
Open
lllakshit wants to merge 1 commit into
Open
fix(init): stop specify init hanging on arrow-key pickers in agent harnesses#4178lllakshit wants to merge 1 commit into
lllakshit wants to merge 1 commit into
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
| url_specs, trust_override=trust_extension_urls | ||
| url_specs, | ||
| trust_override=trust_extension_urls, | ||
| allow_prompt=_prompts_allowed(non_interactive), |
mnriem
requested changes
Aug 17, 2026
mnriem
left a comment
Collaborator
There was a problem hiding this comment.
Please address Copilot feedback
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.--forceis unchanged: it still only means merge/overwrite, not "answer every prompt."Testing
uv run specify --help(specify init --helpshows--non-interactive)uv sync && uv run pytest(focused: 15 passed covering this change)Focused pytest:
tests/test_console_imports.py,tests/test_live_transient_windows.py,tests/integrations/test_cli.py(non-interactive / picker cases), plus interactive Claude/Alquimia init tests — 15 passed.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 and prompt gatingRequired tests
specify init --non-interactive— CLI init/scaffolding change;/speckit.specifyis not affected (no command templates or scripts changed)Manual test results
Agent: Cursor | OS/Shell: Windows 10 / PowerShell
specify init --help--non-interactivelisted; examples include the agent/CI invocationsspecify init <temp> --non-interactive --ignore-agent-toolsAI Disclosure
This PR was written with Cursor (Composer). I reviewed the issue, the existing TTY handling in
init.py, and the test failures that would come from changingselect_with_arrows. The approach (fail-fast in the selector + an explicit--non-interactiveflag, without overloading--force) was chosen so interactive--forceoverwrite still prompts for remaining choices.