88import sys
99import threading
1010from collections .abc import Callable , Iterable
11+ from contextlib import contextmanager
1112from typing import TYPE_CHECKING , Any
1213
1314from prompt_toolkit import PromptSession
2122
2223from .. import config
2324
25+
26+ @contextmanager
27+ def _safe_patch_stdout ():
28+ """patch_stdout that degrades to a no-op when the terminal output
29+ cannot be created (e.g. headless Windows CI without a console)."""
30+ try :
31+ proxy = patch_stdout ()
32+ except Exception :
33+ yield
34+ return
35+ with proxy :
36+ yield
37+
38+
2439if TYPE_CHECKING :
2540 from ..session import Session
2641
@@ -97,6 +112,16 @@ def _make_prompt_session(
97112 the completion state just before the Tab-triggered task runs, which
98113 then bails out without inserting the common part). Tab must be the
99114 single, deterministic trigger.
115+
116+ ``enable_suspend`` is off on Windows: Ctrl-Z (suspend) is a Unix
117+ terminal feature with no Windows equivalent.
118+
119+ On Windows without a real console (headless CI, redirected stdout),
120+ prompt_toolkit raises ``NoConsoleScreenBufferError`` while creating
121+ the default ``Win32Output``. The fallback rebuilds the session with
122+ a no-op ``DummyOutput`` so the TUI can still be constructed; on a
123+ real terminal the first attempt succeeds and the fallback is never
124+ reached.
100125 """
101126 try :
102127 return PromptSession (
@@ -109,6 +134,7 @@ def _make_prompt_session(
109134 ** kwargs ,
110135 )
111136 except Exception :
137+ # No console available (headless Windows CI): use a no-op output.
112138 from prompt_toolkit .output import DummyOutput
113139
114140 return PromptSession (
@@ -313,7 +339,7 @@ def _input_prompt(self) -> FormattedText:
313339
314340 def _read_multiline (self ) -> str | None :
315341 try :
316- with patch_stdout ():
342+ with _safe_patch_stdout ():
317343 text = self .prompt_session .prompt (self ._input_prompt ())
318344 except EOFError :
319345 # Ctrl-D: quit
@@ -361,7 +387,7 @@ def _ask_question_blocking(self) -> None:
361387 else :
362388 prompt = q .prompt + " > "
363389 try :
364- with patch_stdout ():
390+ with _safe_patch_stdout ():
365391 answer = self .prompt_session .prompt (prompt , multiline = False )
366392 except (EOFError , KeyboardInterrupt ):
367393 answer = ""
0 commit comments