Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions tests/tools/test_file_tools_msys_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Tests for MSYS path normalization in file_tools._resolve_path_for_task.

On Windows + Git Bash/MSYS2, the LLM may pass MSYS-style paths like
``/d/Project/file.sh`` to file tools. Without normalization, Python's
pathlib treats ``/d/Project`` as "root \\ on the current drive" and
resolves it to e.g. ``D:\\d\\Project`` instead of ``D:\\Project``.

Regression test for #44726.
"""

from pathlib import Path
from unittest.mock import patch

from tools import file_tools as ft_mod
from tools.environments import local as local_mod


class TestResolvePathMsysNormalization:
"""_resolve_path_for_task normalizes MSYS paths before Path construction."""

def test_msys_drive_path_normalized_on_windows(self, monkeypatch):
monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
with patch.object(ft_mod, "_resolve_base_dir", return_value=Path("C:/fallback")):
result = ft_mod._resolve_path_for_task("/d/Project/file.sh")
assert result == Path("D:\\Project\\file.sh").resolve()

def test_msys_c_drive_path_normalized_on_windows(self, monkeypatch):
monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
with patch.object(ft_mod, "_resolve_base_dir", return_value=Path("C:/fallback")):
result = ft_mod._resolve_path_for_task("/c/Users/dev/code.py")
assert result == Path("C:\\Users\\dev\\code.py").resolve()

def test_native_windows_path_unchanged(self, monkeypatch):
monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
with patch.object(ft_mod, "_resolve_base_dir", return_value=Path("C:/fallback")):
result = ft_mod._resolve_path_for_task("D:\\Project\\file.sh")
assert result == Path("D:\\Project\\file.sh").resolve()

def test_noop_on_non_windows(self, monkeypatch):
monkeypatch.setattr(local_mod, "_IS_WINDOWS", False)
with patch.object(ft_mod, "_resolve_base_dir", return_value=Path("/home/user")):
result = ft_mod._resolve_path_for_task("/d/Project/file.sh")
assert result == Path("/d/Project/file.sh").resolve()
2 changes: 2 additions & 0 deletions tools/file_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
normalize_search_pagination,
)
from tools import file_state
from tools.environments.local import _msys_to_windows_path
from agent.redact import redact_sensitive_text

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -202,6 +203,7 @@ def _resolve_path_for_task(filepath: str, task_id: str = "default") -> Path:
See :func:`_resolve_base_dir` for how the base is chosen. Absolute input
paths are returned resolved-but-unanchored.
"""
filepath = _msys_to_windows_path(filepath)
p = Path(filepath).expanduser()
if p.is_absolute():
return p.resolve()
Expand Down