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
24 changes: 18 additions & 6 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,26 @@ def load_cli_config() -> Dict[str, Any]:
if "backend" in terminal_config:
terminal_config["env_type"] = terminal_config["backend"]

# Handle special cwd values: "." or "auto" means use current working directory.
# Only resolve to the host's CWD for the local backend where the host
# filesystem is directly accessible. For ALL remote/container backends
# (ssh, docker, modal, singularity), the host path doesn't exist on the
# target -- remove the key so terminal_tool.py uses its per-backend default.
# Handle special cwd values: "." or "auto" means use a concrete working
# directory. For local backends, prefer an already-established TERMINAL_CWD
# (e.g. set by gateway/run.py from MESSAGING_CWD) so we do not clobber the
# gateway's workspace with os.getcwd(). For remote/container backends,
# remove the key so terminal_tool.py uses its per-backend default.
if terminal_config.get("cwd") in (".", "auto", "cwd"):
effective_backend = terminal_config.get("env_type", "local")
if effective_backend == "local":
existing_terminal_cwd = os.environ.get("TERMINAL_CWD", "").strip()
if effective_backend == "local" and existing_terminal_cwd:
try:
if Path(existing_terminal_cwd).expanduser().is_dir():
terminal_config["cwd"] = existing_terminal_cwd
defaults["terminal"]["cwd"] = existing_terminal_cwd
else:
terminal_config["cwd"] = os.getcwd()
defaults["terminal"]["cwd"] = terminal_config["cwd"]
except OSError:
terminal_config["cwd"] = os.getcwd()
defaults["terminal"]["cwd"] = terminal_config["cwd"]
elif effective_backend == "local":
terminal_config["cwd"] = os.getcwd()
defaults["terminal"]["cwd"] = terminal_config["cwd"]
else:
Expand Down
48 changes: 48 additions & 0 deletions tests/cli/test_cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,54 @@ def test_normalize_root_model_keys_does_not_override_existing(self):
assert "provider" not in result # root key still cleaned up


class TestTerminalCwdPrecedence:
"""load_cli_config() should respect an already-set TERMINAL_CWD."""

def test_existing_terminal_cwd_wins_over_dot_cwd(self, tmp_path, monkeypatch):
import yaml
import cli

hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(yaml.safe_dump({"terminal": {"env_type": "local", "cwd": "."}}))

workspace = tmp_path / "messaging-workspace"
workspace.mkdir()
system_cwd = tmp_path / "systemd-working-dir"
system_cwd.mkdir()

monkeypatch.setattr(cli, "_hermes_home", hermes_home)
monkeypatch.setenv("TERMINAL_CWD", str(workspace))
monkeypatch.chdir(system_cwd)

cfg = cli.load_cli_config()

assert cfg["terminal"]["cwd"] == str(workspace)
assert os.environ["TERMINAL_CWD"] == str(workspace)

def test_missing_terminal_cwd_falls_back_to_os_cwd(self, tmp_path, monkeypatch):
import yaml
import cli

hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(yaml.safe_dump({"terminal": {"env_type": "local", "cwd": "."}}))

system_cwd = tmp_path / "systemd-working-dir"
system_cwd.mkdir()

monkeypatch.setattr(cli, "_hermes_home", hermes_home)
monkeypatch.delenv("TERMINAL_CWD", raising=False)
monkeypatch.chdir(system_cwd)

cfg = cli.load_cli_config()

assert cfg["terminal"]["cwd"] == str(system_cwd)
assert os.environ["TERMINAL_CWD"] == str(system_cwd)


class TestProviderResolution:
def test_api_key_is_string_or_none(self):
cli = _make_cli()
Expand Down