Skip to content

fix(init): stop specify init hanging on arrow-key pickers in agent harnesses - #4178

Open
lllakshit wants to merge 1 commit into
github:mainfrom
lllakshit:fix/4152-noninteractive-init-hang
Open

fix(init): stop specify init hanging on arrow-key pickers in agent harnesses#4178
lllakshit wants to merge 1 commit into
github:mainfrom
lllakshit:fix/4152-noninteractive-init-hang

Conversation

@lllakshit

Copy link
Copy Markdown

Description

Fixes #4152.

specify init can hang forever in select_with_arrows waiting for arrow-key input. --force only skips the merge confirmation, so an invocation like specify init --here --integration claude --force still 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:

  1. Fails fast inside select_with_arrows when stdin is not a TTY, naming the flag that would supply the choice (--integration / --script) instead of hanging.
  2. Adds --non-interactive so callers can suppress every picker even when stdin is a TTY. Unspecified selections use documented defaults (integration via SPECKIT_INTEGRATION_DEFAULT / Copilot; script type ps on Windows, sh otherwise). Merge into a non-empty directory still requires --force — there is no safe default for overwrite.

--force is unchanged: it still only means merge/overwrite, not "answer every prompt."

Testing

  • Tested locally with uv run specify --help (specify init --help shows --non-interactive)
  • Ran existing tests with uv sync && uv run pytest (focused: 15 passed covering this change)
  • Tested with a sample project (if applicable)

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:

specify init <temp-dir> --non-interactive --ignore-agent-tools

completed without prompting, defaulted to Copilot / ps on Windows, and wrote .specify/init-options.json.

Test selection reasoning

Changed file Affects Test Why
src/specify_cli/_console.py specify init pickers T1 Fail-fast lives in the shared selector
src/specify_cli/commands/init.py specify init T1 New --non-interactive flag and prompt gating
docs / README docs only User-facing flag documentation

Required tests

  • T1: specify init --non-interactive — CLI init/scaffolding change; /speckit.specify is not affected (no command templates or scripts changed)

Manual test results

Agent: Cursor | OS/Shell: Windows 10 / PowerShell

Command tested Notes
specify init --help --non-interactive listed; examples include the agent/CI invocations
specify init <temp> --non-interactive --ignore-agent-tools Pass — no hang, defaults to copilot + ps
pytest (15 focused tests) Pass

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

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 changing select_with_arrows. The approach (fail-fast in the selector + an explicit --non-interactive flag, without overloading --force) was chosen so interactive --force overwrite still prompts for remaining choices.

@lllakshit
lllakshit requested a review from mnriem as a code owner August 17, 2026 19:26
…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
lllakshit force-pushed the fix/4152-noninteractive-init-hang branch from d32f3cb to 8c57587 Compare August 17, 2026 19:32
@mnriem
mnriem requested a balanced review from Copilot August 17, 2026 20:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-interactive with 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 mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback

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.

specify init hangs on select_with_arrows prompts in non-interactive environments — no flag reaches them, --force does not bypass them

3 participants