Hermes Hooks and Claude Code Hooks: Event Automation Compared
Both Hermes and Claude Code support lifecycle hooks. The events, the execution model, and the debugging experience differ — here is how to pick the right one.
Hooks are how you make an agent's behavior deterministic at specific lifecycle moments. Before a tool runs. After a response completes. When a prompt is submitted. When a skill activates. Both Hermes and Claude Code expose lifecycle hooks, but they do so with different events, different execution models, and different debugging ergonomics.
This piece compares the two side by side so you can pick the right tool for an automation — or decide whether to run both.
Key Takeaways
- Claude Code hooks fire at harness-defined events (PreToolUse, PostToolUse, Stop, UserPromptSubmit, etc.) and execute shell commands from
.claude/settings.json. - Hermes hooks fire at agent-lifecycle events (message received, skill invoked, memory persisted, task delegated) — a different slice of the runtime's operation.
- Claude Code hooks are shell-first: the harness runs a command and inspects its exit code and stdout. This is portable and scriptable.
- Hermes hooks are closer to the agent's decision loop — they sit alongside skills and memory rather than outside the harness.
- Debugging Claude Code hooks benefits from the shell being ubiquitous and loggable. Debugging Hermes hooks benefits from tighter integration with the agent state.
- Portability differs: Claude Code hooks live with your settings.json; Hermes hooks live with the Hermes runtime config.
Claude Code Hooks in One Page
Claude Code hooks are shell commands registered in settings.json under event names. The most commonly used events:
- PreToolUse — fires before a tool executes; can block the tool by exiting non-zero.
- PostToolUse — fires after a tool completes; can log, notify, or post-process.
- UserPromptSubmit — fires when the user submits a prompt; can inject context or validate input.
- Stop — fires when the agent finishes a turn; useful for desktop notifications.
- SessionStart / SessionEnd — lifecycle edges.
A minimal config that notifies you on session stop:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "osascript -e 'display notification \"Claude is done\"'" }
]
}
]
}
}
Everything about this is plain and inspectable. The matcher filters which tools or events trigger the hook. The command runs in your shell. Stdout and exit code signal to the harness whether the event should proceed.
That simplicity is the feature. A hook is just a shell command; if you can write it in bash, you can make the agent do it at the right moment.
Hermes Hooks in One Page
Hermes hooks sit inside the agent's lifecycle rather than the harness's tool-execution boundary. Conceptually, the event set covers things like:
- Message received — a user prompt (from chat, voice, or a messaging gateway) has arrived.
- Skill invoked — a SKILL.md has been activated for this turn.
- Memory persisted — the agent has written a new entry to
~/.hermes/. - Task delegated —
delegate_task()has spawned a subagent. - Task returned — a subagent's result has come back.
- Session boundary — start and end of a conversation unit.
The authoring surface is configuration plus callable hook bodies, defined alongside the Hermes runtime config. The key difference from Claude Code is that a Hermes hook runs in the runtime's context with access to agent state — the active skills, the memory store, the gateway that delivered the message — not just a blind shell process that fires off and reads a few env vars.
The Execution-Model Difference
This is the axis that matters most.
Claude Code's model is "harness executes shell." The hook is an opaque command to the harness. The harness knows almost nothing about it. This has two consequences:
- Portability is high. Any language, any binary, any script can be a hook.
- State access is limited. The hook gets env vars and the tool input/output; it does not have a rich handle to the agent's internal state.
Hermes's model is "runtime invokes hook in-process." The hook is a configured callable with access to agent state. Consequences mirror the inverse:
- State access is rich. The hook can read what skills are active, what the memory store knows, which gateway the message came from.
- Portability is lower. The hook is a Hermes runtime construct, not a free-floating shell command.
Neither is strictly better. "I want a desktop notification" is a shell-first use case; Claude Code nails it. "I want to log every memory write with the current active skill for later audit" benefits from Hermes's tighter state access.
Debugging
Debugging hook flakiness is a real part of working with either runtime.
Claude Code hooks benefit from the shell being ubiquitous. You can tail -f a log file the hook writes to. You can run the hook command by hand with fake env vars. You can version it in git like any other shell script.
Hermes hooks benefit from runtime-level observability. The Hermes log stream shows hook invocations with the triggering event and the agent state at that moment. Debugging involves reading Hermes's logs rather than bolting on your own logging.
A habit that applies to both: start hooks as no-op logs. Confirm the event fires when you think it does, at the cadence you expect, with the data you expect. Only then layer on logic. Nothing wastes time like debugging a hook's side-effect when the real problem is the event did not fire.
Portability
If you move between runtimes, hooks are one of the bigger refactor points.
- Claude Code hooks live in
.claude/settings.json. They come with your repo. - Hermes hooks live in the runtime config. They come with your Hermes install.
Because the event models differ, direct translation is often impossible. "PreToolUse" has no exact Hermes analog — the closest is a skill-invoked hook, but a skill is not a tool. "Message received" has no Claude Code analog — the closest is UserPromptSubmit, but Claude Code does not have gateway metadata.
In practice, rewrite rather than translate. The logic is usually clear; the boundary around it is what changes.
Pick Your Poison
A short heuristic:
- You want deterministic harness behavior around tool execution: Claude Code hooks.
- You want to react to agent-internal events like memory or skill activation: Hermes hooks.
- You want cross-runtime portability: write the logic as a script and shell out from either runtime's hook. That trades some of the Hermes state-access advantage for portability.
- Your agent lives in messaging gateways (Telegram, Discord, etc.): Hermes hooks are substantially richer here because the gateway metadata is first-class.
For more on the gateway side, see Hermes Messaging Gateways: Telegram and Discord. For where hooks fit in a cron-scheduled workflow, see Scheduling Claude Agents: Hermes Cron for Daily Reports.
Sources
- Hermes Agent repository — https://github.com/NousResearch/hermes-agent
- Hermes documentation — https://hermes-agent.nousresearch.com/docs/
- Anthropic Claude Code hooks documentation — https://docs.anthropic.com/claude/docs/claude-code
- Series: Hermes vs Claude Code: When to Use Which
- Series: Hermes Messaging Gateways: Telegram and Discord
- Series: Scheduling Claude Agents: Hermes Cron for Daily Reports