Skip to content

fix(tools): translate MSYS paths to Windows-native before shell lint (#66494) - #66524

Open
stantheman0128 wants to merge 3 commits into
NousResearch:mainfrom
stantheman0128:fix/66494-msys-lint-path
Open

fix(tools): translate MSYS paths to Windows-native before shell lint (#66494)#66524
stantheman0128 wants to merge 3 commits into
NousResearch:mainfrom
stantheman0128:fix/66494-msys-lint-path

Conversation

@stantheman0128

Copy link
Copy Markdown
Contributor

What does this PR do?

On Windows/MSYS the post-write shell linters (node --check, tsc, go vet, rustfmt, py_compile) received the target file path in Git Bash /e/... form. Git Bash single-quotes the {file} argument, which suppresses MSYS POSIX to Windows argument conversion, so native node got /e/project/x.js verbatim and resolved it against the current drive root, producing Cannot find module 'E:\project\Fina\js\models.js' on every .js edit.

The fix reuses the terminal backend's existing _msys_to_windows_path translator, so the automatic lint hands the native toolchain the same Windows-native path the terminal already uses for its cwd. This is exactly the "use the same path-resolution engine as the terminal" approach the issue suggests. Off Windows the path is left untouched, so genuine POSIX names (backslashes and all) still round-trip through the existing shell escaper.

Related Issue

Fixes #66494

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • tools/file_operations.py: in ShellFileOperations._check_lint, translate the file argument via _msys_to_windows_path (then forward-slash form, single-quoted) when _IS_WINDOWS, before substituting into the linter command. Off Windows keeps the previous _escape_shell_arg path.
  • tests/tools/test_file_operations_edge_cases.py: add TestCheckLintWindowsNativePath covering both the Windows translation (/e/project/... -> 'E:/project/...', and the raw MSYS path must NOT appear) and the off-Windows passthrough.

How to Test

  1. On Windows/MSYS with native node on PATH, edit a .js file through write_file/patch in a project on a non-C: drive (e.g. E:\). Before this change the auto-lint reports Cannot find module 'E:\e\...'; after it, node --check runs against the correct E:/... path.
  2. python -m pytest tests/tools/test_file_operations_edge_cases.py -q

Verification output on this branch:

$ python -m pytest tests/tools/test_file_operations_edge_cases.py::TestCheckLintWindowsNativePath -q
..                                                                       [100%]
2 passed in 1.56s

$ python -m pytest tests/tools/test_file_operations_edge_cases.py -q
.....................................                                    [100%]
37 passed in 3.40s

Checklist

Code

  • My commit messages follow Conventional Commits (fix(tools):)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've added tests for my changes
  • I've tested on my platform: Windows 11 (MSYS/Git Bash)

Documentation & Housekeeping

  • Considered cross-platform impact: the translation is gated on _IS_WINDOWS; POSIX behavior is unchanged.

Disclosure: this change was prepared with AI assistance (Claude) and reviewed before submission.

@alt-glitch alt-glitch added type/bug Something isn't working comp/tools Tool registry, model_tools, toolsets tool/file File tools (read, write, patch, search) platform/windows Native Windows-specific behavior or breakage P2 Medium — degraded but workaround exists sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Jul 17, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the lint invocation through the MSYS path boundary. The local Windows premise remains present on current main: tools/file_tools.py:385 resolves MSYS paths to native paths, while tools/file_operations.py:1771 sends them back through Git-Bash escaping for shell linters.

Problems

  • tools/file_operations.py:1782 gates translation only on the host-wide _IS_WINDOWS flag. ShellFileOperations is shared by local and remote backends (tools/file_operations.py:793-799), so a Windows host running a Docker/SSH/Modal/etc. backend can have valid in-backend POSIX /e/... paths rewritten to E:/.... This conflicts with the existing backend-path boundary documented by tests/tools/test_file_tools.py:475-490.
  • The added tests mock _exec on an object made with __new__, so they do not exercise the backend distinction.

Suggested changes

  • Restrict conversion to LocalEnvironment as well as Windows; tools/file_operations.py:1882-1901 already has an established local-backend gate pattern.
  • Add a remote-backend regression test asserting an /e/... path remains POSIX while retaining the local native-path assertion.

Automated hermes-sweeper review.

Comment thread tools/file_operations.py
# left untouched so genuine POSIX names (backslashes and all) survive.
from tools.environments.local import _IS_WINDOWS, _msys_to_windows_path
if _IS_WINDOWS:
native = _msys_to_windows_path(path).replace("\\", "/")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_IS_WINDOWS describes the host, not the terminal backend. ShellFileOperations also executes in Docker/SSH/Modal/etc.; on a Windows host this rewrites valid remote Linux /e/... paths to E:/.... Please additionally gate this on LocalEnvironment (the existing _lsp_local_only() helper shows that backend-boundary pattern) and add a remote-backend passthrough test.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 18, 2026
@stantheman0128

Copy link
Copy Markdown
Contributor Author

Thanks for the catch on the host-vs-backend boundary.

Updated so MSYS lint path translation runs only when the host is Windows and the ops are wired to a LocalEnvironment (same backend check as _lsp_local_only). Docker/SSH/etc. on a Windows host now leave in-backend POSIX paths like /e/... alone.

Also added a remote-backend regression test with a real DockerEnvironment instance (not just a __new__ mock without env), and kept the local native-path assertion.

Verification:

scripts/run_tests.sh tests/tools/test_file_operations_edge_cases.py -q
=== Summary: 1 files, 38 tests passed, 0 failed ===

python scripts/check-windows-footguns.py tools/file_operations.py tests/tools/test_file_operations_edge_cases.py
No Windows footguns found (2 file(s) scanned).

@teknium1 teknium1 added the area/i18n Localization, locales, translations label Jul 19, 2026
stantheman0128 and others added 2 commits July 23, 2026 15:38
…ousResearch#66494)

On Windows/MSYS the post-write shell linters (node --check, tsc, go vet,
rustfmt, py_compile) received the file path in Git Bash /e/... form. Git
Bash single-quotes the argument, which suppresses MSYS POSIX->Windows
conversion, so native node got /e/project/x.js verbatim and resolved it
against the current drive root, producing 'Cannot find module
E:\e\project\x.js'. Reuse the terminal backend's
_msys_to_windows_path translator so the lint hands node the same
Windows-native path the terminal already uses for its cwd. No-op off
Windows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ShellFileOperations is shared by local and remote backends. Translating
/e/... paths whenever the host is Windows rewrote valid in-backend POSIX
paths for Docker/SSH. Restrict the conversion to LocalEnvironment on
Windows, matching the _lsp_local_only backend boundary, and cover the
remote passthrough with a regression test.
@stantheman0128
stantheman0128 force-pushed the fix/66494-msys-lint-path branch from b06d97c to 00245be Compare July 23, 2026 07:42


EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/i18n Localization, locales, translations comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows tool/file File tools (read, write, patch, search) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows/MSYS: automatic file lint fails — path built as E:\ + /e/... (module not found)

3 participants