hermes update command broken across install methods

NousResearch/hermes-agent · cluster 2648 · hermes cli

Data as of

Nothing open on GitHub. Fix merged in #3099. Check it covers your case.

The short answer

0open issues
0open PRs
17items grouped
Known?
Yes. 17 related items grouped here, 6 issues already closed.
Fix event
A fix was merged to main in PR #3099. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PRs #3099, #35224, #35248. That is separate from the fix event.
Still open
Nothing open on GitHub.

Multiple bugs and proposed fixes around hermes update failing under various installation scenarios, including uv tool installs, editable/PyPI installs, system Python, and Debian/Ubuntu PEP 668 restrictions due to incorrect pip or interpreter targeting.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #3099 fix: use sys.executable for pip in update commands to fix PEP 668

    merged to main

    files: hermes_cli/main.py

  • #35224 fix(update): export launcher virtualenv to uv (#35031)

    merged to main

    files: hermes_cli/main.py, tests/hermes_cli/test_cmd_update.py

    Show the diff
    • hermes_cli/main.py
      @@ -8929,7 +8929,10 @@ def _cmd_update_pip(args):
               cmd = [sys.executable, "-m", "pip", "install", "--upgrade", "hermes-agent"]
       
           print(f"→ Running: {' '.join(cmd)}")
      -    result = subprocess.run(cmd)
      +    run_kwargs = {}
      +    if sys.prefix != sys.base_prefix:
      +        run_kwargs["env"] = {**os.environ, "VIRTUAL_ENV": sys.prefix}
      +    result = subprocess.run(cmd, **run_kwargs)
           if result.returncode != 0:
               print("✗ Update failed")
               sys.exit(1)
    • tests/hermes_cli/test_cmd_update.py
      @@ -39,6 +39,45 @@ def mock_args():
           return SimpleNamespace()
       
       
      +class TestCmdUpdatePip:
      +    """Regression tests for pip-install update flows."""
      +
      +    @patch("shutil.which", return_value="/usr/bin/uv")
      +    @patch("subprocess.run")
      +    def test_update_pip_exports_virtualenv_from_sys_prefix(
      +        self, mock_run, _mock_which, mock_args, monkeypatch
      +    ):
      +        from hermes_cli import main as hm
      +
      +        mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="")
      +        monkeypatch.delenv("VIRTUAL_ENV", raising=False)
      +        monkeypatch.setattr(hm.sys, "prefix", "/tmp/hermes-launcher-venv")
      +        monkeypatch.setattr(hm.sys, "base_prefix", "/usr")
      +
      +        hm._cmd_update_pip(mock_args)
      +
      +        assert mock_run.call_count == 1
      +        assert mock_run.call_args.args[0] == ["/usr/bin/uv", "pip", "install", "--upgrade", "hermes-agent"]
      +        assert mock_run.call_args.kwargs["env"]["VIRTUAL_ENV"] == "/tmp/hermes-launcher-venv"
      +
      +    @patch("shutil.which", return_value="/usr/bin/uv")
      +    @patch("subprocess.run")
      +    def test_update_pip_does_not_export_virtualenv_for_system_python(
      +        self, mock_run, _mock_which, mock_args, monkeypatch
      +    ):
      +        from hermes_cli import main as hm
      +
      +        mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="")
      +        monkeypatch.delenv("VIRTUAL_ENV", raising=False)
      +        monkeypatch.setattr(hm.sys, "prefix", "/usr")
      +        monkeypatch.setattr(hm.sys, "base_prefix", "/usr")
      +
      +        hm._cmd_update_pip(mock_args)
      +
      +        assert mock_run.call_count == 1
      +        assert "env" not in mock_run.call_args.kwargs
      +
      +
       class TestCmdUpdateBranchFallback:
           """cmd_update falls back to main when current branch has no remote counterpart."""
       
  • #35248 fix(update): handle uv-tool, pipx, and system-Python installs in hermes update (#29700)

    merged to main

    files: hermes_cli/config.py, hermes_cli/main.py, scripts/release.py, tests/hermes_cli/test_uv_tool_update.py

    Show the diff
    • hermes_cli/config.py
      @@ -329,6 +329,34 @@ def stamp_install_method(method: str) -> None:
               pass
       
       
      +def is_uv_tool_install() -> bool:
      +    """Return True when the *running* Hermes lives in a ``uv tool`` layout.
      +
      +    ``uv tool install hermes-agent`` places the install at
      +    ``.../uv/tools/hermes-agent/...`` (default ``~/.local/share/uv/tools``,
      +    or ``$UV_TOOL_DIR/...``). Such installs live outside any virtualenv, so
      +    ``uv pip install`` fails with ``No virtual environment found`` and the
      +    update path must use ``uv tool upgrade`` instead.
      +
      +    Detection is intentionally restricted to properties of the running
      +    interpreter (``sys.prefix`` / ``sys.executable``). We deliberately do
      +    NOT consult ``uv tool list``: it would also return True when
      +    ``hermes-agent`` happens to be uv-tool-installed on the machine while
      +    the *active* Hermes is a regular pip/venv install, causing
      +    ``hermes update`` to upgrade the wrong copy. It would also block on a
      +    subprocess call (~seconds) just to compute a recommendation string.
      +    """
      +    def _has_uv_tool_marker(path: str) -> bool:
      +        norm = os.path.normpath(path).replace(os.sep, "/").lower()
      +        return "/uv/tools/hermes-agent/" in norm + "/"
      +
      +    if _has_uv_tool_marker(sys.prefix):
      +        return True
      +    if _has_uv_tool_marker(sys.executable or ""):
      +        return True
      +    return False
      +
      +
       def recommended_update_command_for_method(method: str) -> str:
           """Return the update command or guidance for a given install method."""
           if method == "nixos":
      @@ -338,9 +366,10 @@ def recommended_update_command_for_method(method: str) -> str:
           if method == "docker":
               return "docker pull nousresearch/hermes-agent:latest"
           if method == "pip":
      +        if is_uv_tool_install():
      +            return "uv tool upgrade hermes-agent"
               import shutil
      -        uv = shutil.which("uv")
      -        if uv:
      +        if shutil.which("uv"):
                   return "uv pip install --upgrade hermes-agent"
               return "pip install --upgrade hermes-agent"
           return "hermes update"
    • hermes_cli/main.py
      @@ -8971,19 +8971,49 @@ def cmd_update(args):
       def _cmd_update_pip(args):
           """Update Hermes via pip (for PyPI installs)."""
           from hermes_cli import __version__
      +    from hermes_cli.config import is_uv_tool_install
       
           print(f"→ Current version: {__version__}")
           print("→ Checking PyPI for updates...")
       
           uv = shutil.which("uv")
      -    if uv:
      +    in_venv = sys.prefix != sys.base_prefix
      +    # pipx-managed installs live under .../pipx/venvs/<name>/...
      +    pipx_managed = "pipx" in sys.prefix.split(os.sep)
      +    pipx = shutil.which("pipx") if pipx_managed else None
      +
      +    # Only the ``uv pip install`` path inside a venv needs VIRTUAL_ENV
      +    # exported (uv refuses to install without it when the launcher shim
      +    # didn't activate the venv). ``uv tool upgrade`` / ``pipx upgrade``
      +    # operate on a named environment and ignore VIRTUAL_ENV, so we don't
      +    # set it for them.
      +    export_virtualenv = False
      +
      +    if is_uv_tool_install():
      +        if not uv:
      +            print("✗ Detected a uv-tool install but `uv` is not on PATH; install uv and retry.")
      +            sys.exit(1)
      +        cmd = [uv, "tool", "upgrade", "hermes-agent"]
      +    elif pipx_managed and pipx:
      +        # pipx owns its own venv; ``pipx upgrade`` is the only correct path.
      +        # Matches scripts/auto-update.sh, which already uses pipx upgrade.
      +        cmd = [pipx, "upgrade", "hermes-agent"]
      +    elif uv:
               cmd = [uv, "pip", "install", "--upgrade", "hermes-agent"]
      +        if in_venv:
      +            # Launcher shim runs the venv interpreter but doesn't export
      +            # VIRTUAL_ENV; without it uv errors "No virtual environment found".
      +            export_virtualenv = True
      +        else:
      +            # Outside any venv, ``--system`` lets uv target the active
      +            # interpreter, matching pip's default behaviour.
      +            cmd.insert(3, "--system")
           else:
               cmd = [sys.executable, "-m", "pip", "install", "--upgrade", "hermes-agent"]
       
           print(f"→ Running: {' '.join(cmd)}")
           run_kwargs = {}
      -    if sys.prefix != sys.base_prefix:
      +    if export_virtualenv:
               run_kwargs["env"] = {**os.environ, "VIRTUAL_ENV": sys.prefix}
           result = subprocess.run(cmd, **run_kwargs)
           if result.returncode != 0:
    • scripts/release.py
      @@ -651,6 +651,7 @@
           "[redacted:email]": "alexzhu0",
           "[redacted:email]": "AntAISecurityLab",
           "[redacted:email]": "briandevans",
      +    "[redacted:email]": "inchargeautomation-lab",
           "[redacted:email]": "Bartok9",
           "[redacted:email]": "sweetcornna",
           "[redacted:email]": "CruxExperts",
    • tests/hermes_cli/test_uv_tool_update.py
      @@ -0,0 +1,311 @@
      +"""Tests for uv-tool install detection in the update path (issue #29700).
      +
      +``uv tool install hermes-agent`` lives outside any venv, so the previous
      +``uv pip install --upgrade`` update path failed with ``No virtual
      +environment found``. ``is_uv_tool_install`` should detect this layout and
      +both the user-facing recommended command and the actual
      +``_cmd_update_pip`` subprocess invocation should switch to
      +``uv tool upgrade hermes-agent``.
      +
      +Detection is restricted to properties of the running interpreter
      +(``sys.prefix`` / ``sys.executable``) so a pip/venv install on a machine
      +that also has ``uv tool install hermes-agent`` does not get misclassified.
      +"""
      +from __future__ import annotations
      +
      +import subprocess
      +from types import SimpleNamespace
      +from unittest.mock import patch
      +
      +import pytest
      +
      +
      +# ---------------------------------------------------------------------------
      +# is_uv_tool_install
      +# ---------------------------------------------------------------------------
      +
      +
      +class TestIsUvToolInstall:
      +    def test_returns_true_when_sys_prefix_matches_uv_tool_layout(self):
      +        from hermes_cli import config
      +
      +        with patch.object(config.sys, "prefix", "/home/user/.local/share/uv/tools/hermes-agent"):
      +            assert config.is_uv_tool_install() is True
      +
      +    def test_returns_true_when_sys_executable_matches_uv_tool_layout(self):
      +        """Some uv-tool layouts surface the marker on ``sys.executable`` (bin/python)."""
      +        from hermes_cli import config
      +
      +        with patch.object(config.sys, "prefix", "/some/unrelated/venv"), \
      +             patch.object(
      +                 config.sys,
      +                 "executable",
      +                 "/home/user/.local/share/uv/tools/hermes-agent/bin/python",
      +             ):
      +            assert config.is_uv_tool_install() is True
      +
      +    def test_returns_false_when_neither_prefix_nor_executable_matches(self):
      +        from hermes_cli import config
      +
      +        with patch.object(config.sys, "prefix", "/some/unrelated/venv"), \
      +             patch.object(config.sys, "executable", "/usr/bin/python3"):
      +            assert config.is_uv_tool_install() is False
      +
      +    def test_does_not_consult_uv_tool_list(self):
      +        """Detection must NOT shell out: ``uv tool list`` would false-positive
      +        when the active install is pip/venv but the machine also has
      +        ``uv tool install hermes-agent`` somewhere on disk. Copilot review on
      +        PR #29703 flagged this; the fix is to never call ``uv tool list``
      +        from the detection path."""
      +        from hermes_cli import config
      +
      +        with patch.object(config.sys, "prefix", "/some/unrelated/venv"), \
      +             patch.object(config.sys, "executable", "/usr/bin/python3"), \
      +             patch("subprocess.run") as mock_run:
      +            assert config.is_uv_tool_install() is False
      +            mock_run.assert_not_called()
      +
      +    def test_case_insensitive_match(self):
      +        """Match must be case-insensitive — Windows paths preserve case
      +        (e.g. ``...AppData\\Local\\UV\\Tools\\hermes-agent``) and a case-sensitive
      +        check would miss them. We exercise the lower-cased compare path here
      +        without monkey-patching ``os.sep``, which would break the whole suite."""
      +        from hermes_cli import config
      +
      +        with patch.object(
      +            config.sys, "prefix", "/HOME/USER/.local/share/UV/Tools/hermes-agent"
      +        ):
      +            assert config.is_uv_tool_install() is True
      +
      +    def test_handles_empty_executable(self):
      +        from hermes_cli import config
      +
      +        with patch.object(config.sys, "prefix", "/some/unrelated/venv"), \
      +             patch.object(config.sys, "executable", ""):
      +            assert config.is_uv_tool_install() is False
      +
      +
      +# ---------------------------------------------------------------------------
      +# recommended_update_command_for_method
      +# ---------------------------------------------------------------------------
      +
      +
      +class TestRecommendedUpdateCommandForUvTool:
      +    def test_uv_tool_install_recommends_uv_tool_upgrade(self):
      +        from hermes_cli import config
      +
      +        with patch("shutil.which", return_value="/usr/local/bin/uv"), \
      +             patch.object(config, "is_uv_tool_install", return_value=True):
      +            cmd = config.recommended_update_command_for_method("pip")
      +            assert cmd == "uv tool upgrade hermes-agent"
      +
      +    def test_uv_tool_install_recommends_uv_tool_upgrade_even_without_uv_on_path(self):
      +        """Recommendation reflects the *install method*, not whether ``uv`` is
      +        currently on PATH — the user needs to know the right command to run."""
      +        from hermes_cli import config
      +
      +        with patch("shutil.which", return_value=None), \
      +             patch.object(config, "is_uv_tool_install", return_value=True):
      +            cmd = config.recommended_update_command_for_method("pip")
      +            assert cmd == "uv tool upgrade hermes-agent"
      +
      +    def test_uv_pip_install_keeps_legacy_recommendation(self):
      +        """Existing behavior: uv is on PATH but Hermes is a regular pip install."""
      +        from hermes_cli import config
      +
      +        with patch("shutil.which", return_value="/usr/local/bin/uv"), \
      +             patch.object(config, "is_uv_tool_install", return_value=False):
      +            cmd = config.recommended_update_command_for_method("pip")
      +            assert cmd == "uv pip install --upgrade hermes-agent"
      +
      +    def test_no_uv_falls_back_to_plain_pip(self):
      +        from hermes_cli import config
      +
      +        with patch("shutil.which", return_value=None), \
      +             patch.object(config, "is_uv_tool_install", return_value=False):
      +            cmd = config.recommended_update_command_for_method("pip")
      +            assert cmd == "pip install --upgrade hermes-agent"
      +
      +    def test_recommendation_does_not_spawn_subprocess(self):
      +        """Computing the recommendation string must be cheap — no ``uv tool list``
      +        spawn. Copilot review on PR #29703 flagged the prior subprocess hop
      +        as adding overhead and a multi-second timeout window for what is
      +        purely a display string."""
      +        from hermes_cli import config
      +
      +        with patch.object(config.sys, "prefix", "/some/unrelated/venv"), \
      +             patch.object(config.sys, "executable", "/usr/bin/python3"), \
      +             patch("shutil.which", return_value="/usr/local/bin/uv"), \
      +             patch("subprocess.run") as mock_run:
      +            cmd = config.recommended_update_command_for_method("pip")
      +            mock_run.assert_not_called()
      +            assert cmd == "uv pip install --upgrade hermes-agent"
      +
      +
      +# ---------------------------------------------------------------------------
      +# _cmd_update_pip subprocess command
      +# ---------------------------------------------------------------------------
      +
      +
      +class TestCmdUpdatePipUsesUvTool:
      +    @patch("subprocess.run")
      +    def test_runs_uv_tool_upgrade_when_uv_tool_install(self, mock_run):
      +        """The actual subprocess invocation must switch to ``uv tool upgrade``."""
      +        from hermes_cli.main import _cmd_update_pip
      +
      +        mock_run.return_value = subprocess.CompletedProcess(["uv"], 0, stdout="", stderr="")
      +        with patch("shutil.which", return_value="/usr/local/bin/uv"), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=True):
      +            _cmd_update_pip(SimpleNamespace())
      +
      +        assert mock_run.call_args[0][0] == ["/usr/local/bin/uv", "tool", "upgrade", "hermes-agent"]
      +
      +    @patch("subprocess.run")
      +    def test_runs_uv_pip_install_when_not_uv_tool(self, mock_run):
      +        """Existing behavior preserved when uv is present but Hermes isn't a tool install."""
      +        from hermes_cli.main import _cmd_update_pip
      +
      +        mock_run.return_value = subprocess.CompletedProcess(["uv"], 0, stdout="", stderr="")
      +        with patch("shutil.which", return_value="/usr/local/bin/uv"), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=False):
      +            _cmd_update_pip(SimpleNamespace())
      +
      +        assert mock_run.call_args[0][0] == [
      +            "/usr/local/bin/uv",
      +            "pip",
      +            "install",
      +            "--upgrade",
      +            "hermes-agent",
      +        ]
      +
      +    @patch("subprocess.run")
      +    def test_falls_back_to_pip_when_no_uv(self, mock_run):
      +        from hermes_cli.main import _cmd_update_pip
      +
      +        mock_run.return_value = subprocess.CompletedProcess(["pip"], 0, stdout="", stderr="")
      +        with patch("shutil.which", return_value=None), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=False):
      +            _cmd_update_pip(SimpleNamespace())
      +
      +        cmd = mock_run.call_args[0][0]
      +        assert cmd[1:] == ["-m", "pip", "install", "--upgrade", "hermes-agent"]
      +
      +    @patch("subprocess.run")
      +    def test_exits_nonzero_on_subprocess_failure(self, mock_run):
      +        from hermes_cli.main import _cmd_update_pip
      +
      +        mock_run.return_value = subprocess.CompletedProcess(["uv"], 1, stdout="", stderr="")
      +        with patch("shutil.which", return_value="/usr/local/bin/uv"), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=True):
      +            with pytest.raises(SystemExit) as exc_info:
      +                _cmd_update_pip(SimpleNamespace())
      +        assert exc_info.value.code == 1
      +
      +    @patch("subprocess.run")
      +    def test_uv_tool_install_without_uv_on_path_exits_with_hint(self, mock_run):
      +        """If the running interpreter looks like a uv-tool install but ``uv`` is
      +        somehow missing from PATH, surface a clear hint instead of silently
      +        falling back to ``python -m pip``, which would either fail (no venv)
      +        or upgrade the wrong copy."""
      +        from hermes_cli.main import _cmd_update_pip
      +
      +        with patch("shutil.which", return_value=None), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=True):
      +            with pytest.raises(SystemExit) as exc_info:
      +                _cmd_update_pip(SimpleNamespace())
      +        assert exc_info.value.code == 1
      +        mock_run.assert_not_called()
      +
      +
      +# ---------------------------------------------------------------------------
      +# pipx-managed installs, --system fallback, and VIRTUAL_ENV overlay
      +# (issue #29700 / #35031 family — consolidated update-path handling)
      +# ---------------------------------------------------------------------------
      +
      +
      +class TestCmdUpdatePipInstallLayouts:
      +    """The uv pip path must adapt to where the running interpreter lives:
      +
      +    - inside a venv (launcher shim)  -> export VIRTUAL_ENV, no ``--system``
      +    - bare pip outside any venv      -> add ``--system``, no overlay
      +    - pipx-managed                   -> ``pipx upgrade``
      +    """
      +
      +    @patch("subprocess.run")
      +    def test_pipx_managed_uses_pipx_upgrade(self, mock_run, monkeypatch):
      +        from hermes_cli import main as hm
      +
      +        mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="")
      +        monkeypatch.setattr(hm.sys, "prefix", "/home/u/.local/pipx/venvs/hermes-agent")
      +        monkeypatch.setattr(hm.sys, "base_prefix", "/usr")
      +
      +        def _which(name):
      +            return {"uv": "/usr/bin/uv", "pipx": "/usr/bin/pipx"}.get(name)
      +
      +        with patch("shutil.which", side_effect=_which), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=False):
      +            hm._cmd_update_pip(SimpleNamespace())
      +
      +        assert mock_run.call_args[0][0] == ["/usr/bin/pipx", "upgrade", "hermes-agent"]
      +        # pipx upgrade ignores VIRTUAL_ENV; we must not set it.
      +        assert "env" not in mock_run.call_args.kwargs
      +
      +    @patch("subprocess.run")
      +    def test_pipx_layout_without_pipx_binary_treated_as_venv(
      +        self, mock_run, monkeypatch
      +    ):
      +        from hermes_cli import main as hm
      +
      +        mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="")
      +        monkeypatch.setattr(hm.sys, "prefix", "/home/u/.local/pipx/venvs/hermes-agent")
      +        monkeypatch.setattr(hm.sys, "base_prefix", "/usr")
      +
      +        # pipx layout detected via prefix, but pipx binary missing on PATH.
      +        def _which(name):
      +            return "/usr/bin/uv" if name == "uv" else None
      +
      +        with patch("shutil.which", side_effect=_which), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=False):
      +            hm._cmd_update_pip(SimpleNamespace())
      +
      +        # prefix != base_prefix, so this is treated as a venv -> overlay, no --system.
      +        assert mock_run.call_args[0][0] == [
      +            "/usr/bin/uv", "pip", "install", "--upgrade", "hermes-agent",
      +        ]
      +        assert mock_run.call_args.kwargs["env"]["VIRTUAL_ENV"].endswith("hermes-agent")
      +
      +    @patch("subprocess.run")
      +    def test_bare_pip_outside_venv_adds_system(self, mock_run, monkeypatch):
      +        from hermes_cli import main as hm
      +
      +        mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="")
      +        # No venv: prefix == base_prefix.
      +        monkeypatch.setattr(hm.sys, "prefix", "/usr")
      +        monkeypatch.setattr(hm.sys, "base_prefix", "/usr")
      +
      +        with patch("shutil.which", return_value="/usr/bin/uv"), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=False):
      +            hm._cmd_update_pip(SimpleNamespace())
      +
      +        assert mock_run.call_args[0][0] == [
      +            "/usr/bin/uv", "pip", "install", "--system", "--upgrade", "hermes-agent",
      +        ]
      +        assert "env" not in mock_run.call_args.kwargs
      +
      +    @patch("subprocess.run")
      +    def test_venv_exports_virtualenv_and_omits_system(self, mock_run, monkeypatch):
      +        from hermes_cli import main as hm
      +
      +        mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="")
      +        monkeypatch.delenv("VIRTUAL_ENV", raising=False)
      +        monkeypatch.setattr(hm.sys, "prefix", "/home/u/.hermes/hermes-agent/venv")
      +        monkeypatch.setattr(hm.sys, "base_prefix", "/usr")
      +
      +        with patch("shutil.which", return_value="/usr/bin/uv"), \
      +             patch("hermes_cli.config.is_uv_tool_install", return_value=False):
      +            hm._cmd_update_pip(SimpleNamespace())
      +
      +        cmd = mock_run.call_args[0][0]
      +        assert "--system" not in cmd
      +        assert cmd == ["/usr/bin/uv", "pip", "install", "--upgrade", "hermes-agent"]
      +        assert mock_run.call_args.kwargs["env"]["VIRTUAL_ENV"] == "/home/u/.hermes/hermes-agent/venv"

8 PRs closed without merging

From the close comments: merged via another change 3 · closed without comment 1 · done elsewhere 1 · duplicate 1 · superseded by another PR 1 · other 1

  • #2655 fix: use sys.executable for pip in update script to fix PEP 668 (#2648) · merged via another change
  • #2715 fix(cli): use venv pip in update command instead of system pip · done elsewhere → #3099, #2655
  • #29701 fix(cli): add --system flag to uv pip install for uv tool installs (#29700) · closed without comment
  • #29703 fix(cli): use `uv tool upgrade` when Hermes is a uv tool install (#29700) · merged via another change → #35248
  • #29707 fix(cli): use 'uv tool upgrade' when hermes was installed via 'uv tool install' (#29700) · superseded by another PR → #35248
Show 3 more
  • #35064 fix(update): export launcher virtualenv to uv (#35031) · merged via another change → #35224
  • #48726 fix(cli): pin --python and add --break-system-packages for no-venv uv update · other → #48721
  • #48756 fix(cli): target correct Python in no-venv uv update path (#48721) · duplicate → #48726, #48721
Closed issues · 6
  • Closed#2648Update script uses bare 'pip' instead of venv pip, fails on Debian/Ubuntu with externally-managed-environment error
  • Closed#29700`hermes update` fails when Hermes is installed via `uv tool install`
  • Closed#35031hermes update fails with 'No virtual environment found' when launcher venv is not active in shell
  • Closed#35105[Bug]: `hermes update` fails on a PyPI/editable install when `uv` is available
  • Closed#39444fix: `hermes update` fails with 'No virtual environment found' when using uv
  • Closed#48721[Bug]: hermes update on system Python targets wrong interpreter and hits PEP 668 (no-venv + uv branch)
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.10: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means 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 Lab

At a glance

Subsystem
hermes cli
Merged PRs
#3099, #35224, #35248
Closed issues
6
Attempts
8 PRs closed without merging
Fix link checked
30 May 2026

For your agent

The same facts, typed.