Contributing
المساهمة في المشروع
What this page is, and what it holds.
This page covers Contributing. You will use hermes update and hermes version here; about 9 minutes to read. Do not disable approvals to save time. Start read-only and widen once you trust the results.
How to contribute to Hermes Agent — dev setup, code style, PR process
Outcomes taken from this page, not a template.
- Understand what الأمان والموافقات is and when you need it.
- Run
hermes updateandhermes versionand understand what happens next. - Read the table and take only the row that applies to you.
- Set
HERMES_HOMEin the right place.
Exactly as they appear in Hermes.
hermes updatehermes versionhermes doctor hermes chat
HERMES_HOMEVIRTUAL_ENVOPENROUTER_API_KEY
Jump to the part you need.
Nothing summarised away.
The documentation body below is reproduced from the official source so commands and identifiers stay exact. Each section carries a short note describing what it contains.
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
Explains the idea itself. Read it slowly; the later sections build on it.
We value contributions in this order:
- Bug fixes — crashes, incorrect behavior, data loss
- Cross-platform compatibility — macOS, different Linux distros, WSL2
- Security hardening — shell injection, prompt injection, path traversal
- Performance and robustness — retry logic, error handling, graceful degradation
- New skills — broadly useful ones (see Creating Skills)
- New tools — rarely needed; most capabilities should be skills
- Documentation — fixes, clarifications, new examples
Common contribution paths
Explains the idea itself. Read it slowly; the later sections build on it.
- Building a custom/local tool without modifying Hermes core? Start with Build a Hermes Plugin
- Building a new built-in core tool for Hermes itself? Start with Adding Tools
- Building a new skill? Start with Creating Skills
- Building a new inference provider? Start with Adding Providers
Development Setup
Ordered, practical steps. Run one and confirm it worked before moving on. Commands here: hermes update, hermes doctor hermes chat.
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 ↗) |
| 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.
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 installAfter that, create branches and run tests from that checkout:
git checkout -b fix/description
scripts/run_tests.shYou can also run a fully isolated Hermes instance (throwaway HERMES_HOME, separate Electron userData, distinct Electron app name to avoid the single-instance lock):
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.
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 installConfigure for Development
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/.envRun
# 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:
mkdir -p ~/.local/bin
ln -sf "$(pwd)/venv/bin/hermes" ~/.local/bin/hermesRun Tests
scripts/run_tests.shCode Style
Explains the idea itself. Read it slowly; the later sections build on it.
- 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()withexc_info=Truefor unexpected errors - Cross-platform: Never assume Unix (see below)
- Profile-safe paths: Never hardcode
~/.hermes— useget_hermes_home()fromhermes_constantsfor code paths anddisplay_hermes_home()for user-facing messages. See AGENTS.md ↗ for full rules.
Cross-Platform Compatibility
Explains the idea itself. Read it slowly; the later sections build on it.
See Platform Support. Native Windows uses Git Bash (from Git for Windows ↗) 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.SIGKILLreferences. It's not defined on Windows. Either route throughgateway.status.terminate_pid(pid, force=True)(the centralized primitive that doestaskkill /T /Fon Windows and SIGKILL on POSIX), or fall back withgetattr(signal, "SIGKILL", signal.SIGTERM). - Catch
OSErroralongsideProcessLookupErroronos.kill(pid, 0)probes. Windows raisesOSError(WinError 87, "parameter is incorrect") for an already-gone PID instead ofProcessLookupError. - Don't force the terminal to POSIX semantics.
os.setsid,os.killpg,os.getpgid,os.forkall raise on Windows — gate them withif sys.platform != "win32":orif 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:
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:
if platform.system() != "Windows":
kwargs["preexec_fn"] = os.setsid3. Path separators
Use pathlib.Path instead of string concatenation with /.
Security Considerations
A lookup table. Do not read it all; find the row that applies to you.
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
A lookup table. Do not read it all; find the row that applies to you.
Branch Naming
fix/description # Bug fixes
feat/description # New features
docs/description # Documentation
test/description # Tests
refactor/description # Code restructuringBefore Submitting
- Run tests:
scripts/run_tests.shfor CI-parity. Use directpython -m pytest ...only when the wrapper is unavailable or you are intentionally debugging outside the wrapper. - Test manually: Run
hermesand exercise the code path you changed - 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. - 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 ↗:
<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 pipingRepo-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.mdgets 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
Commands you type in a terminal. Understand what one does before copying it. Commands here: hermes version.
- Use GitHub 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
Explains the idea itself. Read it slowly; the later sections build on it.
- Discord: discord.gg/NousResearch ↗
- GitHub Discussions: For design proposals and architecture discussions
- Skills Hub: Upload specialized skills and share with the community
License
Explains the idea itself. Read it slowly; the later sections build on it.
By contributing, you agree that your contributions will be licensed under the MIT License ↗.
3 questions answered by this page alone.
Every option is a real identifier from the Hermes documentation. The wrong ones are real too, just from other pages.