|
Download website/docs/developer-guide/contributing.md from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 13.3 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/website/docs/developer-guide/contributing.md
- Command line
-
hf download hf://SaylorTwift/hermes-agent/website/docs/developer-guide/contributing.md
-
curl -L -o contributing.md https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/website/docs/developer-guide/contributing.md
13.3 kB
| sidebar_position: 4 | |
| title: "Contributing" | |
| description: "How to contribute to Hermes Agent — dev setup, code style, PR process" | |
| # Contributing | |
| Thank you for contributing to Hermes Agent! This guide covers setting up your dev environment, understanding the codebase, and getting your PR merged. | |
| ## Contribution Priorities | |
| We value contributions in this order: | |
| 1. **Bug fixes** — crashes, incorrect behavior, data loss | |
| 2. **Cross-platform compatibility** — macOS, different Linux distros, WSL2 | |
| 3. **Security hardening** — shell injection, prompt injection, path traversal | |
| 4. **Performance and robustness** — retry logic, error handling, graceful degradation | |
| 5. **New skills** — broadly useful ones (see [Creating Skills](creating-skills.md)) | |
| 6. **New tools** — rarely needed; most capabilities should be skills | |
| 7. **Documentation** — fixes, clarifications, new examples | |
| ## Common contribution paths | |
| - Building a custom/local tool without modifying Hermes core? Start with [Build a Hermes Plugin](../developer-guide/plugins/index.md) | |
| - Building a new built-in core tool for Hermes itself? Start with [Adding Tools](./adding-tools.md) | |
| - Building a new skill? Start with [Creating Skills](./creating-skills.md) | |
| - Building a new inference provider? Start with [Adding Providers](./adding-providers.md) | |
| ## Development Setup | |
| ### Prerequisites | |
| | Requirement | Notes | | |
| | -------------------- | --------------------------------------------------------------------------------------------- | | |
| | **Git** | With the `git-lfs` extension installed | | |
| | **Python 3.11–3.13** | uv will install it if missing | | |
| | **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) | | |
| ### Install with the standard installer | |
| 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. | |
| ```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 | |
| ``` | |
| After that, create branches and run tests from that checkout: | |
| ```bash | |
| git checkout -b fix/description | |
| scripts/run_tests.sh | |
| ``` | |
| 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): | |
| ```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 :) | |
| ``` | |
| ### Manual clone fallback | |
| 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. | |
| 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. | |
| ```bash | |
| git clone https://github.com/NousResearch/hermes-agent.git | |
| cd hermes-agent | |
| # Create venv with Python 3.11, OUTSIDE the source tree | |
| uv venv ~/.hermes/venvs/hermes-dev --python 3.11 | |
| 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 | |
| ```bash | |
| scripts/run_tests.sh | |
| ``` | |
| ## Code Style | |
| - **PEP 8** with practical exceptions (no strict line length enforcement) | |
| - **Comments**: Only when explaining non-obvious intent, trade-offs, or API quirks | |
| - **Error handling**: Catch specific exceptions. Use `logger.warning()`/`logger.error()` with `exc_info=True` for unexpected errors | |
| - **Cross-platform**: Never assume Unix (see below) | |
| - **Profile-safe paths**: Never hardcode `~/.hermes` — use `get_hermes_home()` from `hermes_constants` for code paths and `display_hermes_home()` for user-facing messages. See [AGENTS.md](https://github.com/NousResearch/hermes-agent/blob/main/AGENTS.md#profiles-multi-instance-support) for full rules. | |
| ## 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. | |
| 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`. | |
| - **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 `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: | |
| ### 1. File encoding | |
| Some environments may save `.env` files in non-UTF-8 encodings: | |
| ```python | |
| try: | |
| load_dotenv(env_path) | |
| except UnicodeDecodeError: | |
| load_dotenv(env_path, encoding="latin-1") | |
| ``` | |
| ### 2. Process management | |
| `os.setsid()`, `os.killpg()`, and signal handling differ across platforms: | |
| ```python | |
| import platform | |
| if platform.system() != "Windows": | |
| kwargs["preexec_fn"] = os.setsid | |
| ``` | |
| ### 3. Path separators | |
| Use `pathlib.Path` instead of string concatenation with `/`. | |
| ## Security Considerations | |
| Hermes has terminal access. Security matters. | |
| ### Existing Protections | |
| | Layer | Implementation | | |
| | ------------------------------- | --------------------------------------------------------------------------- | | |
| | **Sudo password piping** | Uses `shlex.quote()` to prevent shell injection | | |
| | **Dangerous command detection** | Regex patterns in `tools/approval.py` with user approval flow | | |
| | **Cron prompt injection** | Scanner blocks instruction-override patterns | | |
| | **Write deny list** | Protected paths resolved via `os.path.realpath()` to prevent symlink bypass | | |
| | **Skills guard** | Security scanner for hub-installed skills | | |
| | **Code execution sandbox** | Child process runs with API keys stripped | | |
| | **Container hardening** | Docker: all capabilities dropped, no privilege escalation, PID limits | | |
| ### Contributing Security-Sensitive Code | |
| - Always use `shlex.quote()` when interpolating user input into shell commands | |
| - Resolve symlinks with `os.path.realpath()` before access control checks | |
| - Don't log secrets | |
| - Catch broad exceptions around tool execution | |
| - Test on all platforms if your change touches file paths or processes | |
| ## Pull Request Process | |
| ### Branch Naming | |
| ``` | |
| fix/description # Bug fixes | |
| feat/description # New features | |
| docs/description # Documentation | |
| test/description # Tests | |
| refactor/description # Code restructuring | |
| ``` | |
| ### Before Submitting | |
| 1. **Run tests**: `scripts/run_tests.sh` for CI-parity. Use direct `python -m pytest ...` only when the wrapper is unavailable or you are intentionally debugging outside the wrapper. | |
| 2. **Test manually**: Run `hermes` and exercise the code path you changed | |
| 3. **Check cross-platform impact**: Consider macOS, Linux, WSL2, and native Windows. If you touch file I/O, process management, terminal handling, subprocesses, or signals, run `scripts/check-windows-footguns.py`. | |
| 4. **Keep PRs focused**: One logical change per PR | |
| ### PR Description | |
| Include: | |
| - **What** changed and **why** | |
| - **How to test** it | |
| - **What platforms** you tested on | |
| - Reference any related issues | |
| ### Commit Messages | |
| We use [Conventional Commits](https://www.conventionalcommits.org/): | |
| ``` | |
| <type>(<scope>): <description> | |
| ``` | |
| | Type | Use for | | |
| | ---------- | ----------------------------- | | |
| | `fix` | Bug fixes | | |
| | `feat` | New features | | |
| | `docs` | Documentation | | |
| | `test` | Tests | | |
| | `refactor` | Code restructuring | | |
| | `chore` | Build, CI, dependency updates | | |
| Scopes: `cli`, `gateway`, `tools`, `skills`, `agent`, `install`, `whatsapp`, `security` | |
| Examples: | |
| ``` | |
| fix(cli): prevent crash in save_config_value when model is a string | |
| feat(gateway): add WhatsApp multi-user session isolation | |
| fix(security): prevent shell injection in sudo password piping | |
| ``` | |
| ### Repo-local review checklists: `.agents/checks/*.md` | |
| Projects built on (or reviewed by) Hermes can keep reviewer checklists inside the repository under `.agents/checks/`. Each file is a focused, plain-markdown checklist that an agent loads before reviewing a change touching the matching area: | |
| ``` | |
| .agents/ | |
| checks/ | |
| security.md # e.g. "grep the diff for shell interpolation; check subprocess calls quote args" | |
| migrations.md # e.g. "every schema change ships a backfill and a rollback note" | |
| public-api.md # e.g. "exported signatures changed? flag for semver review" | |
| ``` | |
| Conventions that make these work well: | |
| - **One concern per file**, named after the concern. Small files get read in full; a monolithic `checklist.md` gets skimmed. | |
| - **Write checks as verifiable actions** ("run X and confirm Y"), not aspirations ("code should be secure"). | |
| - **State the trigger at the top** — which paths or change types the checklist applies to — so an agent (or human) can skip irrelevant ones cheaply. | |
| - Keep them in version control next to the code they guard: they evolve with the codebase, and a PR that changes the rules changes the checklist in the same diff. | |
| When you ask Hermes to review a PR in a repository that has `.agents/checks/`, tell it (or teach it via a skill) to read the relevant checklists first and report against them. This gives review agents the project-specific bar that generic review prompts miss. | |
| ## Reporting Issues | |
| - Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues) | |
| - Include: OS, Python version, Hermes version (`hermes --version`), full error traceback | |
| - Include steps to reproduce | |
| - Check existing issues before creating duplicates | |
| - For security vulnerabilities, please report privately | |
| ## Community | |
| - **Discord**: [discord.gg/NousResearch](https://discord.gg/NousResearch) | |
| - **GitHub Discussions**: For design proposals and architecture discussions | |
| - **Skills Hub**: Upload specialized skills and share with the community | |
| ## License | |
| By contributing, you agree that your contributions will be licensed under the [MIT License](https://github.com/NousResearch/hermes-agent/blob/main/LICENSE). | |