Skip to content

feat: PyPI wheel packaging — pip install hermes-agent && hermes#26350

Closed
alt-glitch wants to merge 19 commits into
mainfrom
hermes/hermes-5ac74b48
Closed

feat: PyPI wheel packaging — pip install hermes-agent && hermes#26350
alt-glitch wants to merge 19 commits into
mainfrom
hermes/hermes-5ac74b48

Conversation

@alt-glitch

@alt-glitch alt-glitch commented May 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Makes pip install hermes-agent && hermes a seamless experience:

  • CI builds frontend assets (web dashboard + TUI) into the wheel before uv build
  • hermes update detects pip installs and runs pip install --upgrade instead of git pull
  • hermes update --check queries PyPI for pip users instead of git rev-list
  • hermes postinstall — one-shot bootstrap for non-Python deps (node, browser, ripgrep, ffmpeg) + runs setup
  • ensure_dependency() wired into TUI and browser tool — prompts to install missing deps on first use
  • install.sh --ensure and --postinstall modes for targeted dep bootstrap
  • Gateway service units only include existing PATH dirs (no broken paths for pip installs)
  • hermes doctor --fix generates config from defaults when template files are missing

What works from pip install

Feature Status
Agent loop (chat) Works — no node needed
hermes setup Works — first-run guard auto-triggers
hermes dashboard Works — web_dist shipped in wheel
hermes --tui Prompts to install node, then works
Browser tools Prompts to install agent-browser, then works
hermes update Detects pip, runs upgrade
hermes doctor Works without template files

Test plan

  • 17 unit tests (all new functions covered)
  • E2E Docker tests: 3 scenarios (bare, no-node, with-node) — 33/33 passing
  • Verified hermes update --check pip-aware (was the one failure, now fixed)
  • Existing test suite: fixed 3 regressions from behavioral changes
  • /simplify code review: DRY cleanup, no TOCTOU issues, browser name list aligned

alt-glitch added 12 commits May 15, 2026 13:11
For pip-installed hermes-agent (no .git directory), fall back to
querying PyPI's JSON API to compare __version__ against the latest
published release, using stdlib only (urllib + json, no packaging dep).
…bootstrap

Adds --ensure DEPS for pip-runtime dep installation and --postinstall
for pip users who want the full post-install experience without cloning.
When cli-config.yaml.example is not present (e.g. pip wheel install),
fall back to writing DEFAULT_CONFIG via save_config() instead of
warning and requiring a manual fix.
…hermes/node_modules

Extract PATH building into _build_service_path_dirs() that skips directories
which don't exist on disk (e.g. node_modules/.bin for pip installs) and also
includes ~/.hermes/node/bin and ~/.hermes/node_modules/.bin for agent-browser.
…m build

Add _find_bundled_tui() that checks for hermes_cli/tui_dist/entry.js
(present in wheel installs) and wire it into _make_tui_argv() between
the HERMES_TUI_DIR prebuilt path and the npm install fallback.
…command

Adds detect_install_method() to identify nixos/homebrew/git/pip installs,
and recommended_update_command_for_method() to return the right upgrade command
for each method. Updates recommended_update_command() to use these for pip-installed
instances (no .git dir, not managed).
When .git is absent and detect_install_method returns "pip", fork
hermes update to run `uv pip install --upgrade hermes-agent` (or
`python -m pip install --upgrade hermes-agent` as fallback) instead of
hard-exiting with "Not a git repository".
…ffold

Match the full set of subdirs created by install.sh: pairing, hooks,
image_cache, audio_cache, and skills are now pre-created alongside the
existing cron, sessions, logs, logs/curator, and memories dirs. This
makes hermes doctor checks cleaner without changing any runtime behaviour.
Includes paired change: browser tool now searches ~/.hermes/node_modules/.bin/
for agent-browser installed via install.sh --ensure browser.
…ate update command

- banner.py: remove redundant `import json as _json` (json already at module level)
- main.py: _cmd_update_pip now delegates to recommended_update_command_for_method
  instead of duplicating the uv-vs-pip detection logic
- main.py: remove redundant `import subprocess as _sp` (subprocess already at module level)
_cmd_update_check() had its own `.git` gate separate from _cmd_update_impl.
For pip installs, fork to _check_via_pypi() and display the result with
the correct recommended_update_command().
@alt-glitch alt-glitch requested a review from a team May 15, 2026 13:12
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-5ac74b48 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8268 on HEAD, 8268 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4316 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch force-pushed the hermes/hermes-5ac74b48 branch from fb4f5f4 to 9913545 Compare May 15, 2026 13:14
@alt-glitch alt-glitch added type/feature New feature or request P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard python:uv Pull requests that update python:uv code labels May 15, 2026
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).
One-shot bootstrap that installs non-Python deps (node, browser,
ripgrep, ffmpeg) via ensure_dependency(), then runs setup if no
provider is configured. Closes the gap between `pip install` and
the full user-facing experience.

Also fixes 3 pre-existing test regressions caused by earlier commits:
- test_recommended_update_command: mock detect_install_method for git env
- test_check_for_updates_no_git_dir: now falls back to PyPI, not None
- test_plist_path_includes_node_modules_bin: skip when dir absent
… CLI reference

Document pip install hermes-agent as a first-class install option.
Clarify that PyPI releases track tagged versions (major/minor),
not every commit on main — git installer is for bleeding-edge.
- dep_ensure.py: use get_hermes_home() instead of hand-rolled env var
- dep_ensure.py: add "chrome" to browser name list (was inconsistent with browser_tool.py)
- main.py _cmd_update_check: use detect_install_method() directly instead of redundant .git check
- main.py _cmd_update_pip: build command list directly instead of fragile split() on display string
- banner.py: rename _check_via_pypi → check_via_pypi (cross-module public API)
… --check description

- installation.md: add tip about `hermes postinstall` for upfront dep install
- quickstart.md: show `hermes postinstall` in pip install flow
- updating.md: fix --check description to mention PyPI path for pip installs
@alt-glitch alt-glitch force-pushed the hermes/hermes-5ac74b48 branch from 86c3468 to 349f054 Compare May 15, 2026 13:56
@alt-glitch alt-glitch changed the title feat(pypi): pip install hermes-agent — ship pre-built frontend assets, runtime dep bootstrap, pip-aware update feat: PyPI wheel packaging — pip install hermes-agent && hermes May 15, 2026
Comment thread hermes_cli/config.py
return None


def detect_install_method(project_root: Optional[Path] = None) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we toss a docker detection in here too? or maybe we can simply write an INSTALL_METHOD file to ~/.hermes or something?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added both. detect_install_method() now checks ~/.hermes/.install_method first (stamped by installers), then falls back to heuristics (managed → docker → .git → pip). Docker detection checks /.dockerenv and /proc/1/cgroup. install.sh stamps 'git', Dockerfile stamps 'docker', hermes postinstall stamps 'pip'.

Comment thread hermes_cli/dep_ensure.py
@@ -0,0 +1,106 @@
"""Lazy dependency bootstrapper for non-Python runtime deps.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't love the idea that we're duplicating the list of required packages (ffmpeg etc) in even more places (nix , dockerfile, install.sh..), but you gotta do what you gotta do..

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's inherent to multi-ecosystem projects — Nix uses nixpkgs attrs (nodejs_22, ripgrep), Dockerfile uses apt names (nodejs, ripgrep), install.sh has its own install functions. The Python side is centralized in dep_ensure.py (single _DEP_CHECKS dict), and main.py just iterates its keys. Cross-language sharing isn't worth the complexity — they're fundamentally different toolchains.

**Option A — pip (simplest):**

```bash
pip install hermes-agent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we recommend uv pip here? :3

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — switched all docs to uv pip install as the default. Added a tip for folks without uv pointing them to the install script.


### pip installs

PyPI releases track **tagged versions** (major and minor releases), not every commit on `main`. Check for updates and upgrade with:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another path that only tracks tagged versions is pushing our need for release management hard. I think me & you & @teknium1 & one or two other folks should have a chat and make sure we're ready for this internally before we merge docs with this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — the docs shouldn't go live until we've aligned on release cadence. For now this PR just adds the plumbing (detect pip, update --check queries PyPI, etc). The doc changes are descriptive of what the code does if/when we ship to PyPI, not a public promise. Happy to gate the doc pages behind a flag or move them to a draft section until we finalize the release workflow with the team.

Comment thread .gitignore
models-dev-upstream/
hermes_cli/tui_dist/*
hermes_cli/scripts/
docs/superpowers/* No newline at end of file

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed — was there for wheel build artifacts (install.sh gets copied to hermes_cli/scripts/ during CI build). Not needed in the gitignore since that only happens in the CI workflow.

@teknium1

Copy link
Copy Markdown
Contributor

Merged via #26593 with all 19 commits cherry-picked and your authorship preserved end-to-end. Thanks for the thorough work — ensure_dependency() is exactly the right shape for the pip-install case, and the Docker E2E matrix gave the salvage a lot of confidence.

Tests: 315 passed (your 17 new + the surrounding ACP + hermes_cli suites). Wheel build confirmed to include hermes_cli/scripts/install.sh, hermes_cli/tui_dist/entry.js, and hermes_cli/web_dist/**.

One small follow-up I'll handle in a separate PR: the recently-merged #26234 added hermes acp --setup-browser shipping its own acp_adapter/bootstrap/bootstrap_browser_tools.{sh,ps1}. After your PR landed, both paths exist for browser-tool bootstrap. I'll consolidate so --setup-browser calls ensure_dependency("browser") and retire the standalone scripts — your pattern is the generalization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists python:uv Pull requests that update python:uv code type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants