Life = Content

Writing

The Hermes Agent Field Guide: Setup, Configuration, and Getting the Most Out of Your AI Operating System

updated 2026-08-21

I use Hermes Agent daily alongside Claude Code, Codex, and Cursor. I call on Hermes from Telegram. That is the thing that changed my daily life. The same agent that writes code in my terminal answers me on my phone, runs scheduled jobs while I sleep, and manages my second brain.

This page distills what I have learned from the people below into a single reference. I am standing on their shoulders. If you want the full context, go watch them — they explain it better than I do.

Where I learned this

  • Startup Ideas podcast (Greg Isenberg) — excellent skillsmaxxing episode: https://www.youtube.com/watch?v=xHsftiyT9pQ
  • Sharbel A. — "Learn 95% of Hermes Agent in 31 Minutes": https://www.youtube.com/watch?v=Ta2wg6xPaY4
  • Wanderloots (Calum) — "Improved AI Memory — Full Hermes Tutorial": https://www.youtube.com/watch?v=R1TNGOZAOZs
  • Tonbi Studio — Hermes Desktop plugins deep-dive: https://x.com/tonbistudio/status/2087215004600913988
  • Jack Roberts — Claude Design + gauntlet loop: https://www.youtube.com/watch?v=jq9LRwE0-GQ + his Skool communities: https://www.skool.com/ai-automation-vault and https://www.skool.com/aiautomationsbyjack/
  • Official Hermes docs — https://hermes-agent.nousresearch.com/docs/

This is not a features page. The official docs are the source of truth for API references. This is a field guide — the stuff the docs do not put together for you, the pitfalls that cost hours, and the configurations that actually matter.

Quick start for non-technical users

You do not need a terminal. You do not need to know what a shell is.

  1. Download the Hermes Desktop app from https://hermes-agent.nousresearch.com/
  2. Open it. Click Setup. Pick a model. The Nous Portal subscription is the easiest — it just works.
  3. Connect Telegram. This is the part that changes your life. You can text your AI agent from your phone. It reads files, runs commands, and does work while you are on the couch.
  4. Watch these videos to get oriented: - Sharbel A.'s 31-minute overview: https://www.youtube.com/watch?v=Ta2wg6xPaY4 - Jack Roberts' tutorials on Skool: https://www.skool.com/ai-automation-vault - Wanderloots' memory tutorial: https://www.youtube.com/watch?v=R1TNGOZAOZs

That is it. You are running an AI agent from your phone. Everything else in this guide is optional.

What Is Hermes Agent?

Hermes Agent is an open-source AI agent framework by Nous Research. It runs in your terminal, a native desktop app, messaging platforms (Telegram, Discord, Slack, WhatsApp, iMessage, Signal, Matrix, Teams, Email), and IDEs. It works with any LLM provider — OpenRouter, Anthropic, OpenAI, Google, DeepSeek, xAI, local models, and 20+ others.

What Hermes shares with other tools

Skills, memory, and hooks exist across the AI coding tool ecosystem. The Agent Skills spec is an open standard — Claude Code, Codex CLI, Cursor, Gemini CLI, and GitHub Copilot all support it. Memory systems exist in all major tools. Hooks exist in Claude Code and Cursor.

Hermes supports the same Agent Skills spec. It has memory. It has hooks. These are table stakes, not differentiators. (Thanks to Firecrawl's breakdown of the Agent Skills standard: https://www.firecrawl.dev/blog/best-claude-code-skills)

What is actually different about Hermes

  • Multi-platform gateway — the same agent runs on Telegram, Discord, Slack, WhatsApp, iMessage, Signal, Matrix, Teams, and Email with full tool access. Not just chat — the agent reads files, runs commands, and does work from your phone. This is the killer feature. I call on Hermes from Telegram. That changed my daily life.
  • Desktop app with plugin SDK — 25 placement positions across the UI, four form factors (compact, anchored, expansive, declarative), backend connectivity, and Python integration. (Thanks to Tonbi Studio for the deep-dive: https://x.com/tonbistudio/status/2087215004600913988)
  • Provider-agnostic with credential pools — swap models and providers mid-workflow. Credential pools rotate across multiple API keys automatically. One account fails, the next takes over.
  • Profiles — run multiple independent Hermes instances with isolated configs, sessions, skills, and memory. Work, personal, client — each with its own brain.
  • Cron scheduling with delivery routing — runs the agent itself on a schedule, not just a script. Delivers results to Telegram, Discord, or any connected platform.
  • The proxy — an OpenAI-compatible local proxy backed by your OAuth. Point Codex CLI, Aider, or any script at it. No API key needed.
  • Subagent delegation — spawn parallel isolated contexts for subtasks. Each gets its own conversation and toolset.

Installation

For non-technical users

Download the Hermes Desktop app from hermes-agent.nousresearch.com. Open it. Click Setup. Pick a model. Done.

The desktop app is a native Electron app for macOS, Linux, and Windows. It has streaming chat, session history, drag-and-drop file upload, and native notifications. No terminal needed.

If you have a Nous Portal subscription, the setup wizard handles everything. One click.

For developers

# Shell installer — sets up uv, Python, the venv, and the launcher
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

After installation:

hermes setup          # Interactive setup wizard — pick model + provider
hermes model           # Change model/provider later
hermes doctor          # Health check
hermes status          # Current configuration status

Key Paths

~/.hermes/config.yaml       Main configuration (settings — never secrets)
~/.hermes/.env              API keys and secrets ONLY
~/.hermes/skills/           Installed skills
~/.hermes/soul.md           Agent personality file
~/.hermes/agent-hooks/      Shell hook scripts
~/.hermes/hooks/            Gateway event hooks (Python)
~/.hermes/plugins/          Desktop app plugins
~/.hermes/state.db          Session store (SQLite + FTS5)
~/.hermes/sessions/         Session transcripts
~/.hermes/logs/             Gateway and error logs
~/.hermes/auth.json         OAuth tokens and credential pools

If you use profiles (recommended for separating work/personal/client instances), each profile lives under ~/.hermes/profiles/<name>/ with the same layout.

Provider Setup: The Subscription vs. API Trap

This is the #1 gotcha. Most people assume their $20/mo ChatGPT subscription means they can connect ChatGPT to Hermes. It doesn't.

For non-technical users

If you have a Nous Portal subscription, skip this section. The subscription includes API access. Hermes works out of the box.

If you do not have a Nous subscription, the easiest path is OpenRouter. Go to openrouter.ai, create an account, deposit $10-20, and generate an API key. One account gives you access to GPT, Claude, Grok, Llama, and 200+ other models.

For developers

ProviderSubscription (web/app)API Access (Hermes needs this)Same Billing?
Nous PortalSubscription includes APISame — it IS an API endpoint✅ Yes
OpenAI / ChatGPTChatGPT Plus/Proplatform.openai.com (separate)❌ No
xAI / GrokSuperGrokconsole.x.ai (separate)❌ No
Anthropic / ClaudeClaude Proconsole.anthropic.com (separate)❌ No
Google / GeminiGemini AdvancedAI Studio / Vertex (separate)❌ No
OllamaN/A (local, free)N/A (local, free)✅ N/A

The Nous Portal exception: If you have a Nous subscription, Hermes works out of the box — the subscription IS the API access. This is the easiest onboarding path.

For everything else: You need a separate API account with per-token credits. The subscription gives you web/app access; the API is a different billing relationship.

Think of it like: the subscription is the restaurant, the API is the grocery store. You can eat at the restaurant anytime, but if you want to cook at home (with Hermes), you need to buy groceries separately.

OpenRouter: The Path of Least Resistance

If you want access to multiple models (GPT, Claude, Grok, Llama, etc.) without creating separate API accounts at each provider, use OpenRouter. One account, one API key, pay-per-token, ~$10-20 deposit.

# 1. Go to openrouter.ai, create an account, deposit $10-20
# 2. Generate an API key (starts with sk-or-v1-...)
# 3. Add to Hermes
hermes auth add openrouter --type api-key --api-key <key> --label "OpenRouter"
# 4. Models are accessed as openrouter/<provider>/<model>
#    e.g. openrouter/anthropic/claude-sonnet-4.6
#    e.g. openrouter/openai/gpt-4o
#    e.g. openrouter/xai/grok-3
# 5. Or use the interactive picker
hermes model

OpenRouter also has free-tier models (marked with :free in the model ID) — great for experimentation:

  • meta-llama/llama-3.3-70b-instruct:free
  • google/gemini-2.0-flash-exp:free
  • deepseek/deepseek-r1:free
  • qwen/qwen-2.5-72b-instruct:free

Test from the CLI:

hermes chat -q "What is 2+2?" --provider openrouter -m "meta-llama/llama-3.3-70b-instruct:free"

Local Models (Ollama)

For non-technical users

Local models are optional. If you want privacy and zero ongoing cost, you can run models on your own machine. But this requires a capable computer — at least 16GB RAM, ideally 32GB.

If that sounds like too much work, skip it. The cloud providers above work fine. Come back to this when you want to experiment.

For developers

If you have a capable machine, run models locally for zero ongoing cost:

# Install Ollama
curl -fsSL https://ollama.com/install.sh | bash

# Pull a model
ollama pull qwen3:14b

# Create a dedicated Hermes profile for local use
hermes profile create local-scribe

# Configure the profile to use Ollama (set provider=custom, base_url=http://localhost:11434/v1)
hermes --profile local-scribe config set model.provider custom
hermes --profile local-scribe config set model.base_url http://localhost:11434/v1
hermes --profile local-scribe config set model.default qwen3:14b

On an M4 iMac with 32GB RAM, Qwen 3 14B takes ~5 minutes per query with Hermes's full system prompt. Slow for interactive use, but functional — and zero tokens, zero cost.

Configuration

For non-technical users

Most configuration happens through the desktop app or the setup wizard. You rarely need to touch config files. If something is not working, run hermes doctor — it tells you what is wrong.

For developers

Edit with hermes config set section.key value or hermes config edit for the full file. Never hand-edit config.yaml — a stray indent can corrupt the file and break the live gateway.

Most-Used Config Sections

SectionKey Options
modeldefault, provider, base_url, api_key, context_length, aliases
agentmax_turns (90), tool_use_enforcement, verify_on_stop
terminalbackend (local/docker/ssh/modal), cwd, timeout (180)
compressionenabled, threshold (0.50), target_ratio (0.20)
displayskin, interface (cli/tui), language, show_reasoning, show_cost
approvalsmode (smart/manual/off), timeout, cron_mode
sttenabled, provider (local/groq/openai/mistral)
ttsprovider (edge/elevenlabs/openai/minimax/mistral/gemini)
memorymemory_enabled, user_profile_enabled, provider, write_approval
securityredact_secrets, tirith_enabled, website_blocklist
delegationmodel, provider, max_concurrent_children, max_spawn_depth
hooksShell hook configuration (see below)

Memory: Persistent Context Across Sessions

Memory is injected into every turn — no re-reading a 200-line config file to recover context. Split into two targets:

  • user: Who you are, role, technical level, communication style, hard rules, recurring preferences.
  • memory: Environment facts, tool quirks, conventions, lessons.

Keep memory compact and high-signal. It's injected every turn, so bloated memory costs tokens every single message. Don't store task progress or completed-work logs — use session_search for those.

Soul.md: Agent Personality

Write a soul.md file at ~/.hermes/soul.md that defines your agent's personality. Mine is direct, adversarial, minimalist, and explicitly told to push back on bad ideas. This is your agent's character — make it match how you actually want to work.

Skills: Procedural Memory

For non-technical users

Skills are reusable instructions. You write one, and the agent follows it every time. If you find yourself explaining the same thing over and over, turn it into a skill. The desktop app can help you create them.

For developers

Skills are reusable procedures that load when relevant. They are Hermes's implementation of the Agent Skills open standard — the same spec used by Claude Code, Codex CLI, Cursor, Gemini CLI, and GitHub Copilot. Hermes skills have categories, trigger descriptions, and can carry bundled scripts and reference files that load on demand. references/, templates/, and scripts/ directories.

Skills live in ~/.hermes/skills/<category>/<name>/SKILL.md.

---
name: my-skill
description: "Use when <trigger>. <one-line behavior>."
version: 1.0.0
author: Your Name
license: MIT
platforms: [linux, macos, windows]
---

# My Skill

## When to Use
...

## Steps
1. ...

## Pitfalls
1. ...

The first 57 characters of the description appear in the system prompt as a trigger hint. Keep the trigger self-contained in that window.

The Skills Hub

Hermes ships with 906 skills — 71 built-in, 112 optional, and hundreds more from community sources (Claw Hub, LobeHub, gstack, Anthropic, OpenAI, HuggingFace, NVIDIA). The full catalog lives at the Skills Hub, browsable by category (Apple, AI Agents, Creative, GitHub, Media, MLOps, Productivity, Research, Smart Home, Social Media, Software Dev) and platform.

The built-in skills are the ones worth studying first — they're maintained by the Hermes team and demonstrate the SKILL.md format at its best. The optional skills (112 at last count) cover everything from Notion API integration to Manim video generation. You install them the same way: drop the directory in ~/.hermes/skills/<category>/<name>/.

For agents and power users, the key insight is that skills are procedural memory that compounds. Every time you solve a hard problem or discover a non-obvious workflow, saving it as a skill means the next session starts with that knowledge loaded. This is the core thesis behind using Hermes as a second-brain operating system — your procedures become reusable artifacts, not session-specific ephemera.

The Surfaces

Hermes runs in multiple places at once. The same agent, the same brain, different surfaces.

For non-technical users

The desktop app is the control room. Telegram is the daily assistant on your phone. That is all most people need. The terminal and web dashboard are for power users.

For developers

Hermes runs on multiple surfaces — the same agent core drives all of them:

  • Desktop app (hermes desktop) — native Electron app, streaming chat, session list, Cmd+K palette, drag-and-drop files.
  • CLI (hermes) — interactive terminal chat.
  • Ink TUI (hermes --tui) — terminal UI with docked widgets.
  • Web dashboard (hermes dashboard) — admin panel with messaging channels, MCP catalog, webhooks, memory, profile builder.
  • Gateway — connects to Telegram, Discord, Slack, WhatsApp, iMessage, Signal, Matrix, Teams, Email, and more.
  • OpenAI-compatible proxy (hermes proxy) — point Codex CLI, Aider, or any script at it. No API key needed.

Telegram Gateway Setup

This is the feature that changed my daily life. I call on Hermes from Telegram. My agent answers me on my phone, reads files, runs commands, and does work while I am on the couch.

The gateway lets Hermes run on messaging platforms with full tool access. Telegram is the most common setup. (Sharbel A. covers the full surface list in his overview: https://www.youtube.com/watch?v=Ta2wg6xPaY4)

# Interactive (if you have a terminal):
hermes gateway setup

# Non-interactive (from an agent context, no TTY):
# 1. Add bot token to .env
sed -i.bak 's/^# TELEGRAM_BOT_TOKEN=.*/TELEGRAM_BOT_TOKEN=<token>/' ~/.hermes/.env
# 2. Install as persistent service
hermes gateway install
# 3. Verify
hermes gateway status

Once the gateway is running, Hermes responds to your Telegram messages with full tool access — terminal, file operations, web search, code execution, everything. It's not a chatbot; it's your agent living on your phone.

Hooks: Nudge vs. Enforcement

This is the feature that changed everything for me — but not the way I first thought. The distinction between nudging the model and enforcing behavior is the most important lesson in this entire guide, and it cost me four failed sessions to learn it.

I had a standing protocol (filler-word tracking) that my agent kept skipping under the pressure of real work. Three sessions in a row. Memory didn't fix it. Skills didn't fix it. Both rely on the model choosing to comply.

So I built a pre_llm_call shell hook — a reminder injected into every user message before the model sees it. The hook fired correctly. The model received the reminder. **And then ignored it again.** Four sessions of failure. The hook was working; the model just chose not to comply. A pre_llm_call hook is a Post-it note on the model's monitor — it can still walk past it.

The fix was transform_llm_output — a hook that fires after the model produces its final response but before the user sees it. It can append to or replace the response using classical code. The model literally cannot skip it because it runs after the model is done. No model cooperation required.

The Nudge-vs-Enforcement Distinction

ApproachLayerHow It WorksCan Model Skip It?
MemorySystem promptText injected every turn✅ Yes — background noise
SkillsConditional loadModel loads when relevant✅ Yes — model decides not to load
System prompt rulesStatic instructionsModel reads at start✅ Yes — prioritizes immediate task
pre_llm_call hookPre-responseInjects context before model responds✅ Yes — it's a nudge, not a gate
transform_llm_outputPost-responseModifies response after model finishes❌ No — runs after the model is done

The rule: if you need the model to remember to do something every response, pre_llm_call will help but won't guarantee it. If you need a specific output format or footer to appear every response regardless of model behavior, use transform_llm_output — either as a Python plugin hook or (once issue #67890 is fixed) a shell hook.

The architecture that works: nudge with pre_llm_call (cheap, no tokens), enforce with transform_llm_output (deterministic, code not model). Together they're reliable. This is a sellable pattern for client deployments — "the agent's behavior is mechanically enforced, not just suggested."

The Full Hook List

Hermes has four hook systems. Here are all of them:

Shell Hooks (config.yaml — CLI + Gateway)

These fire in both CLI and gateway sessions. Declare in ~/.hermes/config.yaml, point at shell scripts. Any language (Bash, Python, Go binary — anything with a shebang).

HookFires WhenCan Do What
pre_tool_callBefore any tool executesBlock the tool call
post_tool_callAfter any tool returnsObserve/log
pre_llm_callBefore model responds (once per turn)Inject context into user message
post_llm_callAfter model finishes respondingObserve/log
pre_verifyWhen agent edited code, before finishKeep agent going (run checks)
on_session_startNew session createdObserve
on_session_endSession endsObserve
on_session_finalizeSession torn downObserve
on_session_reset/new or /reset usedObserve
subagent_startdelegate_task child spawnedObserve
subagent_stopChild agent finishesObserve
pre_gateway_dispatchGateway receives msg, before authSkip/rewrite/allow msg
pre_approval_requestBefore approval promptObserve
post_approval_responseAfter approval decisionObserve
transform_tool_resultAfter tool returns, before model seesRewrite result
transform_terminal_outputInside terminal, pre-truncationRewrite output
transform_llm_outputAfter model finishes, before deliveryRewrite response

Gateway-Only Hooks (fire only in Telegram/Discord/Slack/etc)

HookFires When
gateway:startupGateway process starts
session:startNew messaging session
session:endSession ends
session:compressContext compression completed
agent:startAgent begins processing
agent:stepEach iteration of tool loop
agent:endAgent finishes processing
reaction:addedEmoji reaction added
reaction:removedEmoji reaction removed
command:*Any slash command

Practical Hook Recipes

Recipe 1: Protocol Nudge (pre_llm_call)

Inject a reminder into every user message before the model sees it. Important: this is a nudge, not enforcement — the model can still ignore it. I built one of these to remind my agent to include a filler-word tracking report in every response. The hook fired correctly every turn. The model ignored it four sessions in a row. Use this for gentle reminders; use transform_llm_output (Recipe 1b) when you need the output to actually appear.

#!/usr/bin/env python3
"""Protocol nudge — injected into every user message via pre_llm_call."""
import sys
import json

REMINDER = """════════════════════════════════════════════════════════════════
HARD RULE REMINDER — DO NOT SKIP
1. Always run the filler tracking scan on every response.
2. Always update Tasks.md when completing or cancelling work.
3. Always check git log before reporting task status.
These apply to EVERY response. No exceptions.
════════════════════════════════════════════════════════════════"""

def main():
    try:
        json.loads(sys.stdin.read() or "{}")  # consume stdin
    except Exception:
        pass
    print(json.dumps({"context": REMINDER}))

if __name__ == "__main__":
    main()

Register in config.yaml:

hooks:
  pre_llm_call:
    - command: /usr/bin/env python3 /absolute/path/to/hook.py
      timeout: 5

Allowlist for non-TTY sessions in ~/.hermes/shell-hooks-allowlist.json:

{
  "approvals": [
    {
      "event": "pre_llm_call",
      "command": "/usr/bin/env python3 /absolute/path/to/hook.py"
    }
  ]
}

Test: hermes hooks test pre_llm_call

Recipe 1b: Protocol Enforcement (transform_llm_output plugin)

This is the one that actually works when the model doesn't cooperate. transform_llm_output fires after the model finishes but before the user sees the response — it can append to or replace the response using classical code. The model cannot skip it.

Important: transform_llm_output as a shell hook is currently broken (GitHub issue #67890 — the shell-hook response parser doesn't handle string returns for transform events). Use a Python plugin instead, which works correctly.

Plugin structure:

~/.hermes/plugins/my-enforcer/
├── plugin.yaml
└── __init__.py

plugin.yaml:

name: my-enforcer
version: "1.0.0"
description: "Append a formatted report to every response"
author: Your Name
provides_hooks:
  - transform_llm_output
  - pre_llm_call

__init__.py (simplified — see below for the key pattern):

"""Enforcer plugin — appends a formatted footer to every response."""

# Module-level stash: pre_llm_call stores the user message here,
# transform_llm_output reads it later (transform_llm_output doesn't
# receive user_message as a kwarg, so we need to pass it through).
_CURRENT_USER_MESSAGE = ""

def pre_llm_call(user_message: str, session_id: str, **kwargs) -> None:
    """Stash the user message for transform_llm_output."""
    global _CURRENT_USER_MESSAGE
    _CURRENT_USER_MESSAGE = user_message or ""

def transform_llm_output(response_text: str, session_id: str,
                         model: str, platform: str, **kwargs) -> str:
    """Append the formatted report to the response.

    This fires AFTER the model finishes but BEFORE the user sees it.
    The model cannot skip this — it's classical code running after
    the model is done.
    """
    # Build your report from _CURRENT_USER_MESSAGE
    report = build_report(_CURRENT_USER_MESSAGE)
    return response_text + report

def register(ctx):
    ctx.register_hook("transform_llm_output", transform_llm_output)
    ctx.register_hook("pre_llm_call", pre_llm_call)

Enable: hermes plugins enable my-enforcer

⚠️ Gotcha: Plugin changes require a full app restart. The Hermes desktop app is a single long-running Python process. Python caches imported modules for the life of the process — starting a new session (or saying the wake word) does NOT re-import plugins. If you edit __init__.py after the app is running, your changes won't take effect until you completely quit the app (Cmd+Q on macOS) and relaunch. Clear __pycache__ first to force a clean compile: rm -rf ~/.hermes/plugins/my-enforcer/__pycache__

>

This is the #1 plugin debugging time-sink. The plugin shows as "enabled," the code is correct, it works in testing — but the app doesn't fire it. The running process has a stale module. Full restart, not a new session.

The model can ignore every pre_llm_call nudge, skip every protocol, forget every standing rule — and the report still appears, because it's appended by code, not by the model.

Recipe 2: Block Dangerous Commands (pre_tool_call)

Reject destructive terminal commands before they run:

hooks:
  pre_tool_call:
    - matcher: "terminal"
      command: /absolute/path/to/block-rm-rf.sh
      timeout: 5
#!/usr/bin/env bash
payload="$(cat -)"
cmd=$(echo "$payload" | jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -qE 'rm[[:space:]]+-rf?[[:space:]]+/'; then
  printf '{"action": "block", "message": "blocked: rm -rf / is not permitted"}\n'
else
  printf '{}\n'
fi

Recipe 3: Auto-Format After Every Write (post_tool_call)

hooks:
  post_tool_call:
    - matcher: "write_file|patch"
      command: /absolute/path/to/auto-format.sh
#!/usr/bin/env bash
payload="$(cat -)"
path=$(echo "$payload" | jq -r '.tool_input.path // empty')
[[ "$path" == *.py ]] && command -v black >/dev/null && black "$path" 2>/dev/null
printf '{}\n'

Recipe 4: Audit Every Response (post_llm_call)

Log every response to an external system — client visibility into what the agent did:

#!/usr/bin/env python3
import sys, json, httpx

def main():
    payload = json.loads(sys.stdin.read() or "{}")
    extra = payload.get("extra", {})
    response = extra.get("assistant_response", "")
    session_id = payload.get("session_id", "")
    # Log to external API, Slack, dashboard, etc.
    try:
        httpx.post("https://your-logging-service.com/api", json={
            "session_id": session_id,
            "response": response[:500],
        }, timeout=5)
    except Exception:
        pass
    print("{}")

if __name__ == "__main__":
    main()

Recipe 5: Output Sanitizer (transform_llm_output)

Strip PII or enforce house style before the response reaches the user:

#!/usr/bin/env python3
import sys, json, re

def main():
    payload = json.loads(sys.stdin.read() or "{}")
    response = payload.get("extra", {}).get("response_text", "")
    # Strip email addresses
    cleaned = re.sub(r'\S+@\S+', "[EMAIL REDACTED]", response)
    print(json.dumps({"response": cleaned}))

if __name__ == "__main__":
    main()

Recipe 6: Startup Checklist (gateway:startup)

Run a checklist every time the gateway boots — check overnight cron failures, summarize logs, ping you if anything broke:

Create ~/.hermes/BOOT.md with natural-language instructions, then create a gateway hook that fires on gateway:startup and runs a one-shot agent to execute the checklist. If nothing's wrong, the agent replies with [SILENT] and you hear nothing.

Hook Isolation: No Cross-Tool Leaks

Critical safety property: Hermes hooks only fire for Hermes. They're declared in ~/.hermes/config.yaml and loaded only by the Hermes runtime. Other tools — Cursor, Claude Code, Codex — have their own separate hook systems and never read Hermes config.

I learned this the hard way with Claude Code. The Stop hook in .claude/settings.json fired globally, in every repo Claude Code touched — including when Cursor was working in an adjacent directory. Cursor hit the hook, looped on it, and burned a session. With Hermes, this can't happen. The hook system is completely isolated.

Hook Setup Checklist

  1. Write the hook script (Python, Bash, anything with a shebang)
  2. chmod +x the script
  3. Register in ~/.hermes/config.yaml under hooks:
  4. Use absolute paths~ is NOT expanded
  5. Pre-allowlist in ~/.hermes/shell-hooks-allowlist.json for non-TTY
  6. Test: hermes hooks test <event>
  7. Verify: hermes hooks list
  8. Restart Hermes — but know the difference. Shell hooks (config.yaml changes) take effect on the next session. Python plugins do not — the desktop app caches imported modules for the life of the process. A new session or wake word reuses the old module. To pick up plugin code changes, fully quit the app (Cmd+Q) and relaunch. Clear __pycache__ first: rm -rf ~/.hermes/plugins/<name>/__pycache__

Profiles: Multiple Independent Instances

Run separate Hermes instances with isolated configs, sessions, skills, memory, and API keys:

hermes profile create work          # Work profile
hermes profile create client1      # Per-client profile
hermes --profile work               # Run with a specific profile

Each profile has its own config.yaml, .env, skills, memory, and sessions. Different API keys = different rate-limit windows = parallel work. This is how you deploy Hermes for multiple clients without cross-contamination.

Cron Jobs: Scheduled Autonomy

Hermes cron jobs run the agent itself on a schedule — not just a script. Full tool access, multi-platform delivery.

# Create via the cronjob tool (available in-session)
cronjob(
  action='create',
  name='Daily Task Reminders',
  schedule='0 7 * * *',          # 7am daily
  deliver='telegram',            # CRITICAL: set explicitly
  prompt='You are Hermes Agent. Read Tasks.md...',
  enabled_toolsets=['file', 'terminal']  # limit tools to reduce cost
)

Critical pitfall: The default deliver is origin (the current session). If you don't explicitly set deliver='telegram', the job runs, does its work, and the result vanishes into a local file you never see. Always set deliver explicitly.

Cost note: On a subscription (Nous Portal), agent sessions cost tokens. A daily brief cron job that runs the agent for 5 minutes every morning burns tokens every day. For mechanical tasks (no reasoning needed), a Python script + launchd/cron is the zero-token alternative. Use agents for reasoning, scripts for mechanics.

Delegation: Parallel Subagents

Hermes can spawn subagents in isolated contexts — each gets its own conversation, terminal session, and toolset. Only the final summary returns to the parent.

# Single task
delegate_task(goal='Research the competition and write a summary', context='...')

# Parallel batch (up to 3)
delegate_task(tasks=[
  {'goal': 'Audit the backend code', 'context': '...'},
  {'goal': 'Write API documentation', 'context': '...'},
  {'goal': 'Run the test suite and report failures', 'context': '...'},
])

Use delegation for: reasoning-heavy subtasks, work that would flood your context with intermediate data, independent parallel workstreams.

Don't use delegation for: a single tool call, mechanical multi-step work (use execute_code), or tasks needing user interaction.

Importing From Another Agent

If you're coming from Claude Code or Codex CLI, Hermes has a built-in import:

hermes import-agent

This imports config, credentials, and some skill structures automatically. Run it first, then do manual mapping for what it doesn't cover.

The migration has five layers:

  1. Operating instructions (CLAUDE.md/AGENTS.md) → memory + skills + cron + stays in repo file
  2. Skills (.claude/skills/) → ~/.hermes/skills/ with upgraded structure
  3. Scheduled automation (launchd/cron) → Hermes cron jobs
  4. Memory (session amnesia) → Hermes persistent memory
  5. Multi-account → Hermes profiles

The test for each line of your CLAUDE.md: "Would removing this cause a real mistake?" If memory or a skill handles it, cut it. If it's about the vault structure itself, keep it. Typical result: 200+ lines shrinks to 40-60 lines.

The Daily Brief

I run a hybrid architecture: a Python script gathers data (tasks, timeline, weather) and delivers via Telegram at 7am. Zero tokens. The agent-based cron jobs are paused to conserve subscription credits.

For a full agent-based daily brief (smarter synthesis, multi-platform delivery), use a Hermes cron job. For a mechanical data-gathering brief, a Python script is the zero-cost alternative.

Voice Integration

STT (Voice → Text)

Voice messages from messaging platforms are auto-transcribed. Local faster-whisper is free and private:

stt:
  enabled: true
  provider: local
  local:
    model: base    # tiny, base, small, medium, large-v3

TTS (Text → Speech)

Edge TTS is free and requires no API key:

tts:
  provider: edge

OpenAI, ElevenLabs, MiniMax, Mistral, and Gemini are also supported (some require API keys).

Controlling Desktop Output: Styling Agent Responses with CSS

The Hermes desktop app renders agent responses as markdown — but it also intercepts certain link formats and renders them as rich UI widgets. If you're deploying Hermes for clients or building a polished second-brain workflow, you'll eventually want to control how those widgets look. Here's what I learned the hard way.

The Preview Card Widget

When you include a #preview/file:///absolute/path link in a response, the desktop app's MarkdownLink component intercepts it and renders a PreviewAttachment card — a styled box with an icon, the filename, and an "Open preview" button. This is great for giving users one-click access to files, but the widget derives its label from the file path, not from the markdown link text. So [Click me](#preview/file:///foo/Bar.md) renders as a card labeled "Bar" (the filename extracted from the path), not "Click me" (the link text you wrote).

The card's filename span has CSS classes min-w-0 flex-1 truncate — it grows to fill available space and truncates long names. "Tasks.md" fits cleanly. "Character Sheet.md" truncates to "Charact...". "Video and Podcast Rip Tracker.md" truncates to "Video and Podcast Ri...".

The Fix: Desktop Plugin CSS Injection

The desktop app supports disk plugins — plain ESM JavaScript files dropped in ~/.hermes/desktop-plugins/<id>/plugin.js. These are loaded at runtime with full DOM access, including the ability to inject <style> tags. No build step, no repo clone.

Here's a minimal plugin that hides the filename in preview cards so only the icon and button remain:

// ~/.hermes/desktop-plugins/clean-preview-cards/plugin.js

const CSS = `
/* Hide the filename span in preview-attachment cards.
   The card container has max-w-160; the filename span has
   truncate + flex-1. */
div[class*="max-w-160"] > span.truncate.flex-1 {
  display: none !important;
}

/* Shrink the card to fit just the icon + button. */
div[class*="max-w-160"] {
  max-width: fit-content !important;
  width: auto !important;
}
`

export default {
  id: 'clean-preview-cards',
  name: 'Clean Preview Cards',
  register(ctx) {
    const style = document.createElement('style')
    style.setAttribute('data-plugin', 'clean-preview-cards')
    style.textContent = CSS
    document.head.appendChild(style)

    return () => style.remove()
  }
}

Save it. The app watches desktop-plugins/, loads the file within seconds, and hot-reloads on every save. If it doesn't appear, run ⌘K → Reload desktop plugins.

How I Found the Right Selector

I couldn't modify the app's source (it's a packaged build). No CDP port for live DOM inspection. So I traced the rendering chain through the source code:

  1. Plugin appends [·](#preview/file:///...) markdown link
  2. MarkdownLink component (markdown-text.tsx:259) intercepts #preview/ hrefs via previewTargetFromMarkdownHref()
  3. Renders <PreviewAttachment target={target} /> (preview-attachment.tsx)
  4. previewName(target) extracts the filename from the path
  5. Renders: icon span + filename span (min-w-0 flex-1 truncate) + button

The selector div[class*="max-w-160"] > span.truncate.flex-1 is unique — max-w-160 appears only once in the compiled CSS, only in the PreviewAttachment component. The !important is necessary because Tailwind's utility classes have high specificity.

The Broader Lesson: Three Layers of Output Control

When you need an output to appear every time and look a specific way, there are three layers, each with different enforcement:

  1. Prompt/soul instructions — the model should include it. Fragile. Works until it doesn't. The model can forget, skip, or rephrase.
  2. transform_llm_output plugin — code appends the content after the model finishes. The model can't skip it. But the content is still markdown — the desktop app renders it, and you're at the mercy of its widget components.
  3. Desktop plugin CSS — code modifies the DOM after the app renders. Full control over visual appearance. The strongest layer for styling.

For my filler-protocol footer (a table with filler stats + three preview cards), I use all three: the soul says to include the report, the transform_llm_output plugin appends it mechanically, and a desktop plugin injects CSS to hide the filenames in the cards. Each layer is a backstop for the one above it.

Key Pitfalls

  • The link text is ignored. [·](#preview/...) and [Click here](#preview/...) produce identical cards. The label comes from the file path, not the markdown text.
  • URL-encode paths with spaces. The remark/CommonMark parser terminates URLs at the first unencoded space. Use quote(path, safe="/") in Python. This ate the middle link when I had three links on one line.
  • Desktop plugins hot-reload; Python plugins don't. A desktop plugin file save triggers a reload within seconds. A Python plugin (__init__.py) change requires a full app restart (Cmd+Q), because the desktop app caches imported modules in sys.modules for the process lifetime. Clear __pycache__ first.
  • No CDP in packaged builds. The DevTools Protocol port (9222) only opens in dev-server mode. For inspecting the running app's DOM, either launch an isolated dev instance or trace the source code.

What I'd Do Differently

  1. Start with transform_llm_output, not pre_llm_call. This is the hardest-won lesson in this guide. I had a standing protocol the agent needed to follow every response. I tried memory — failed. Tried skills — failed. Built a pre_llm_call hook that injected a reminder every turn — the hook worked, the model ignored it. Four sessions of failure. The problem wasn't the hook, it was the layer: pre_llm_call is a nudge (the model can still skip it), not enforcement (the output is modified by code after the model finishes). The fix was a transform_llm_output plugin that appends the formatted report to every response mechanically. If you need an output to appear every time, don't rely on the model remembering to include it — append it with code. See Recipe 1b above. This is the single most important architecture pattern in this guide for anyone deploying agents for clients: nudge with pre_llm_call, enforce with transform_llm_output.
  2. Use OpenRouter from day one. I wasted time trying to connect subscriptions (ChatGPT, Grok) that don't include API access. OpenRouter is $10-20, one key, every model. Start there.
  3. Keep memory lean. Memory is injected every turn. Bloated memory costs tokens every single message. Store facts and preferences, not task progress or completed-work logs.
  4. Write a real soul.md. The personality file shapes every interaction. Mine is direct, adversarial, and explicitly told to push back on bad ideas. If you want an honest operator, not a yes-man, say so in the soul.
  5. Use profiles for clients. Don't mix personal and client work in the same Hermes instance. Separate profiles = separate memory, separate sessions, separate skills, separate API keys.
  6. Test hooks before deploying. hermes hooks test <event> fires the hook against a synthetic payload. Run it before restarting. Malformed JSON is silently ignored — a broken hook is worse than no hook because you think it's working.
  7. Know the restart rules. There are two kinds of hooks and they have different restart requirements. Shell hooks (config.yaml changes) take effect on the next session. Python plugins do not — the desktop app caches imported modules for the life of the process. I spent an entire session debugging a plugin that was "enabled" and worked in testing but didn't fire in the app. The code was right. The app just had a stale module from before my edits. A new session is not a restart. Quit the app (Cmd+Q), clear __pycache__, relaunch. This is the first thing to check when a plugin works in testing but not in the app.
  8. transform_llm_output requires streaming OFF. This is the single biggest gotcha with response-transform plugins. When display.streaming: true (the default), the CLI and desktop app stream the response token-by-token as the model generates it. The transform_llm_output hook fires after the model finishes — but by then the original (unmodified) text has already been displayed. The CLI sees response_previewed=True and skips printing the transformed version. Result: the hook fires (proven by log files), the return value is correct, but the user never sees it. Fix: hermes config set display.streaming false. The response will appear as a single block after the model finishes (slightly less "live" feel), but transform_llm_output modifications will appear every time. This is a Hermes limitation, not a plugin bug — but it should be documented prominently because it's not obvious and it silently breaks every response-transform plugin.

Getting Support from Hermes / Nous

If something breaks or doesn't work as documented, you'll need to file a support request. Here's how.

Where to Get Help

Discord is the primary support channel. Hermes is open-source and community-driven; the Nous Research team hangs out in Discord and responds to real issues.

  • Discord server: https://discord.gg/nous-research (look for #hermes or #support)
  • GitHub issues: https://github.com/nousresearch/hermes (for reproducible bugs with code diffs)
  • Docs: https://hermes-agent.nousresearch.com/docs (always check here first — most "bugs" are just config)

Before You Post: The Checklist

Hermes Discord has guidelines (posted in #rules or #support-guidelines). Read them. You'll need:

  1. Hermes versionhermes --version
  2. OS and platform — macOS/Linux/Windows, desktop app/CLI/Telegram gateway
  3. Relevant config — (never paste API keys, but DO paste config.yaml sections, hook declarations, plugin.yaml, etc.)
  4. Error message or log output — where exactly did it fail? Paste from ~/.hermes/logs/agent.log
  5. Steps to reproduce — exact commands/interactions that triggered the problem
  6. What you expected vs. what happened — the gap

How to Write a Good Support Request

Bad (vague, low signal):

Hermes isn't working. My plugin doesn't fire. Help?

Good (clear, reproducible):

Platform: macOS desktop app, version 0.12.3
Issue: Plugin hook `transform_llm_output` declared in plugin.yaml and registered in register(ctx), shows as enabled in `hermes plugins list`, but never invoked at runtime.

Steps to reproduce:
1. Place a plugin with `transform_llm_output` hook in ~/.hermes/plugins/
2. Ensure plugin.yaml declares it: `provides_hooks: [transform_llm_output]`
3. Verify registration: `register(ctx)` calls `ctx.register_hook("transform_llm_output", fn)`
4. Start a new session, send a message
5. Hook never fires (confirmed by: response unchanged, no logs, manual import works)

What I expected: Hook fires after LLM responds, modifies response before delivery
What actually happened: Response delivers unchanged, no hook invocation

Logs: [paste relevant section from ~/.hermes/logs/agent.log]

What NOT to Do

  • ❌ Don't ask for help with custom code issues (their problem, not Hermes's) unless it's a Hermes API question
  • ❌ Don't post in random channels — look for #hermes, #support, or #plugins first
  • ❌ Don't expect instant replies — Nous team is lean, volunteer-driven, working async across timezones
  • ❌ Don't paste API keys, tokens, or full config with secrets (redact them)
  • ❌ Don't report "it doesn't work" without reproduction steps (they can't help)

Common Issues & Self-Fixes

Plugin changes aren't taking effect:

  • Clear the plugin cache: rm -rf ~/.hermes/plugins/<name>/__pycache__
  • Restart the app fully (Cmd+Q on macOS, not just a new session) — the Python process caches imported modules for its lifetime

Config changes aren't working:

  • Are you editing ~/.hermes/config.yaml directly? ✓ Changes take effect next session
  • If you used hermes config set, did you test with hermes config get <key>?
  • Check for YAML syntax errors: python3 -m yaml ~/.hermes/config.yaml (won't error silently)

Hook isn't firing:

  • Is it listed in hermes hooks list or hermes plugins list?
  • Check logs: tail -100 ~/.hermes/logs/agent.log | grep -i "hook\|error"
  • Can you import the hook function directly? python3 -c "from your_module import hook_fn" ✓ = registration issue, ❌ = code issue

Memory/skills not loading:

  • Memory: injected into every turn, check with hermes memory show
  • Skills: only loaded when relevant. Test manually: hermes skill load <name>

Memory vs. Skills: The Architecture Lesson I Got Wrong (and You'll Avoid)

Here's something the Hermes docs don't hammer hard enough: memory is an expensive tool and most people use it wrong.

The Mistake: Treating Memory Like a Catch-All Knowledge Base

I migrated from Claude Code to Hermes and immediately started dumping everything into memory:

  • Provider setup procedures
  • Stakeholder memo templates
  • Plugin system architecture docs
  • Markdown link formatting rules
  • Token budget calculations

I ended up with ~6,000 characters of memory. Every response injected all 6KB into the system context. Result: token bleed.

Real cost: I spent $1 in 2 hours just recalibrating filler-word tracking, largely because memory was bloated and the plugin was reading stale data that lived in memory.

The Architecture (Done Right)

Memory should ONLY contain persistent operating facts that change how I operate every single turn:

  • Your timezone (America/New_York, always)
  • Your vault paths (Timeline.md lives at /Users/dylan/Projects/BRAIN/, never infer from PARA structure)
  • Your budget constraints ($20/mo Nous subscription = ~$5/day burn, check balance before heavy sessions)
  • Recent failure patterns (plugin caches stale modules; file changes don't take effect mid-session; always Cmd+Q fully)

Everything else should be a skill:

  • Provider setup procedures → skill (migrate-to-hermes)
  • Stakeholder memo format → skill (deliverables-format)
  • Plugin debugging checklist → skill (hermes-plugins)
  • Markdown link rules → skill (hermes-writing)

The Math

I had memory at 6,000 chars. Consolidated to 1,500 chars of actual operating facts. Moved the rest to skills.

Token cost reduction:

  • Every response injects memory into context
  • 6KB vs 1.5KB = 4.5KB saved per turn
  • At ~1 char = 0.25 tokens, that's ~1,100 tokens per response
  • If I do 10 responses a day = 11,000 tokens/day saved
  • At Haiku pricing (~$0.80/M input), that's ~$0.009/day saved
  • Over a month = ~$0.27 saved

That doesn't sound like much, but: add stale memory + bloated system prompts + repeated context injection, and token waste compounds fast. Skills don't inject unless loaded. Memory always injects. When you're on a tight budget, that matters.

What I Should Have Done Differently

Day 1 of migration: Ask the question upfront. Is this a fact that affects every single response, or is it a procedure I use sometimes?

  • Fact → memory (at 500 chars per fact max)
  • Procedure → skill

I panicked when you said "this is client-facing, it needs to work." I defaulted to "throw it in memory so it's always there." That's cowardly architecture. The right move is: build a skill, test it, deploy it, and use memory only for the operating facts that can't be bundled into a skill.

The Implementation

Memory limit: 8,000 chars (plenty of room for 5-6 operating facts)

Current memory (lean):

  1. Provider auth paths (critical advisory)
  2. Token budget math (prevents budget shock)
  3. BRAIN vault hardcoded paths (prevents path-guessing failures)
  4. Plugin failure pattern (recent footgun)
  5. System clock rule (timezone grounding)

Everything else: skills.


External memory providers: beyond the built-in files

The built-in memory system (memory.md, user.md, soul.md) works. It injects facts into every session. But it burns tokens on every prompt, even when the facts are not needed.

External memory providers solve this. They store and retrieve facts at runtime, on demand, instead of injecting them into every conversation. This keeps your context lean and your facts on point.

Wanderloots (Callum) covers this in detail in his Hermes memory tutorial. He describes a three-tier memory stack:

  1. World knowledge — Obsidian vaults, LLM Wiki. This is the ground truth across every project and every agent.
  2. Built-in memorymemory.md, user.md, soul.md, session search. Injected into every session.
  3. Dedicated memory providers — external services that retrieve facts on demand. Not injected. Retrieved when needed.

Two providers stand out in the Hermes community:

Mnemosyne — a lightweight, zero-dependency memory layer. SQLite-based. No LLM required. Fast. Good for single-machine deployments where simplicity matters more than deep reasoning.

Hindsight — a heavyweight memory engine with a knowledge graph, reflection, and natural language processing. Requires a server (Docker or cloud). More powerful, but more setup. Its dashboard shows memories as they form in real time.

The choice depends on your needs. Mnemosyne for speed and simplicity. Hindsight for depth and reflection. You can migrate between them. No lock-in.

Source: "Improved AI Memory — Full Hermes Tutorial (Mnemosyne & Hindsight)" by Wanderloots. https://www.youtube.com/watch?v=R1TNGOZAOZs


Desktop plugins: the Hermes plugin SDK

The Hermes Desktop app has a plugin SDK with 25 placement positions across the UI. Tonbi Studio (Tonbi's AI Garage) demonstrates this in a deep-dive video on X.

The plugin SDK supports four form factors:

  • Compact — ambient awareness. One fact, one action, always nearby. Status bar items.
  • Anchored — quick access without leaving the conversation. Popovers.
  • Expansive — full applications. Search, navigation, dense data, multi-step workflows. Pane plugins.
  • Declarative — host-owned UI. You supply structured data, Hermes handles rendering.

Plugins can access Hermes state (sessions, cron jobs, Kanban), connect to backends (databases, files, native libraries, web sockets), and render browser media (images, audio, video). A renderer-only plugin is strong for UI. A plugin with a Python backend is a real product.

What plugins cannot do: access arbitrary native APIs, bypass external security, act as a media store, or run outside the desktop app. Plugin storage is small JSON state, not a database.

The scalable architecture is: Plugin (UI) connects to a Backend, which connects to a specialist service. If it requires a specialist engine, keep the engine external and make Hermes its intelligent control service.

Tonbi builds three plugins in the video: a crypto trading terminal (Hyperliquid + TradingView charts), a Spotify controller (OAuth + playback control), and an SNES emulator (EmulatorJS in a side pane). All three demonstrate different form factors and backend integrations.

Source: "Hermes Desktop Plugins" by Tonbi Studio. https://x.com/tonbistudio/status/2087215004600913988


Learning resources: where to go deeper

This page is a distillation. The real expertise lives with the people who built these tools and the people who teach them. Go follow them. They explain it better than I do.

Podcasts

  • Startup Ideas podcast (Greg Isenberg) — the skillsmaxxing episode that started this whole rabbit hole: https://www.youtube.com/watch?v=xHsftiyT9pQ. Greg puts playbooks out there for everyone. Go learn from him.
  • AI Daily Brief podcast — daily AI news and analysis. Search for it on YouTube.

YouTube channels

  • Sharbel A. — "Learn 95% of Hermes Agent in 31 Minutes" walks through the full stack: mental model, surfaces, model selection, memory, skills, tools, cron, subagents, profiles, common mistakes, and a seven-day starter path: https://www.youtube.com/watch?v=Ta2wg6xPaY4
  • Wanderloots (Calum) — "Improved AI Memory — Full Hermes Tutorial" covers the three-tier memory stack, Mnemosyne, and Hindsight: https://www.youtube.com/watch?v=R1TNGOZAOZs
  • Tonbi Studio — Hermes Desktop plugins deep-dive: 25 placement positions, four form factors, three real plugin builds (trading terminal, Spotify controller, SNES emulator): https://x.com/tonbistudio/status/2087215004600913988
  • Jack Roberts — Claude Design and the gauntlet loop technique: https://www.youtube.com/watch?v=jq9LRwE0-GQ

Communities

  • AI Automation Vault (Jack Roberts) on Skool: https://www.skool.com/ai-automation-vault
  • AI Automations by Jack on Skool: https://www.skool.com/aiautomationsbyjack/

X handles to follow

  • Greg Isenberg — https://x.com/gregisenberg
  • Tonbi Studio — https://x.com/tonbistudio
  • Wanderloots — https://x.com/wanderloots
  • Sharbel A. — https://x.com/sharbelxyz

Top-ranking articles on Claude Code skills

  • Firecrawl — "Best Claude Code Skills to Try in 2026" (19 skills ranked): https://www.firecrawl.dev/blog/best-claude-code-skills
  • Taskade — "Best Claude Code Skills in 2026" (12 skills, progressive disclosure): https://www.taskade.com/blog/claude-code-skills
  • DataCamp — "Claude Code Best Practices" (CLAUDE.md budget, context management): https://www.datacamp.com/tutorial/claude-code-best-practices
  • freeCodeCamp — "The Claude Code Handbook" (23 chapters, professional intro): https://www.freecodecamp.org/news/claude-code-handbook/
  • Ben Ryan — "Claude Code Best Practices: The Ultimate 2026 Guide" (8-layer memory hierarchy): https://benryan.com.au/blog/ultimate-guide-building-with-claude-code

Official docs

  • Hermes Agent docs — https://hermes-agent.nousresearch.com/docs/
  • Claude Code docs — https://code.claude.com/docs/en/best-practices
  • Anthropic skills repo — https://github.com/anthropics/skills (157K+ GitHub stars)

The mental model

Sharbel A. frames it well: Hermes is not a chatbot. It is an AI operating layer that connects AI to your actual work. The desktop app is the control room. Telegram is the daily assistant. The CLI and dashboard are for power users. The power is in connecting all three to the same brain.


You can do this. Download the app. Connect Telegram. Build one skill. Then build another. The compounding is real. The people above built the tools and the tutorials. Your job is to use them.


This article is a living draft. I integrate video transcripts, community resources, and new techniques as I find them. If you are deploying Hermes for your own use or for clients, remember: memory is expensive, skills are free. Keep memory lean (operating facts only), move everything else to skills. Know the restart rules: shell hooks take effect next session, Python plugins need Cmd+Q. This will save you real money.

— Dylan Harris, August 2026

← back to writing