From 5e8d25d7e78da269e00d6ac46374a38d97e92081 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 13 Aug 2026 23:41:49 -0700 Subject: [PATCH] fix(cli): --in accepts Git Bash / MSYS-style paths on Windows (#85865) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(cli): --in accepts Git Bash / MSYS paths on Windows Under Git Bash, 'hermes chat --in ~' reaches the CLI as /c/Users/ (the shell expands ~ to an MSYS POSIX path; MSYS2 argument conversion is disabled for native executables), and the isdir check failed with '--in directory not found: /c/Users/...'. Route the value through the existing _msys_to_windows_path translator (MSYS + Cygwin + WSL drive spellings; no-op elsewhere) before expanduser/abspath. Hit live: Bot Mode's agent-messaging protocol delivers with --in ~, so every bot-to-bot send from a Git Bash-driven agent failed on Windows. Tests pin both the translation cases and (source-level) the call site actually using it. * test: assert the MSYS translation, not platform abspath The prior assertion ran os.path.abspath on the translated Windows path, which on the Linux CI runner (posixpath) treats 'C:\Users\alice' as relative and prepends the runner cwd. Pin the translation output and ntpath absoluteness instead — same contract, platform-independent. --- hermes_cli/main.py | 10 +++- tests/hermes_cli/test_in_dir_msys_paths.py | 59 ++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/hermes_cli/test_in_dir_msys_paths.py diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 4abb7bf8303ba..12f342dc4d2c3 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -2538,7 +2538,15 @@ def cmd_chat(args): # recorded cwd (so the restore step below is skipped). in_dir = getattr(args, "in_dir", None) if in_dir: - _target_dir = os.path.abspath(os.path.expanduser(in_dir)) + # Git Bash / MSYS hands the CLI POSIX-style paths (`--in ~` expands to + # `/c/Users/x` before Python ever sees it; MSYS2's path conversion is + # disabled for native executables). Translate the MSYS/Cygwin/WSL + # drive-root spellings to native Windows form first — no-op elsewhere. + from tools.environments.local import _msys_to_windows_path + + _target_dir = os.path.abspath( + os.path.expanduser(_msys_to_windows_path(in_dir)) + ) if not os.path.isdir(_target_dir): print(f"Error: --in directory not found: {in_dir}") sys.exit(1) diff --git a/tests/hermes_cli/test_in_dir_msys_paths.py b/tests/hermes_cli/test_in_dir_msys_paths.py new file mode 100644 index 0000000000000..de1e3e129db92 --- /dev/null +++ b/tests/hermes_cli/test_in_dir_msys_paths.py @@ -0,0 +1,59 @@ +"""``--in`` accepts Git Bash / MSYS-style paths on Windows. + +Under Git Bash, ``hermes chat --in ~`` reaches the native CLI as +``/c/Users/`` — the shell expands ``~`` to an MSYS POSIX path and +MSYS2's automatic argument conversion is disabled for native executables. +The resolver must translate the MSYS/Cygwin/WSL drive-root spellings to +native form before the isdir check, or every Git Bash invocation dies with +"--in directory not found" (hit live by the Bot Mode agent-messaging flow, +whose SOUL protocol tells agents to deliver with ``--in ~``). + +The translation itself (``_msys_to_windows_path``) has exhaustive unit +coverage in tests/tools/test_local_env_windows_msys.py; this pins the +``--in`` call site actually applying it. +""" + +from unittest import mock + +from tools.environments.local import _msys_to_windows_path + + +class TestInDirMsysResolution: + def test_git_bash_tilde_expansion_translates_on_windows(self): + # Assert the translation itself — abspath/expanduser are platform + # functions, and this test also runs on Linux CI where posix abspath + # would treat 'C:\\...' as relative and prepend the runner's cwd. + with mock.patch("tools.environments.local._IS_WINDOWS", True): + translated = _msys_to_windows_path("/c/Users/alice") + assert translated == "C:" + chr(92) + "Users" + chr(92) + "alice" + # ntpath (what the resolver uses on Windows) sees it as absolute. + import ntpath + + assert ntpath.isabs(translated) + + def test_native_and_posix_forms_untouched(self): + with mock.patch("tools.environments.local._IS_WINDOWS", True): + assert _msys_to_windows_path("C:/Users/alice") == "C:/Users/alice" + assert _msys_to_windows_path("~/projects") == "~/projects" + + def test_non_windows_never_translates(self): + with mock.patch("tools.environments.local._IS_WINDOWS", False): + assert _msys_to_windows_path("/c/Users/alice") == "/c/Users/alice" + + def test_main_call_site_uses_translation(self): + """Guard: the --in resolution in hermes_cli.main must route through + _msys_to_windows_path (a plain expanduser/abspath does not survive + Git Bash). Source-level check keeps this honest without spawning + the full CLI.""" + import inspect + + import hermes_cli.main as main_mod + + src = inspect.getsource(main_mod) + idx = src.find('in_dir = getattr(args, "in_dir", None)') + assert idx != -1, "--in resolution block moved; update this test" + block = src[idx : idx + 800] + assert "_msys_to_windows_path" in block, ( + "--in no longer translates MSYS paths; Git Bash `--in ~` will " + "fail with '--in directory not found: /c/Users/...'" + )