Skip to content
Merged
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
10 changes: 10 additions & 0 deletions hermes_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,8 @@ def _ensure_tui_node() -> None:
env={**os.environ, "HERMES_HOME": hermes_home},
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
check=False,
)
except (OSError, subprocess.SubprocessError):
Expand Down Expand Up @@ -1647,6 +1649,8 @@ def _node_bin(bin: str) -> str:
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace",
env={**os.environ, "CI": "1"},
)
if result.returncode != 0:
Expand All @@ -1671,6 +1675,8 @@ def _node_bin(bin: str) -> str:
cwd=str(ink_dir),
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
if result.returncode != 0:
combined = f"{result.stdout or ''}{result.stderr or ''}".strip()
Expand Down Expand Up @@ -1699,6 +1705,8 @@ def _node_bin(bin: str) -> str:
cwd=str(tui_dir),
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
if result.returncode != 0:
combined = f"{result.stdout or ''}{result.stderr or ''}".strip()
Expand Down Expand Up @@ -2372,6 +2380,8 @@ def cmd_whatsapp(args):
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace",
)
except KeyboardInterrupt:
print("\n ✗ Install cancelled")
Expand Down
39 changes: 39 additions & 0 deletions tests/hermes_cli/test_tui_npm_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ def _touch_tui_entry(root: Path) -> None:
entry.write_text("console.log('tui')")


def _assert_utf8_replace_capture(kwargs: dict) -> None:
assert kwargs["text"] is True
assert kwargs["encoding"] == "utf-8"
assert kwargs["errors"] == "replace"


def test_need_install_when_ink_missing(tmp_path: Path, main_mod) -> None:
(tmp_path / "package-lock.json").write_text("{}")
assert main_mod._tui_need_npm_install(tmp_path) is True
Expand Down Expand Up @@ -228,6 +234,8 @@ def fake_run(*args, **kwargs):
"--include-workspace-root=false",
]
assert calls[0][1]["cwd"] == str(tmp_path)
_assert_utf8_replace_capture(calls[0][1])
_assert_utf8_replace_capture(calls[1][1])


def test_make_tui_argv_keeps_desktop_workspace_install_behaviour(
Expand Down Expand Up @@ -263,6 +271,8 @@ def fake_run(*args, **kwargs):
"--progress=false",
]
assert calls[0][1]["cwd"] == str(tmp_path)
_assert_utf8_replace_capture(calls[0][1])
_assert_utf8_replace_capture(calls[1][1])


def test_make_tui_argv_keeps_desktop_always_build_behaviour(
Expand All @@ -286,6 +296,35 @@ def fake_run(*args, **kwargs):

assert calls
assert calls[0][0][0] == ["/bin/npm", "run", "build"]
_assert_utf8_replace_capture(calls[0][1])


def test_make_tui_argv_decodes_dev_prebuild_with_utf8_replace(
tmp_path: Path, main_mod, monkeypatch
) -> None:
ink_dir = tmp_path / "packages" / "hermes-ink"
ink_dir.mkdir(parents=True)
tsx = tmp_path / "node_modules" / ".bin" / "tsx"
tsx.parent.mkdir(parents=True)
tsx.write_text("")

monkeypatch.setattr(main_mod, "_tui_need_npm_install", lambda _root: False)
monkeypatch.setattr(main_mod.shutil, "which", lambda name: f"/bin/{name}")
calls = []

def fake_run(*args, **kwargs):
calls.append((args, kwargs))
return types.SimpleNamespace(returncode=0, stdout="", stderr="")

monkeypatch.setattr(main_mod.subprocess, "run", fake_run)

argv, cwd = main_mod._make_tui_argv(tmp_path, tui_dev=True)

assert argv == [str(tsx), "src/entry.tsx"]
assert cwd == tmp_path
assert calls[0][0][0] == ["/bin/npm", "run", "build"]
assert calls[0][1]["cwd"] == str(ink_dir)
_assert_utf8_replace_capture(calls[0][1])


# ── _workspace_root helper ──────────────────────────────────────────
Expand Down
Loading