prompting: end an open tool line before writing a prompt

This commit is contained in:
2026-10-06 19:06:22 +08:00
parent a81b138bf8
commit 1068ea0025
5 changed files with 81 additions and 3 deletions
+23
View File
@@ -7,6 +7,7 @@ from plyngent.cli.display import (
_clear_streamed_lines,
_line_count_for_clear,
_pretty_line_for,
close_open_pretty_line,
get_markdown_enabled,
markdown_render_available,
print_markdown,
@@ -1002,6 +1003,28 @@ async def test_pretty_parallel_batch_keeps_the_line_above_the_prefix(
assert "\x1b[" not in out
async def test_pretty_line_closed_by_out_of_band_output(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""A prompt/confirm printed while the call runs ends the open prefix line.
The prompt backend runs :func:`close_open_pretty_line` before writing, so its
box starts on a fresh line and the result then prints whole.
"""
monkeypatch.setattr("plyngent.cli.display.interactive_terminal", lambda: True)
async def _events():
yield _pretty_call("read_file", '{"path": "a.txt"}')
close_open_pretty_line() # what a confirm box does mid-call
yield _result("L1-4\none\n")
await render_events(_events())
out = capsys.readouterr().out
assert out == ("\n* Read 'a.txt' \n\n* Read 'a.txt' L1-4 (done)\n\n")
assert "\x1b[" not in out
def test_clear_streamed_lines_clears_exactly_the_given_rows(capsys: pytest.CaptureFixture[str]) -> None:
"""Two rows means two clears; the cursor lands on the topmost cleared row."""
_clear_streamed_lines(2)
+4 -1
View File
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
import pytest
from plyngent.cli.display import close_open_pretty_line
from plyngent.cli.limits import (
auto_continue_enabled,
format_tool_confirm_box,
@@ -14,7 +15,7 @@ from plyngent.cli.limits import (
reset_auto_continue_turn,
set_auto_continue_default,
)
from plyngent.prompting import NonInteractiveBackend, get_prompt_backend, temporary_backend
from plyngent.prompting import NonInteractiveBackend, get_prompt_backend, get_prompt_output_hook, temporary_backend
from plyngent.tools.process.pty_session import PtyManager
from tests.test_prompting import ScriptedBackend
@@ -121,6 +122,8 @@ def test_format_tool_confirm_box_multiline() -> None:
def test_install_cli_limit_hooks() -> None:
install_cli_limit_hooks()
assert callable(getattr(PtyManager, "_limit_continue", None))
# A prompt raised mid-call must end the open pretty tool line first.
assert get_prompt_output_hook() is close_open_pretty_line
PtyManager.set_limit_continue_hook(None)
# Backend remains usable after install.
assert get_prompt_backend().is_interactive() or True
+22
View File
@@ -19,11 +19,14 @@ from plyngent.prompting import (
configure_prompting,
confirm,
form,
get_prompt_backend,
get_prompt_output_hook,
prompt_cancelled,
read_line_with_timeout,
reset_prompting,
run_cancellable_prompt_async,
run_prompt_async,
set_prompt_output_hook,
temporary_backend,
)
@@ -257,3 +260,22 @@ def test_non_interactive_confirm_uses_default() -> None:
with temporary_backend(NonInteractiveBackend()):
assert confirm("ok?", default=False) is False
assert confirm("ok?", default=True) is True
def test_prompt_output_hook_runs_before_chrome(capsys: pytest.CaptureFixture[str]) -> None:
"""A registered hook runs before echo/secho write; clearing it removes it.
The CLI registers one so an open pretty tool line ends before a prompt box.
"""
calls: list[str] = []
assert get_prompt_output_hook() is None
set_prompt_output_hook(lambda: calls.append("break"))
try:
backend = get_prompt_backend()
backend.echo("box")
backend.secho("question", fg="yellow")
finally:
set_prompt_output_hook(None)
assert calls == ["break", "break"]
assert get_prompt_output_hook() is None
assert "box" in capsys.readouterr().out