fix(shell): address Windows backspace and TUI shell regressions

This commit is contained in:
tjb-tech
2026-04-19 14:22:37 +00:00
parent 35fb12d5da
commit d4eae153d3
5 changed files with 75 additions and 13 deletions
@@ -59,16 +59,7 @@ function MultilineTextInput({
return;
}
if (key.backspace || key.delete) {
if (key.delete) {
if (cursorOffset >= value.length) {
return;
}
const nextValue = value.slice(0, cursorOffset) + value.slice(cursorOffset + 1);
onChange(nextValue);
return;
}
if (key.backspace) {
if (cursorOffset === 0) {
return;
}
@@ -78,6 +69,15 @@ function MultilineTextInput({
return;
}
if (key.delete) {
if (cursorOffset >= value.length) {
return;
}
const nextValue = value.slice(0, cursorOffset) + value.slice(cursorOffset + 1);
onChange(nextValue);
return;
}
if (!input) {
return;
}
+1
View File
@@ -43,6 +43,7 @@ class BashTool(BaseTool):
arguments.command,
cwd=cwd,
prefer_pty=True,
stdin=asyncio.subprocess.DEVNULL,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
)
+10 -3
View File
@@ -34,14 +34,14 @@ def resolve_shell_command(
if bash:
argv = [bash, "-lc", command]
if prefer_pty:
wrapped = _wrap_command_with_script(argv)
wrapped = _wrap_command_with_script(argv, platform_name=resolved_platform)
if wrapped is not None:
return wrapped
return argv
shell = shutil.which("sh") or os.environ.get("SHELL") or "/bin/sh"
argv = [shell, "-lc", command]
if prefer_pty:
wrapped = _wrap_command_with_script(argv)
wrapped = _wrap_command_with_script(argv, platform_name=resolved_platform)
if wrapped is not None:
return wrapped
return argv
@@ -104,7 +104,14 @@ async def create_shell_subprocess(
return process
def _wrap_command_with_script(argv: list[str]) -> list[str] | None:
def _wrap_command_with_script(
argv: list[str],
*,
platform_name: PlatformName | None = None,
) -> list[str] | None:
resolved_platform = platform_name or get_platform()
if resolved_platform == "macos":
return None
script = shutil.which("script")
if script is None:
return None
+25
View File
@@ -154,3 +154,28 @@ async def test_bash_tool_collects_combined_output(monkeypatch, tmp_path: Path):
assert result.is_error is False
assert result.output == "line one\nline two"
assert result.metadata["returncode"] == 0
@pytest.mark.asyncio
async def test_bash_tool_uses_devnull_stdin_for_non_interactive_shell(monkeypatch, tmp_path: Path):
process = _FakeProcess(
stdout=_FakeStdout([b"ok\n", b""]),
returncode=0,
)
seen_kwargs: dict[str, object] = {}
async def fake_create_shell_subprocess(*args, **kwargs):
del args
seen_kwargs.update(kwargs)
return process
monkeypatch.setattr("openharness.tools.bash_tool.create_shell_subprocess", fake_create_shell_subprocess)
result = await BashTool().execute(
BashToolInput(command="echo ok"),
ToolExecutionContext(cwd=tmp_path),
)
assert result.is_error is False
assert seen_kwargs["stdin"] == asyncio.subprocess.DEVNULL
assert seen_kwargs["prefer_pty"] is True
+29
View File
@@ -49,3 +49,32 @@ def test_resolve_shell_command_uses_powershell_on_windows(monkeypatch):
"-Command",
"Write-Output hi",
]
def test_resolve_shell_command_skips_script_on_macos(monkeypatch):
def fake_which(name: str) -> str | None:
mapping = {
"bash": "/bin/bash",
"script": "/usr/bin/script",
}
return mapping.get(name)
monkeypatch.setattr("openharness.utils.shell.shutil.which", fake_which)
command = resolve_shell_command("echo hi", platform_name="macos", prefer_pty=True)
assert command == ["/bin/bash", "-lc", "echo hi"]
def test_resolve_shell_command_linux_without_script_falls_back(monkeypatch):
def fake_which(name: str) -> str | None:
mapping = {
"bash": "/usr/bin/bash",
}
return mapping.get(name)
monkeypatch.setattr("openharness.utils.shell.shutil.which", fake_which)
command = resolve_shell_command("echo hi", platform_name="linux", prefer_pty=True)
assert command == ["/usr/bin/bash", "-lc", "echo hi"]