feat: wire ensure_dependency into TUI and browser tool call sites

Before: missing node → hard exit; missing browser → FileNotFoundError.
After: both try ensure_dependency() first, which prompts interactively
and delegates installation to install.sh --ensure.

ripgrep and ffmpeg already degrade gracefully (grep fallback, skip
conversion) so they don't need wiring.

Also documents the design rationale in dep_ensure.py: detection and
prompting live in Python (portable, instant, UX-integrated); only
the actual installation delegates to install.sh (1900 lines of
battle-tested OS/package-manager logic).
This commit is contained in:
alt-glitch 2026-05-15 13:28:21 +00:00 committed by Teknium
parent e38a478c05
commit c57709a3d6
3 changed files with 36 additions and 3 deletions

View file

@ -1,7 +1,17 @@
"""Lazy dependency bootstrapper for non-Python runtime deps.
Wraps install.sh --ensure to install node, browser, ripgrep, ffmpeg
on first use. Prompts interactively unless told not to.
Detection and prompting live here in Python not in install.sh because:
1. shutil.which() works on every platform; install.sh needs bash.
2. Detection is instant; spawning bash for a "is node installed?" check is waste.
3. Python controls the UX (rich prompts, non-interactive fallback, TTY detection).
install.sh is still the *installation* backend because it has 1900 lines of
battle-tested OS detection and package-manager logic (apt/brew/pacman/dnf/
zypper/Termux/). Reimplementing that in Python would be huge duplication.
Deps that degrade gracefully (ripgrep grep fallback, ffmpeg skip conversion)
don't need ensure_dependency wired in — only hard-fail sites do (TUI needs node,
browser tool needs agent-browser).
"""
from __future__ import annotations

View file

@ -1042,6 +1042,13 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
if env_node and os.path.isfile(env_node) and os.access(env_node, os.X_OK):
return env_node
path = shutil.which(bin)
if not path and bin == "node":
try:
from hermes_cli.dep_ensure import ensure_dependency
if ensure_dependency("node"):
path = shutil.which("node")
except Exception:
pass
if not path:
print(f"{bin} not found — install Node.js to use the TUI.")
sys.exit(1)