feat: self update npm deps on hermes update

This commit is contained in:
Brooklyn Nicholson 2026-04-11 18:29:18 -05:00
parent ec553fdb49
commit a1d2a0c0fd
2 changed files with 81 additions and 8 deletions

View file

@ -3133,6 +3133,8 @@ def _update_via_zip(args):
)
_install_python_dependencies_with_optional_fallback(pip_cmd)
_update_node_dependencies()
# Sync skills
try:
from tools.skills_sync import sync_skills
@ -3652,9 +3654,42 @@ def _install_python_dependencies_with_optional_fallback(
print(f" ⚠ Skipped optional extras that still failed: {', '.join(failed_extras)}")
def _update_node_dependencies() -> None:
npm = shutil.which("npm")
if not npm:
return
paths = (
("repo root", PROJECT_ROOT),
("ui-tui", PROJECT_ROOT / "ui-tui"),
)
if not any((path / "package.json").exists() for _, path in paths):
return
print("→ Updating Node.js dependencies...")
for label, path in paths:
if not (path / "package.json").exists():
continue
result = subprocess.run(
[npm, "install", "--silent", "--no-fund", "--no-audit", "--progress=false"],
cwd=path,
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0:
print(f"{label}")
continue
print(f" ⚠ npm install failed in {label}")
stderr = (result.stderr or "").strip()
if stderr:
print(f" {stderr.splitlines()[-1]}")
def cmd_update(args):
"""Update Hermes Agent to the latest version."""
import shutil
from hermes_cli.config import is_managed, managed_error
if is_managed():
@ -3873,13 +3908,8 @@ def cmd_update(args):
)
_install_python_dependencies_with_optional_fallback(pip_cmd)
# Check for Node.js deps
if (PROJECT_ROOT / "package.json").exists():
import shutil
if shutil.which("npm"):
print("→ Updating Node.js dependencies...")
subprocess.run(["npm", "install", "--silent"], cwd=PROJECT_ROOT, check=False)
_update_node_dependencies()
print()
print("✓ Code updated!")

View file

@ -106,6 +106,49 @@ class TestCmdUpdateBranchFallback:
pull_cmds = [c for c in commands if "pull" in c]
assert len(pull_cmds) == 0
@patch("shutil.which")
@patch("subprocess.run")
def test_update_refreshes_repo_and_tui_node_dependencies(
self, mock_run, mock_which, mock_args
):
mock_which.side_effect = {"uv": "/usr/bin/uv", "npm": "/usr/bin/npm"}.get
mock_run.side_effect = _make_run_side_effect(
branch="main", verify_ok=True, commit_count="1"
)
cmd_update(mock_args)
npm_calls = [
(call.args[0], call.kwargs.get("cwd"))
for call in mock_run.call_args_list
if call.args and call.args[0][0] == "/usr/bin/npm"
]
assert npm_calls == [
(
[
"/usr/bin/npm",
"install",
"--silent",
"--no-fund",
"--no-audit",
"--progress=false",
],
PROJECT_ROOT,
),
(
[
"/usr/bin/npm",
"install",
"--silent",
"--no-fund",
"--no-audit",
"--progress=false",
],
PROJECT_ROOT / "ui-tui",
),
]
def test_update_non_interactive_skips_migration_prompt(self, mock_args, capsys):
"""When stdin/stdout aren't TTYs, config migration prompt is skipped."""
with patch("shutil.which", return_value=None), patch(