Environment
- Windows 11, Git for Windows (
C:\Program Files\Git\bin\bash.exe)
- Hermes installed via the official
install.sh path; terminal.backend: local
- Python 3.11 (Windows MSC build inside the hermes venv)
Reproduce
- Install Git for Windows + Hermes on Windows (no WSL).
hermes chat, then run any terminal call — e.g. echo hello.
- Result:
{\"output\": \"\", \"exit_code\": 126} every time.
Browser tool also fails on Windows with WinError 193 (not a valid Win32 application) on every navigation.
Root Causes (3 independent bugs, same platform)
1. select.select on pipe fds is not supported on Windows.
tools/environments/base.py::_drain calls select.select([proc.stdout.fileno()], …), which raises OSError 10038 on Windows pipes. The except (ValueError, OSError): break swallows it and the drain thread exits immediately → stdout is never read. All terminal() calls return empty output, and _extract_cwd_from_output never sees the __HERMES_CWD__ marker so self.cwd is never synced.
2. get_temp_dir() returns /tmp.
LocalEnvironment.get_temp_dir falls back to /tmp. Python on Windows resolves that to C:\tmp (non-existent), so the snapshot/cwd files written by Git Bash at /tmp/hermes-* can never be read back by Python — every open(self._cwd_file) silently FileNotFoundErrors.
3. builtin cd rejects single-quoted Windows backslash paths → exit 126.
_wrap_command emits builtin cd 'C:\Users\…' || exit 126. Git Bash's cd builtin does not accept that form; it needs POSIX (/c/Users/…). This is the exact source of the 126.
(Bonus) Browser: tools/browser_tool.py launches npx/agent-browser (which are .cmd shims on Windows) via subprocess.Popen without shell=True, producing WinError 193.
Fix
All three terminal bugs are small, local patches:
tools/environments/base.py — branch _drain on Windows and do blocking proc.stdout.read(4096) instead of select. Subprocess pipe handles aren't propagated to grandchildren on Windows (Py 3.7+), so EOF arrives promptly.
tools/environments/local.py
get_temp_dir(): on Windows, return tempfile.gettempdir().replace('\\', '/'). C:/Users/…/Temp is valid for both Python open() and Git Bash source.
- Add
_win_to_posix_path (C:\foo → /c/foo) and _posix_to_win_path (/c/foo → C:/foo).
- Override
LocalEnvironment._wrap_command to run cwd through _win_to_posix_path on Windows before building the script — fixes the builtin cd failure.
- In
_run_bash, feed _posix_to_win_path(self.cwd) to subprocess.Popen(cwd=…) so CreateProcess still gets a Windows path even if self.cwd was updated from pwd -P output.
tools/browser_tool.py — on Windows, detect .cmd/.bat shims and invoke via shell=True (or prepend ['cmd','/c']).
I've tested these patches locally against my install and am happy to send a PR if useful.
Note
The install docs say "Requires WSL2 on Windows", but the code path actually targets Git Bash. Clarifying this (or actually supporting WSL2) would help.
Environment
C:\Program Files\Git\bin\bash.exe)install.shpath;terminal.backend: localReproduce
hermes chat, then run anyterminalcall — e.g.echo hello.{\"output\": \"\", \"exit_code\": 126}every time.Browser tool also fails on Windows with
WinError 193(not a valid Win32 application) on every navigation.Root Causes (3 independent bugs, same platform)
1.
select.selecton pipe fds is not supported on Windows.tools/environments/base.py::_draincallsselect.select([proc.stdout.fileno()], …), which raisesOSError 10038on Windows pipes. Theexcept (ValueError, OSError): breakswallows it and the drain thread exits immediately → stdout is never read. Allterminal()calls return empty output, and_extract_cwd_from_outputnever sees the__HERMES_CWD__marker soself.cwdis never synced.2.
get_temp_dir()returns/tmp.LocalEnvironment.get_temp_dirfalls back to/tmp. Python on Windows resolves that toC:\tmp(non-existent), so the snapshot/cwd files written by Git Bash at/tmp/hermes-*can never be read back by Python — everyopen(self._cwd_file)silentlyFileNotFoundErrors.3.
builtin cdrejects single-quoted Windows backslash paths →exit 126._wrap_commandemitsbuiltin cd 'C:\Users\…' || exit 126. Git Bash'scdbuiltin does not accept that form; it needs POSIX (/c/Users/…). This is the exact source of the 126.(Bonus) Browser:
tools/browser_tool.pylaunchesnpx/agent-browser(which are.cmdshims on Windows) viasubprocess.Popenwithoutshell=True, producingWinError 193.Fix
All three terminal bugs are small, local patches:
tools/environments/base.py— branch_drainon Windows and do blockingproc.stdout.read(4096)instead ofselect. Subprocess pipe handles aren't propagated to grandchildren on Windows (Py 3.7+), so EOF arrives promptly.tools/environments/local.pyget_temp_dir(): on Windows, returntempfile.gettempdir().replace('\\', '/').C:/Users/…/Tempis valid for both Pythonopen()and Git Bashsource._win_to_posix_path(C:\foo→/c/foo) and_posix_to_win_path(/c/foo→C:/foo).LocalEnvironment._wrap_commandto runcwdthrough_win_to_posix_pathon Windows before building the script — fixes thebuiltin cdfailure._run_bash, feed_posix_to_win_path(self.cwd)tosubprocess.Popen(cwd=…)soCreateProcessstill gets a Windows path even ifself.cwdwas updated frompwd -Poutput.tools/browser_tool.py— on Windows, detect.cmd/.batshims and invoke viashell=True(or prepend['cmd','/c']).I've tested these patches locally against my install and am happy to send a PR if useful.
Note
The install docs say "Requires WSL2 on Windows", but the code path actually targets Git Bash. Clarifying this (or actually supporting WSL2) would help.