diff --git a/src/tau_coding/tui/widgets.py b/src/tau_coding/tui/widgets.py index 52a61205..ea988d00 100644 --- a/src/tau_coding/tui/widgets.py +++ b/src/tau_coding/tui/widgets.py @@ -1995,7 +1995,13 @@ def _context_usage(session: SessionSummarySource) -> str: def _styled_cwd(cwd: Path, *, theme: TuiTheme) -> Text: """Style the parent path as metadata while emphasizing the working directory.""" short_path = _short_path(cwd) - parent, separator, name = short_path.rpartition("/") + # Windows paths use backslashes, so split on whichever separator comes last. + split_at = max(short_path.rfind("/"), short_path.rfind("\\")) + parent, separator, name = ( + (short_path[:split_at], short_path[split_at], short_path[split_at + 1 :]) + if split_at != -1 + else ("", "", short_path) + ) text = Text(overflow="fold", no_wrap=False) if separator and name: text.append(f"{parent}{separator}", style=theme.completion_description) diff --git a/tests/test_coding_tools.py b/tests/test_coding_tools.py index 1ff23936..aab803e0 100644 --- a/tests/test_coding_tools.py +++ b/tests/test_coding_tools.py @@ -1,5 +1,6 @@ import asyncio import shlex +import sys from pathlib import Path from time import monotonic @@ -162,6 +163,14 @@ async def test_bash_tool_captures_stdout_and_exit_code(tmp_path: Path) -> None: assert result.details["timed_out"] is False +# These tests rely on bash behavior (shell_command_prefix routes through bash, +# and job control like `sleep 1 & wait`) that only applies on POSIX. +requires_posix_shell = pytest.mark.skipif( + sys.platform == "win32", reason="bash-specific shell behavior is POSIX-only" +) + + +@requires_posix_shell @pytest.mark.anyio async def test_create_coding_tools_applies_shell_command_prefix( tmp_path: Path, @@ -180,6 +189,7 @@ async def test_create_coding_tools_applies_shell_command_prefix( assert result.details["shell_command_prefix_applied"] is True +@requires_posix_shell @pytest.mark.anyio async def test_bash_tool_applies_opt_in_shell_command_prefix(tmp_path: Path) -> None: rc_path = tmp_path / ".zshrc" @@ -231,6 +241,7 @@ async def test_bash_tool_timeout_kills_shell_children(tmp_path: Path) -> None: assert not marker.exists() +@requires_posix_shell @pytest.mark.anyio async def test_bash_tool_cancellation_kills_shell_children(tmp_path: Path) -> None: tool = create_bash_tool(cwd=tmp_path) diff --git a/tests/test_credentials.py b/tests/test_credentials.py index d7a4c563..eb24c3e9 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -1,3 +1,4 @@ +import os from stat import S_IMODE import pytest @@ -12,7 +13,9 @@ def test_file_credential_store_round_trips_and_sets_private_permissions(tmp_path store.set("openai", "test-key") assert store.get("openai") == "test-key" - assert S_IMODE(path.stat().st_mode) == 0o600 + if os.name == "posix": + # chmod(0o600) is a no-op for these bits on Windows filesystems. + assert S_IMODE(path.stat().st_mode) == 0o600 def test_file_credential_store_deletes_key(tmp_path) -> None: diff --git a/tests/test_system_prompt.py b/tests/test_system_prompt.py index a043acc4..7a03f895 100644 --- a/tests/test_system_prompt.py +++ b/tests/test_system_prompt.py @@ -45,7 +45,9 @@ def test_default_prompt_includes_tools_guidelines_date_and_cwd(tmp_path: Path) - assert "Tau documentation (read only when the user asks about Tau itself" in prompt assert "custom providers or adding built-in providers/models (docs/models.md)" in prompt assert "creating or modifying extensions (docs/extensions.md" in prompt - assert prompt.endswith(f"Current date: 2026-06-17\nCurrent working directory: {tmp_path}") + # build_system_prompt normalizes the cwd to forward slashes on every platform. + expected_cwd = str(tmp_path).replace("\\", "/") + assert prompt.endswith(f"Current date: 2026-06-17\nCurrent working directory: {expected_cwd}") def test_tool_without_prompt_snippet_is_hidden_from_available_tools() -> None: diff --git a/tests/test_tui_app.py b/tests/test_tui_app.py index 630559d0..4357045d 100644 --- a/tests/test_tui_app.py +++ b/tests/test_tui_app.py @@ -549,10 +549,11 @@ def test_session_sidebar_lists_multiple_context_files() -> None: console.print(render_session_sidebar(session)) output = console.export_text() + # The sidebar renders labels via str(Path(...)), so separators are native. assert "AGENTS.md" in output - assert ".agents/AGENTS.md" in output - assert "docs/AGENTS.md" in output - assert "/Users/alex/.agents/AGENTS.md" in output + assert str(Path(".agents") / "AGENTS.md") in output + assert str(Path("docs") / "AGENTS.md") in output + assert str(Path("/Users/alex/.agents/AGENTS.md")) in output def test_compact_session_info_renders_sidebar_facts() -> None: @@ -564,7 +565,7 @@ def test_compact_session_info_renders_sidebar_facts() -> None: lines = output.splitlines() provider_line = next(index for index, line in enumerate(lines) if "openai:fake-model" in line) context_line = next(index for index, line in enumerate(lines) if "12k/200k" in line) - assert "/workspace/project (--)" in output + assert f"{Path('/workspace/project')} (--)" in output assert "context 12k/200k" not in output assert "openai:fake-model" in lines[provider_line] assert "(medium)" in lines[provider_line] @@ -574,7 +575,7 @@ def test_compact_session_info_renders_sidebar_facts() -> None: def test_compact_session_info_styles_parent_path_as_metadata() -> None: cwd = _styled_cwd(Path("/workspace/project"), theme=TAU_DARK_THEME) - assert cwd.plain == "/workspace/project (--)" + assert cwd.plain == f"{Path('/workspace/project')} (--)" assert str(cwd.spans[0].style) == TAU_DARK_THEME.completion_description assert str(cwd.spans[1].style) == TAU_DARK_THEME.prompt_text assert str(cwd.spans[2].style) == TAU_DARK_THEME.completion_description @@ -3606,7 +3607,9 @@ def fake_notify(message: str, **kwargs: object) -> None: await pilot.press("enter") assert session.export_calls == [(Path("out.jsonl"), "jsonl")] - assert notifications == ["Exported session to /workspace/project/session.html"] + assert notifications == [ + f"Exported session to {Path('/workspace/project/session.html')}" + ] assert session.prompt_texts == [] @@ -4116,7 +4119,7 @@ async def test_tui_app_completes_resume_session_argument() -> None: assert app._completion_state.selected is not None assert app._completion_state.selected.description == ( - "Session - fake-model - /workspace/project" + f"Session - fake-model - {Path('/workspace/project')}" ) await pilot.press("tab")