fix(pm): make bootstrap and bundle ownership explicit
Finish bootstrap uv before PM replaces its store entry. Keep failure receipts stdlib-only and align the cryptography requirement and override with the locked version. Let bundle builders declare launch paths and update ownership. Remove payload discovery, Store probing, and the unused develop command. Derive Nix Python from the PM lock and share its provenance stamp. Document setup, activation, optional dependencies, and distribution ownership. Targeted Windows tests, relocated runtime launches, Electron bundling, and bilingual docs builds pass. Native Nix and signed-package acceptance remain CI gates.
This commit is contained in:
@@ -34,109 +34,112 @@ We value contributions in this order:
|
||||
| Requirement | Notes |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| **Git** | With the `git-lfs` extension installed |
|
||||
| **Python 3.14** | uv will install it if missing |
|
||||
| **Python 3.14** | The project requires `>=3.14,<3.15`. PM provides the pinned interpreter. |
|
||||
| **uv** | Fast Python package manager ([install](https://docs.astral.sh/uv/)) |
|
||||
| **Node.js 26+** | Optional — needed for browser tools and WhatsApp bridge (matches root `package.json` engines) |
|
||||
| **Node.js** | Use the PM pin or a version accepted by root `package.json` engines |
|
||||
|
||||
### Install with the standard installer
|
||||
### PM developer environment
|
||||
|
||||
For most contributors, the best development bootstrap is the same path users
|
||||
take: run the standard installer, then work inside the repository it cloned.
|
||||
The installer creates the Hermes venv, wires the `hermes` command, stamps the
|
||||
install method for `hermes update`, and clones the full git project into
|
||||
`$HERMES_HOME/hermes-agent` (usually `~/.hermes/hermes-agent`). That keeps your
|
||||
development environment on the same layout the CLI, updater, lazy dependency
|
||||
installer, gateway, and docs assume.
|
||||
Use the [PM developer workflow](/reference/package-management#developer-workflow) for preparation, activation, everyday commands,
|
||||
dependency changes, and test environments. Select your development
|
||||
home before setup so experimental code does not migrate production data.
|
||||
|
||||
After successful setup, activate from the repository root in each new shell.
|
||||
|
||||
Bash:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
|
||||
cd "${HERMES_HOME:-$HOME/.hermes}/hermes-agent"
|
||||
|
||||
# Add dev/test extras on top of the standard install.
|
||||
uv pip install -e ".[all,dev]"
|
||||
|
||||
# Optional: browser tools / docs site dependencies.
|
||||
npm install
|
||||
source ./activate
|
||||
python hermes --version
|
||||
```
|
||||
|
||||
After that, create branches and run tests from that checkout:
|
||||
PowerShell:
|
||||
|
||||
```powershell
|
||||
. .\activate.ps1
|
||||
python hermes --version
|
||||
```
|
||||
|
||||
Run `python hermes` for this checkout, not a global `hermes` alias. PM activation
|
||||
adds installed tools and the selected dependency tree. It does not install
|
||||
packages or JS workspaces. `deactivate` restores the prior shell environment.
|
||||
|
||||
### Manual development and test environment {#manual-development-and-test-environment}
|
||||
|
||||
Use Python 3.14 (`>=3.14,<3.15`). Keep a development environment outside the
|
||||
source tree if an agent will operate on that checkout. Leave PM activation
|
||||
before this sequence. Keep the same development `HERMES_HOME` when running it.
|
||||
|
||||
This environment is for tests and editor tools. Its own interpreter includes
|
||||
pytest without relying on PM's `PYTHONPATH`. On Windows, initialize the native
|
||||
C++ build environment for your architecture before building source dependencies.
|
||||
|
||||
POSIX:
|
||||
|
||||
```bash
|
||||
git checkout -b fix/description
|
||||
scripts/run_tests.sh
|
||||
uv venv "$HOME/.hermes/venvs/hermes-dev" --python 3.14
|
||||
export UV_PROJECT_ENVIRONMENT="$HOME/.hermes/venvs/hermes-dev"
|
||||
uv sync --locked --extra all --extra dev
|
||||
export HERMES_PYTHON="$UV_PROJECT_ENVIRONMENT/bin/python"
|
||||
"$HERMES_PYTHON" hermes --version
|
||||
```
|
||||
|
||||
You can also run a fully isolated Hermes instance (throwaway HERMES_HOME, separate Electron
|
||||
userData, distinct Electron app name to avoid the single-instance lock):
|
||||
PowerShell:
|
||||
|
||||
```powershell
|
||||
$devEnv = Join-Path $env:LOCALAPPDATA 'hermes-dev-env'
|
||||
uv venv $devEnv --python 3.14
|
||||
$env:UV_PROJECT_ENVIRONMENT = $devEnv
|
||||
uv sync --locked --extra all --extra dev
|
||||
$env:HERMES_PYTHON = Join-Path $devEnv 'Scripts/python.exe'
|
||||
& $env:HERMES_PYTHON hermes --version
|
||||
```
|
||||
|
||||
This environment is for source development and tests. It does not replace
|
||||
PM's tool store or a packaged app's dependency selection.
|
||||
Run `uv pip check --python` with this environment's interpreter to check its dependencies.
|
||||
Do not install into an MSIX payload or point a bundled app at this environment.
|
||||
|
||||
For an isolated development instance, select a disposable `HERMES_HOME` before
|
||||
starting the source command. Use `python hermes setup` to configure it rather
|
||||
than copying production credentials into the checkout.
|
||||
|
||||
### JavaScript workspaces and website
|
||||
|
||||
From the repository root, run `npm ci` for the desktop, TUI, dashboard, and
|
||||
shared JS workspaces. The website is separate:
|
||||
|
||||
```bash
|
||||
scripts/dev-sandbox.sh python -m hermes_cli.main
|
||||
scripts/dev-sandbox.sh --persistent python -m hermes_cli.main desktop # state survives restarts, but lives in the worktree :)
|
||||
npm ci --prefix website
|
||||
npm run build:fast --prefix website
|
||||
```
|
||||
|
||||
### Manual clone fallback
|
||||
Use a Node/npm version accepted by the corresponding `package.json` engines.
|
||||
Native desktop dependencies can also require the platform build toolchain.
|
||||
|
||||
Use this only if you intentionally do not want Hermes' managed install layout
|
||||
(for example, a throwaway clone inside a container or CI job). If you install
|
||||
this way, make sure you run the `hermes` entrypoint from this venv; running the
|
||||
system `python3 -m hermes_cli.main` can pick up unrelated system Python
|
||||
packages.
|
||||
Logos and icons are generated from `assets/nous-girl-*.svg` and
|
||||
`assets/backgrounds/`. `node scripts/generate-icons.mjs` uses the locked,
|
||||
isolated `icon-build` dependency group. Do not commit generated PNG/ICO/ICNS
|
||||
outputs or add icon renderers to production dependencies.
|
||||
|
||||
Create the venv **outside** the cloned source tree. A venv that lives inside
|
||||
the directory the agent operates from can be wiped by a relative-path command
|
||||
the agent runs against its own checkout (`rm -rf venv`, `uv venv venv`, etc.),
|
||||
which silently destroys the running runtime mid-session. Keeping it outside the
|
||||
tree means no relative path from the workspace resolves to it.
|
||||
### Run tests
|
||||
|
||||
```bash
|
||||
git clone https://github.com/NousResearch/hermes-agent.git
|
||||
cd hermes-agent
|
||||
|
||||
# Create venv with Python 3.14, OUTSIDE the source tree
|
||||
uv venv ~/.hermes/venvs/hermes-dev --python 3.14
|
||||
export VIRTUAL_ENV="$HOME/.hermes/venvs/hermes-dev"
|
||||
export PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
# Install with all extras (messaging, cron, CLI menus, dev tools)
|
||||
uv pip install -e ".[all,dev]"
|
||||
|
||||
# Optional: browser tools
|
||||
npm install
|
||||
```
|
||||
|
||||
### Configure for Development
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/{cron,sessions,logs,memories,skills}
|
||||
cp cli-config.yaml.example ~/.hermes/config.yaml
|
||||
touch ~/.hermes/.env
|
||||
|
||||
# Add at minimum an LLM provider key:
|
||||
echo 'OPENROUTER_API_KEY=sk-or-v1-your-key' >> ~/.hermes/.env
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
# The standard installer already put `hermes` on PATH.
|
||||
hermes doctor
|
||||
hermes chat -q "Hello"
|
||||
```
|
||||
|
||||
If you used the manual clone fallback, run `./hermes` from the checkout or
|
||||
symlink this clone's venv explicitly:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.local/bin
|
||||
ln -sf "$(pwd)/venv/bin/hermes" ~/.local/bin/hermes
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
Use the canonical runner on every host:
|
||||
|
||||
```bash
|
||||
scripts/run_tests.sh
|
||||
scripts/run_tests.sh tests/agent/ -v
|
||||
```
|
||||
|
||||
On Windows, run the script through Bash. When no local `.venv` or `venv`
|
||||
contains pytest, the runner accepts the explicit `HERMES_PYTHON` above. It
|
||||
clears credentials, isolates `HERMES_HOME`, and runs each test file in a separate
|
||||
subprocess through `scripts/run_tests_parallel.py`. It does not use xdist.
|
||||
|
||||
Run the relevant JS workspace checks for JS changes. Native install/update
|
||||
E2E runs on disposable CI hosts, never against the developer's live app.
|
||||
See [Package management](../reference/package-management.md) for PM commands and runtime ownership.
|
||||
|
||||
## Code Style
|
||||
|
||||
- **PEP 8** with practical exceptions (no strict line length enforcement)
|
||||
@@ -147,14 +150,14 @@ scripts/run_tests.sh
|
||||
|
||||
## Cross-Platform Compatibility
|
||||
|
||||
See **[Platform Support](../getting-started/platform-support.md)**. Native Windows uses Git Bash (from [Git for Windows](https://git-scm.com/download/win)) for shell commands. A few features require POSIX kernel primitives and are gated: the dashboard's embedded PTY terminal pane (`/chat` tab) needs a POSIX PTY (Linux, macOS, or WSL2). If you're doing Windows-heavy dev, run the Windows-footgun lint (`scripts/check-windows-footguns.py`) before pushing.
|
||||
See **[Platform Support](../getting-started/platform-support.md)**. Native Windows uses Git Bash (from [Git for Windows](https://git-scm.com/download/win)) for shell commands. The dashboard uses POSIX PTYs on Unix and the `pywinpty`/ConPTY bridge on Windows. Availability depends on that host's native dependency support. If you're doing Windows-heavy dev, run the Windows-footgun lint (`scripts/check-windows-footguns.py`) before pushing.
|
||||
|
||||
When contributing code, keep these rules in mind:
|
||||
|
||||
- **Don't add unguarded `signal.SIGKILL` references.** It's not defined on Windows. Either route through `gateway.status.terminate_pid(pid, force=True)` (the centralized primitive that does `taskkill /T /F` on Windows and SIGKILL on POSIX), or fall back with `getattr(signal, "SIGKILL", signal.SIGTERM)`.
|
||||
- **Catch `OSError` alongside `ProcessLookupError` on `os.kill(pid, 0)` probes.** Windows raises `OSError` (WinError 87, "parameter is incorrect") for an already-gone PID instead of `ProcessLookupError`.
|
||||
- **Use `psutil.pid_exists()` for process liveness.** Do not use `os.kill(pid, 0)` on Windows; it is not a safe probe.
|
||||
- **Don't force the terminal to POSIX semantics.** `os.setsid`, `os.killpg`, `os.getpgid`, `os.fork` all raise on Windows — gate them with `if sys.platform != "win32":` or `if os.name != "nt":`.
|
||||
- **Open files with an explicit `encoding="utf-8"`.** The Python default on Windows is the system locale (often cp1252), which mojibakes or crashes on non-Latin text.
|
||||
- **Use explicit text encodings.** User-authored UTF-8 reads use `utf-8-sig` to accept a leading BOM. Writes use `utf-8` without adding a BOM.
|
||||
- **Use `pathlib.Path` / `os.path.join` — never manually concat with `/`.** This matters less for strings the OS gives us back and more for strings we construct to hand to subprocesses.
|
||||
|
||||
Key patterns:
|
||||
|
||||
Reference in New Issue
Block a user