Hermes Agent Agent Rules

A personal AI agent with terminal tools, messaging integrations, memory, and reusable skills.

Overview

Hermes Agent limits core growth with a six-step capability ladder and rules that protect prompt caching. The root guide covers the CLI, gateway, desktop app, and plugins, while local AGENTS.md files handle area-specific work.

Documents

├── 
├── agent
│   └── 
├── apps
│   └── desktop
│       ├── src
│       │   └── 
│       └── 
├── cron
│   └── 
├── gateway
│   ├── platforms
│   │   └── 
│   └── 
├── hermes_cli
│   └── 
├── plugins
│   └── 
├── skills
│   └── 
├── tools
│   └── 
├── tui_gateway
│   └── 
├── web
│   └── 
├── website
│   └── docs
│       └── developer-guide
│           └── Long-form subsystem documentation
└── hermes-agent-devDevelopment workflow skill named by the guide

Techniques in this file

01 / Context budgeting

Treat prompt stability as a conversation invariant

The guide explains that changing past context or toolsets invalidates the cached prefix and increases cost. Commands that change prompt state default to the next session, with an explicit immediate option.

Source excerpt starting at line 20.
- **Per-conversation prompt caching is sacred.** A long-lived conversation reuses a cached  prefix every turn. Anything that mutates past context, swaps toolsets, reloads memories, or  rebuilds the system prompt mid-conversation invalidates that cache and multiplies the user's  cost. We do not do it; the ONE exception is context compression. Slash commands that mutate  system-prompt state (skills, tools, memory) must be **cache-aware**: default to deferred  invalidation (takes effect next session) with an opt-in `--now` flag (`/skills install --now`  is the canonical pattern).

02 / Behavioral framing

Provide an ordered decision process for new capability

The footprint ladder starts with extending existing code and works through commands, gated tools, plugins, and MCP before adding a core tool. The ordering connects architecture decisions to per-call schema cost.

Source excerpt starting at line 134.
Choose the highest (least-footprint) rung that correctly solves the problem: 1. **Extend existing code** — a variation of something that exists. Zero new surface.2. **CLI command + skill** — config/state/infra expressible as shell commands; the agent runs   `hermes <subcommand>` guided by a skill. Default for subscriptions, scheduled tasks,   service setup (`hermes webhook`, `hermes cron`, `hermes tools`).3. **Service-gated tool (`check_fn`)** — needs structured params/returns AND only appears when   a prerequisite is configured (Home Assistant tools, memory-provider tools). This rung gates   reachability/opt-in process-wide; a capability that varies per SESSION (who is watching) is   a named toolset folded in by the toolset resolver, not a `check_fn` — see "Surface capability   is a property of the SESSION" below.4. **Plugin** — third-party/niche/user-specific; lives in `~/.hermes/plugins/` or a pip   package, discovered at runtime.5. **MCP server (in the catalog)** — genuinely a tool but not core-fundamental. Zero permanent   core-schema footprint, reusable by any MCP host, reached via the built-in MCP client.6. **New core tool** — only when fundamental, broadly useful to nearly every user, and   unreachable via terminal + file or an MCP server (terminal, read_file, web_search,   browser_navigate).

03 / Pointing at the source of truth

Make the test wrapper enforce isolation

The required runner clears credential variables, fixes locale and timezone, uses a temporary home, and isolates test files in subprocesses. The guide connects those choices to local-versus-CI incidents.

Source excerpt starting at line 316.
**ALWAYS use `scripts/run_tests.sh`**, never bare `pytest`. It enforces CI parity: credentialvars unset, `TZ=UTC`, `LANG=C.UTF-8`, `HERMES_HOME` → temp dir, and per-file subprocessisolation via `scripts/run_tests_parallel.py` (no xdist; workers scale with CPU count) somodule-level dicts/ContextVars cannot leak between files. Direct `pytest` on a big machinewith API keys set has caused repeated "works locally, fails in CI" incidents (and the reverse).

04 / Verification by change type

Test on the host that owns the behavior

Platform-specific behavior must run on that operating system with the project’s named markers. The guide explains that alternate skip mechanisms can produce tests that run on no CI platform.

Source excerpt starting at line 368.
**Use the marker, never a bare `skipif`.** `scripts/ci/list_os_marked_tests.py` finds files forthe macOS/Windows lanes by grepping the marker *name*, then filters with `-m <marker>`. A`skipif(sys.platform != "win32")` test skips on Linux AND is never imported on Windows — it runsnowhere, silently. A file-local alias (`windows_only = pytest.mark.skipif(...)`) is listed but`-m windows_only` deselects everything: green over zero coverage. Don't `pytest.skip()` non-hostrows of a platform `@parametrize` — split into one marked test per OS.

05 / Good and bad pairs

Prefer behavioral relationships over changing catalog values

The test guidance contrasts assertions about fixed model names and counts with contracts between data structures. It explains how snapshot-like checks can turn expected catalog updates into maintenance failures.

Source excerpt starting at line 385.
A change-detector fails whenever data *expected to change* is updated — model catalogs,`_config_version`, enumeration counts, hardcoded model lists. It adds no coverage and taxesevery routine update. Don't: `assert "gemini-2.5-pro" in _PROVIDER_MODELS["gemini"]`,`assert DEFAULT_CONFIG["_config_version"] == 21`, `assert len(models) == 8`. Do: `assert"gemini" in _PROVIDER_MODELS and len(_PROVIDER_MODELS["gemini"]) >= 1` (plumbing works);`assert raw["_config_version"] == DEFAULT_CONFIG["_config_version"]` (migration reacheslatest); `assert not (set(moonshot_models) & coding_plan_only_models)` (no leak); everycatalog model has a context-length entry (relationship). If it reads like a snapshot, deleteit; if it reads like a contract between two pieces of data, keep it. Reviewers reject newchange-detectors; authors convert them before re-review.

06 / Router files

Map each work area to its instruction file

The routing table connects source areas to nested AGENTS.md files and describes what each covers. The root file also documents size targets and truncation behavior for injected area guidance.

Source excerpt starting at line 4.
This root file holds only what applies everywhere. Each area has its own `AGENTS.md` (aim for~8k chars; `agent/subdirectory_hints.py` delivers up to 32k and truncates head/tail with a warningpast that); see the **routing table** at the end and read the area file before editing in that area.

Ideas for your repo

  1. Explain the recurring cost that architectural constraints protect.
  2. Give new capabilities an ordered set of integration options.
  3. Centralize test isolation in a required runner.
  4. Test platform behavior on the platform that owns it.
  5. Assert contracts between data rather than values expected to change.
Sponsored byModem

Give your agents the whole story.

These instructions explain how to work in Hermes Agent. Modem shows your agents what customers said, who is affected, and what changed.

See how Modem works