Native Windows (with Git for Windows installed) can now run the Hermes CLI and gateway end-to-end without crashing. install.ps1 already existed and the Git Bash terminal backend was already wired up — this PR fills the remaining gaps discovered by auditing every Windows-unsafe primitive (`signal.SIGKILL`, `os.kill(pid, 0)` probes, bare `fcntl`/`termios` imports) and by comparing hermes against how Claude Code, OpenCode, Codex, and Cline handle native Windows. ## What changed ### UTF-8 stdio (new module) - `hermes_cli/stdio.py` — single `configure_windows_stdio()` entry point. Flips the console code page to CP_UTF8 (65001), reconfigures `sys.stdout`/`stderr`/`stdin` to UTF-8, sets `PYTHONIOENCODING` + `PYTHONUTF8` for subprocesses. No-op on non-Windows. Opt out via `HERMES_DISABLE_WINDOWS_UTF8=1`. - Called early in `cli.py::main`, `hermes_cli/main.py::main`, and `gateway/run.py::main` so Unicode banners (box-drawing, geometric symbols, non-Latin chat text) don't `UnicodeEncodeError` on cp1252 consoles. ### Crash sites fixed - `hermes_cli/main.py:7970` (hermes update → stuck gateway sweep): raw `os.kill(pid, _signal.SIGKILL)` → `gateway.status.terminate_pid(pid, force=True)` which routes through `taskkill /T /F` on Windows. - `hermes_cli/profiles.py::_stop_gateway_process`: same fix — also converted SIGTERM path to `terminate_pid()` and widened OSError catch on the intermediate `os.kill(pid, 0)` probe. - `hermes_cli/kanban_db.py:2914, 3041`: raw `signal.SIGKILL` → `getattr(signal, "SIGKILL", signal.SIGTERM)` fallback (matches the pattern already used in `gateway/status.py`). ### OSError widening on `os.kill(pid, 0)` probes Windows raises `OSError` (WinError 87) for a gone PID instead of `ProcessLookupError`. Widened the catch at: - `gateway/run.py:15101` (`--replace` wait-for-exit loop — without this, the loop busy-spins the full 10s every Windows gateway start) - `hermes_cli/gateway.py:228, 460, 940` - `hermes_cli/profiles.py:777` - `tools/process_registry.py::_is_host_pid_alive` - `tools/browser_tool.py:1170, 1206` ### Dashboard PTY graceful degradation `hermes_cli/pty_bridge.py` depends on `fcntl`/`termios`/`ptyprocess`, none of which exist on native Windows. Previously a Windows dashboard would crash on `import hermes_cli.web_server` because of a top-level import. Now: - `hermes_cli/web_server.py` wraps the pty_bridge import in `try/except ImportError` and sets `_PTY_BRIDGE_AVAILABLE=False`. - The `/api/pty` WebSocket handler returns a friendly "use WSL2 for this tab" message instead of exploding. - Every other dashboard feature (sessions, jobs, metrics, config editor) runs natively on Windows. ### Dependency - `pyproject.toml`: add `tzdata>=2023.3; sys_platform == 'win32'` so Python's `zoneinfo` works on Windows (which has no IANA tzdata shipped with the OS). Credits @sprmn24 (PR #13182). ### Docs - README.md: removed "Native Windows is not supported"; added PowerShell one-liner and Git-for-Windows prerequisite note. - `website/docs/getting-started/installation.md`: new Windows section with capability matrix (everything native except the dashboard `/chat` PTY tab, which is WSL2-only). - `website/docs/user-guide/windows-wsl-quickstart.md`: reframed as "WSL2 as an alternative to native" rather than "the only way". - `website/docs/developer-guide/contributing.md`: updated cross-platform guidance with the `signal.SIGKILL` / `OSError` rules we enforce now. - `website/docs/user-guide/features/web-dashboard.md`: acknowledged native Windows works for everything except the embedded PTY pane. ## Why this shape Pulled from a survey of how other agent codebases handle native Windows (Claude Code, OpenCode, Codex, Cline): - All four treat Git Bash as the canonical shell on Windows, same as hermes already does in `tools/environments/local.py::_find_bash()`. - None of them force `SetConsoleOutputCP` — but they don't have to, Node/Rust write UTF-16 to the Win32 console API. Python does not get that for free, so we flip CP_UTF8 via ctypes. - None of them ship PowerShell-as-primary-shell (Claude Code exposes PS as a secondary tool; scope creep for this PR). - All of them use `taskkill /T /F` for force-kill on Windows, which is exactly what `gateway.status.terminate_pid(force=True)` does. ## Non-goals (deliberate scope limits) - No PowerShell-as-a-second-shell tool — worth designing separately. - No terminal routing rewrite (#12317, #15461, #19800 cluster) — that's the hardest design call and needs a separate doc. - No wholesale `open()` → `open(..., encoding="utf-8")` sweep (Tianworld cluster) — will do as follow-up if users hit actual breakage; most modern code already specifies it. ## Validation - 28 new tests in `tests/tools/test_windows_native_support.py` — all platform-mocked, pass on Linux CI. Cover: - `configure_windows_stdio` idempotency, opt-out, env-preservation - `terminate_pid` taskkill routing, failure → OSError, FileNotFoundError fallback - `getattr(signal, "SIGKILL", …)` fallback shape - `_is_host_pid_alive` OSError widening (Windows-gone-PID behavior) - Source-level checks that all entry points call `configure_windows_stdio` - pty_bridge import-guard present in `web_server.py` - README no longer says "not supported" - 12 pre-existing tests in `tests/tools/test_windows_compat.py` still pass. - `tests/hermes_cli/` ran fully (3909 passed, 9 failures — all confirmed pre-existing on main by stash-test). - `tests/gateway/` ran fully (5021 passed, 1 pre-existing failure). - `tests/tools/test_process_registry.py` + `test_browser_*` pass. - Manual smoke: `import hermes_cli.stdio; import gateway.run; import hermes_cli.web_server` — all clean, `_PTY_BRIDGE_AVAILABLE=True` on Linux (as expected). ## Files - New: `hermes_cli/stdio.py`, `tests/tools/test_windows_native_support.py` - Modified: `cli.py`, `gateway/run.py`, `hermes_cli/main.py`, `hermes_cli/profiles.py`, `hermes_cli/gateway.py`, `hermes_cli/kanban_db.py`, `hermes_cli/pty_bridge.py`, `hermes_cli/web_server.py`, `tools/browser_tool.py`, `tools/process_registry.py`, `pyproject.toml`, `README.md`, and 4 docs pages. Credits to everyone whose prior PR work informed these fixes — see the co-author trailers. All of the PRs listed in `~/.hermes/plans/windows-support-prs.md` fixing `os.kill` / `signal.SIGKILL` / UTF-8 stdio / tzdata / README patterns found the same issues; this PR consolidates them. Co-authored-by: Philip D'Souza <9472774+PhilipAD@users.noreply.github.com> Co-authored-by: Arecanon <42595053+ArecaNon@users.noreply.github.com> Co-authored-by: XiaoXiao0221 <263113677+XiaoXiao0221@users.noreply.github.com> Co-authored-by: Lars Hagen <1360677+lars-hagen@users.noreply.github.com> Co-authored-by: Luan Dias <65574834+luandiasrj@users.noreply.github.com> Co-authored-by: Ruzzgar <ruzzgarcn@gmail.com> Co-authored-by: sprmn24 <oncuevtv@gmail.com> Co-authored-by: adybag14-cyber <252811164+adybag14-cyber@users.noreply.github.com> Co-authored-by: Prasanna28Devadiga <54196612+Prasanna28Devadiga@users.noreply.github.com>
6 KiB
| sidebar_position | title | description |
|---|---|---|
| 2 | Installation | Install Hermes Agent on Linux, macOS, WSL2, native Windows, or Android via Termux |
Installation
Get Hermes Agent up and running in under two minutes with the one-line installer.
Quick Install
Linux / macOS / WSL2
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
Windows (native, PowerShell)
Open PowerShell and run:
irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1 | iex
The native Windows installer provisions uv, Python 3.11, Node.js 22, ripgrep, and ffmpeg, clones the repo under %LOCALAPPDATA%\hermes\hermes-agent, creates a virtualenv, and adds hermes to your User PATH. Restart your terminal (or open a new PowerShell window) after the install so PATH picks up.
Prerequisite: Install Git for Windows first. Hermes uses the bundled Git Bash to execute terminal commands — the same approach Claude Code and other coding agents take on Windows. If you install Git to a non-default location, set HERMES_GIT_BASH_PATH in your environment to point at bash.exe.
If you prefer WSL2, the Linux installer above works inside it; both native and WSL installs can coexist without conflict (native data lives under %LOCALAPPDATA%\hermes, WSL data lives under ~/.hermes).
Android / Termux
Hermes now ships a Termux-aware installer path too:
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
The installer detects Termux automatically and switches to a tested Android flow:
- uses Termux
pkgfor system dependencies (git,python,nodejs,ripgrep,ffmpeg, build tools) - creates the virtualenv with
python -m venv - exports
ANDROID_API_LEVELautomatically for Android wheel builds - installs a curated
.[termux]extra withpip - skips the untested browser / WhatsApp bootstrap by default
If you want the fully explicit path, follow the dedicated Termux guide.
:::note Windows Feature Parity
Everything except the browser-based dashboard chat terminal runs natively on Windows:
- CLI (
hermes chat,hermes setup,hermes gateway, …) — native, uses your default terminal - Gateway (Telegram, Discord, Slack, …) — native, runs as a background PowerShell process
- Cron scheduler — native
- Browser tool — native (Chromium via Node.js)
- MCP servers — native (stdio and HTTP transports both supported)
- Dashboard
/chatterminal pane — WSL2 only (uses a POSIX PTY; native Windows has no equivalent). The rest of the dashboard (sessions, jobs, metrics) works natively — only the embedded PTY terminal tab is gated.
Set HERMES_DISABLE_WINDOWS_UTF8=1 in your environment if you hit an encoding-related bug and want to fall back to the legacy cp1252 stdio path (useful for bisecting).
:::
What the Installer Does
The installer handles everything automatically — all dependencies (Python, Node.js, ripgrep, ffmpeg), the repo clone, virtual environment, global hermes command setup, and LLM provider configuration. By the end, you're ready to chat.
Install Layout
Where the installer puts things depends on whether you're installing as a normal user or as root:
| Installer | Code lives at | hermes binary |
Data directory |
|---|---|---|---|
| Per-user (normal) | ~/.hermes/hermes-agent/ |
~/.local/bin/hermes (symlink) |
~/.hermes/ |
Root-mode (sudo curl … | sudo bash) |
/usr/local/lib/hermes-agent/ |
/usr/local/bin/hermes |
/root/.hermes/ (or $HERMES_HOME) |
The root-mode FHS layout (/usr/local/lib/…, /usr/local/bin/hermes) matches where other system-wide developer tools land on Linux. It's useful for shared-machine deployments where one system install should serve every user. Per-user config (auth, skills, sessions) still lives under each user's ~/.hermes/ or explicit HERMES_HOME.
After Installation
Reload your shell and start chatting:
source ~/.bashrc # or: source ~/.zshrc
hermes # Start chatting!
To reconfigure individual settings later, use the dedicated commands:
hermes model # Choose your LLM provider and model
hermes tools # Configure which tools are enabled
hermes gateway setup # Set up messaging platforms
hermes config set # Set individual config values
hermes setup # Or run the full setup wizard to configure everything at once
Prerequisites
The only prerequisite is Git. The installer automatically handles everything else:
- uv (fast Python package manager)
- Python 3.11 (via uv, no sudo needed)
- Node.js v22 (for browser automation and WhatsApp bridge)
- ripgrep (fast file search)
- ffmpeg (audio format conversion for TTS)
:::info
You do not need to install Python, Node.js, ripgrep, or ffmpeg manually. The installer detects what's missing and installs it for you. Just make sure git is available (git --version).
:::
:::tip Nix users If you use Nix (on NixOS, macOS, or Linux), there's a dedicated setup path with a Nix flake, declarative NixOS module, and optional container mode. See the Nix & NixOS Setup guide. :::
Manual / Developer Installation
If you want to clone the repo and install from source — for contributing, running from a specific branch, or having full control over the virtual environment — see the Development Setup section in the Contributing guide.
Troubleshooting
| Problem | Solution |
|---|---|
hermes: command not found |
Reload your shell (source ~/.bashrc) or check PATH |
API key not set |
Run hermes model to configure your provider, or hermes config set OPENROUTER_API_KEY your_key |
| Missing config after update | Run hermes config check then hermes config migrate |
For more diagnostics, run hermes doctor — it will tell you exactly what's missing and how to fix it.