Terminal CWD and path normalization bug fixes
Cluster 26783 · environments. Data as of
Nothing open on GitHub. Fix merged in #65475 on 16 Jul 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #65475. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #65475. That is separate from the fix event. - Still open
- Nothing open on GitHub.
A cluster of fixes addressing working directory handling edge cases: graceful fallbacks when CWD is deleted, normalization of relative and MSYS/Git Bash paths on Windows, and resolving errors when the current directory is already the target.
Open issues0
Open PRs0
Fixes on record
-
#65475 fix(local): normalize relative initial cwd to an absolute host path
files:
scripts/release.py,tests/tools/test_local_env_relative_cwd.py,tools/environments/local.pyShow the diff
-
scripts/release.py@@ -1990,6 +1990,7 @@ "[redacted:email]": "dexhunter", # PR #60339 salvage (skills snapshot manifest speedup) "[redacted:email]": "uzaylisak", # PR #29988 salvage (detect_local_server_type process-lifetime cache) "[redacted:email]": "8294", # PR #50572 salvage (honor config context_length on banner) + "[redacted:email]": "ya-nsh", # PR #26790 salvage (normalize local terminal relative cwd; #26783) } -
tests/tools/test_local_env_relative_cwd.py@@ -0,0 +1,52 @@ +"""Regression tests for local terminal initial cwd normalization.""" + +from pathlib import Path + +from tools.environments.local import LocalEnvironment, _resolve_local_initial_cwd + + +def test_relative_initial_cwd_resolves_from_parent(tmp_path, monkeypatch): + project = tmp_path / "hermes-agent" + project.mkdir() + monkeypatch.chdir(tmp_path) + + assert _resolve_local_initial_cwd("hermes-agent") == str(project) + + +def test_relative_initial_cwd_matching_current_dir_uses_current_dir(tmp_path, monkeypatch): + project = tmp_path / "hermes-agent" + project.mkdir() + monkeypatch.chdir(project) + + assert _resolve_local_initial_cwd("hermes-agent") == str(project) + + +def test_local_environment_does_not_cd_into_nested_matching_relative_cwd(tmp_path, monkeypatch): + project = tmp_path / "hermes-agent" + project.mkdir() + monkeypatch.chdir(project) + + env = LocalEnvironment(cwd="hermes-agent", timeout=5) + try: + result = env.execute("pwd", timeout=5) + finally: + env.cleanup() + + assert result["returncode"] == 0 + assert result["output"].strip() == str(project) + assert "cd: hermes-agent" not in result["output"] + + +def test_local_environment_keeps_existing_relative_child_cwd(tmp_path, monkeypatch): + project = tmp_path / "hermes-agent" + project.mkdir() + monkeypatch.chdir(tmp_path) + + env = LocalEnvironment(cwd="hermes-agent", timeout=5) + try: + result = env.execute("pwd", timeout=5) + finally: + env.cleanup() + + assert result["returncode"] == 0 + assert result["output"].strip() == str(project) -
tools/environments/local.py@@ -48,6 +48,46 @@ def _msys_to_windows_path(cwd: str) -> str: return f"{drive}:{tail or chr(92)}" # chr(92) = backslash, avoid raw-string escape +def _resolve_local_initial_cwd(cwd: str) -> str: + """Resolve the local backend's initial cwd to an absolute host path. + + ``TERMINAL_CWD`` can be populated from config.yaml before the terminal + backend is created. If that value is relative and happens to match the + directory Hermes was already launched from (for example ``hermes-agent`` + while the process cwd is ``~/.hermes/hermes-agent``), passing it through + unchanged makes the wrapper run ``cd hermes-agent`` *inside* the project + and fail with a confusing nested-path error. Anchor relative local cwd + values once, up front, so both ``subprocess.Popen(cwd=...)`` and the + in-shell ``cd`` use the same absolute directory. + """ + expanded = os.path.expanduser(cwd) if cwd else os.getcwd() + if _IS_WINDOWS: + expanded = _msys_to_windows_path(expanded) + # Use the Windows-aware check explicitly: when _IS_WINDOWS is + # patched in tests on a POSIX host, os.path.isabs would reject + # ``C:\Users\x`` and mangle it through the relative branch. + import ntpath + if ntpath.isabs(expanded): + return expanded + if os.path.isabs(expanded): + return expanded + + candidate = os.path.abspath(expanded) + current = os.getcwd() + + # Common recovery for config values like ``hermes-agent`` when Hermes was + # launched from that directory already. ``os.path.abspath`` would point at + # a nonexistent nested ``./hermes-agent``; use the current directory instead. + if not os.path.isdir(candidate): + wanted_parts = Path(expanded).parts + current_parts = Path(current).parts + if wanted_parts and len(wanted_parts) <= len(current_parts): + if current_parts[-len(wanted_parts):] == wanted_parts: + return current + + return candidate + + def _windows_to_msys_path(cwd: str) -> str: """Translate a native Windows path (``C:\\Users\\x``) to Git Bash / MSYS form (``/c/Users/x``) so ``builtin cd`` resolves it reliably. @@ -1102,9 +1142,8 @@ class LocalEnvironment(BaseEnvironment): """ def __init__(self, cwd: str = "", timeout: int = 60, env: dict = None): - if cwd: - cwd = os.path.expanduser(cwd) - super().__init__(cwd=cwd or os.getcwd(), timeout=timeout, env=env) + cwd = _resolve_local_initial_cwd(cwd) + super().__init__(cwd=cwd, timeout=timeout, env=env) self.init_session() def get_temp_dir(self) -> str:
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#26790 fix: normalize local terminal relative cwd · merged via another change →
5458c76,#65475
Closed issues · 1
- Closed#26783terminal tool fails with `cd: hermes-agent: No such file or directory` when CWD is already the target directory
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- environments
- Merged PRs
- #65475
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 16 Jul 2026