ccc-hermes · diff

git:20260728.baa336e to git:20261013.fbe187d

82 added, 128 removed. Audit A to B.

---
name: ccc-hermes
- description: "Bridge Claude Code to OpenClaw Hermes Gateway (port 18789). Hermes channels become CC triggers; CC skills become Hermes-callable. Optional."
+ description: "Optional bridge between Claude Code and a local Hermes agent gateway: chat channels can wake a Claude Code session, and Claude Code can post progress or accept escalations over MCP. Use when the user runs a Hermes gateway and wants channel triggers, progress posts or hand-offs."
allowed-tools:
- Bash
- Read
- - Write
- Edit
+ - Write
+ - AskUserQuestion
+ argument-hint: "[status | wire | send]"
+ model: sonnet
+ effort: medium
+ disable-model-invocation: true
---
- # $ccc-hermes — Hermes ↔ Claude Code Bridge
-
- Turns Claude Code into a Hermes frontend and Hermes into a Claude Code extension. Messages flow both ways: a Telegram DM can wake a Claude Code session, and a long-running Opus task can stream progress back to your Discord. Only loads if the OpenClaw Gateway (Hermes) is running locally on port 18789.
-
- ## What it does
-
- Two-way MCP bridge between **Claude Code** (single-session deep reasoning) and **Hermes / OpenClaw Gateway** (multi-channel, multi-agent fleet across Discord, Slack, Telegram, WhatsApp, BlueBubbles, WebChat). Inbound: Hermes routes a channel message to a Claude Code session and returns the reply to the originating channel. Outbound: Claude Code calls Hermes-exposed actions (send-to-Discord, query-agent, dispatch-paperclip-task) as MCP tools. Includes auto-escalation — when a Hermes agent (typically GPT-5.5 or Sonnet) hits a hard problem, it forwards the task to Claude Code Opus and streams the answer back.
-
- ## Activation triggers
-
- - "connect to hermes" / "bridge to openclaw"
- - "I want to use my Telegram with Claude Code"
- - "send progress to my Discord while you build"
- - "let Hermes hand off hard tasks to me"
- - "wire up the gateway"
-
- ## Detection logic
-
- Always probe before activating. Bail gracefully if Hermes is not present — this skill is opt-in by environment, not by config.
-
- ```bash
- # Single non-blocking probe (2s timeout)
- curl -fsS --max-time 2 http://localhost:18789/health 2>/dev/null \
- | jq -e '.status == "ok"' >/dev/null && echo "hermes:up" || echo "hermes:down"
- ```
-
- Decision table:
-
- | Probe result | Action |
- |--------------|--------|
- | `hermes:up` | Continue — render bridge status, list available channels |
- | `hermes:down` | Print: "Hermes Gateway not detected at :18789. Install OpenClaw via `npm i -g openclaw` and run `openclaw gateway start`. Skipping bridge." Then exit. |
- | Connection refused | Same as `hermes:down` |
- | Port bound but `/health` 4xx/5xx | Print version mismatch warning + exit |
-
- Never restart the gateway from this skill. Restarts are Kevin-approved only (see `~/clawd/CLAUDE.md` Critical Rules).
+ # $ccc-hermes — channels in, progress out
- ## Architecture
+ **CC Commander** · $ccc-hermes · connects a Claude Code session to the user's own Hermes gateway (part of the third-party OpenClaw agent runtime) through an MCP bridge.
- ```
- ┌──────────────────────┐
- Discord ─────┐ │ │ ┌─── Claude Code
- Slack ─────┤ │ Hermes Gateway │ │ (Opus 5)
- Telegram ─────┼─────│ :18789 │─────┤
- WhatsApp ─────┤ │ │ │ .mcp.json
- WebChat ─────┘ │ • 36 agents │ │ └── ccc-hermes
- │ • channel router │ bridge MCP
- │ • model routing │
- │ • Paperclip queue │
- └──────────┬───────────┘
- │
- MCP bridge (stdio)
- │
- ┌──────────▼───────────┐
- │ Auto-escalator │
- │ score(task) > 0.7? │
- │ → Claude Code Opus │
- │ else │
- │ → Hermes agent │
- └──────────────────────┘
- ```
+ ## When to use / when not to
- Inbound flow: `User → Discord channel → Hermes binds channel→agent → MCP forwards to Claude Code session → reply → Hermes posts to Discord`.
+ - Use when the user already runs a Hermes gateway locally and asks to "connect to hermes", "wire up the gateway", "send progress to my Discord while you build", "use my Telegram with Claude Code", or "let the gateway hand off hard tasks".
+ - Do not use without a running gateway (this skill is opt-in by environment and exits cleanly when none is found), on a single machine with no chat surface (it only adds latency), in cloud sessions (the gateway is loopback-only), for cross-organization work (one shared token, no tenancy), or for event streams above about 10 requests per second (use a task queue).
- Outbound flow: `Claude Code calls hermes.send(channel, msg) → Hermes posts → user sees update in their channel`.
+ Architecture, escalation scoring and limits: [references/architecture.md](references/architecture.md).
- ## Setup
+ ## What happens
- ### Prerequisites
+ 1. **Read the gateway location from the environment.** The user's gateway port comes from their own setup (`openclaw gateway status` reports it); this skill never assumes one.
- - OpenClaw installed globally: `npm i -g openclaw`
- - Gateway running: `openclaw gateway status` shows green
- - Hermes secret set: `op item get "Hermes Bridge Token" --vault Alfred --field token` (or `HERMES_BRIDGE_TOKEN` env var)
+ ```bash
+ [ -n "$HERMES_GATEWAY_PORT" ] && echo "port:set" || echo "port:unset"
+ [ -n "$HERMES_BRIDGE_TOKEN" ] && echo "token:set" || echo "token:unset"
+ ```
- ### Wire up the MCP server
+ If the port is unset, ask the user to export `HERMES_GATEWAY_PORT` from their gateway's status output and stop. Never print the token; report only set or unset.
- Add to your project `.mcp.json` (or `~/.claude/mcp.json` for user-scope):
+ 2. **Probe before anything else** (2 s, non-blocking):
- ```json
- {
- "mcpServers": {
- "hermes-bridge": {
- "command": "openclaw",
- "args": ["bridge", "--mode", "mcp", "--port", "18789"],
- "env": {
- "HERMES_BRIDGE_TOKEN": "${HERMES_BRIDGE_TOKEN}"
- }
- }
- }
- }
- ```
+ ```bash
+ curl -fsS --max-time 2 "http://127.0.0.1:${HERMES_GATEWAY_PORT}/health" 2>/dev/null \
+ | jq -e '.status == "ok"' >/dev/null && echo "hermes:up" || echo "hermes:down"
+ ```
- Reload Claude Code. The bridge surfaces these MCP tools: `hermes.send`, `hermes.list_channels`, `hermes.escalate`, `hermes.subscribe`.
+ | Result | Action |
+ |---|---|
+ | `hermes:up` | Continue: render bridge status and list channels once the bridge is wired. |
+ | `hermes:down` or connection refused | Say "Hermes gateway not detected on 127.0.0.1. Install and start it per its own docs (`npm i -g openclaw`, then `openclaw gateway start`). Skipping the bridge." and stop. |
+ | Port answers but `/health` returns 4xx/5xx | Warn about a probable version mismatch and stop. |
- ### Verify
+ 3. **Check the existing wiring.**
- ```bash
- openclaw gateway status # gateway green
- claude mcp list | grep hermes-bridge # MCP connected
- ```
+ ```bash
+ claude mcp get hermes-bridge 2>/dev/null || echo "bridge:absent"
+ ```
- ## Usage examples
+ If it is already connected, skip to step 6.
- ### 1. Stream progress to Discord while Claude Code builds
+ 4. **Pick the scope** with the wiring picker (see Decision points).
- > "While you scaffold the Stripe integration, send progress updates to my #builds Discord channel."
+ 5. **Wire the bridge.** The token is referenced, never written.
+ - **Project scope:** Read `.mcp.json` if present, then Edit it (or Write it when absent) to add this entry, preserving every other server. Claude Code expands `${VAR}` from the environment at launch:
- Bridge calls `hermes.send(channel="#builds", msg=...)` after each completed step. Channel is bound to the `Alfred` agent in `openclaw.json`, but Claude Code's posts are tagged `via=ccc-hermes`.
+ ```json
+ {
+ "mcpServers": {
+ "hermes-bridge": {
+ "command": "openclaw",
+ "args": ["bridge", "--mode", "mcp", "--port", "${HERMES_GATEWAY_PORT}"],
+ "env": { "HERMES_BRIDGE_TOKEN": "${HERMES_BRIDGE_TOKEN}" }
+ }
+ }
+ }
+ ```
- ### 2. Telegram → Claude Code Opus for deep reasoning
+ - **User scope:** show this command for the user to run in their own terminal (the token expands there, outside the transcript):
- User DMs the Hermes Telegram bot: "Refactor the auth flow in `apps/api/src/auth.ts` to use JWT cookies." Hermes scores complexity (multi-file, security boundary, irreversible) → routes to Claude Code Opus via the bridge → Opus replies → Hermes posts back to Telegram.
+ ```bash
+ claude mcp add hermes-bridge -s user -e HERMES_BRIDGE_TOKEN="$HERMES_BRIDGE_TOKEN" -- openclaw bridge --mode mcp --port "$HERMES_GATEWAY_PORT"
+ ```
- ### 3. Auto-escalation: GPT-5.5 → Claude Code
+ The token itself lives in the user's secret manager and reaches the shell as `HERMES_BRIDGE_TOKEN`; this skill never reads it from a vault.
- Hermes worker (Tank, Sentinel, Codex) hits a hard architectural decision. It calls `hermes.escalate({ task, context, complexity_score })`. The bridge wakes a Claude Code session, runs the task on Opus, and streams the resolution back to the originating channel.
+ 6. **Verify.**
- ## Auto-escalation logic
+ ```bash
+ openclaw gateway status
+ claude mcp get hermes-bridge
+ ```
- Complexity scoring (0.0–1.0). Anything ≥ 0.7 escalates to Claude Code Opus by default.
+ A project-scope server shows "Pending approval" until the user approves it; tell them to approve it in `/mcp`, then run `/mcp reconnect all` (or restart the session). The bridge then exposes `hermes.send`, `hermes.list_channels`, `hermes.escalate` and `hermes.subscribe`. Call `hermes.list_channels` once and report the channel names.
- | Signal | Weight | Notes |
- |--------|--------|-------|
- | File scan only | 0.1 | Stays in Hermes (cheap models fine) |
- | Single-file edit | 0.3 | Hermes Sonnet/Codex |
- | Multi-file refactor | 0.7 | → Claude Code |
- | Architectural decision | 0.9 | → Claude Code Opus + dialectic-review |
- | Security/auth boundary | 0.85 | → Claude Code Opus + security-auditor |
- | Schema migration | 0.85 | → Claude Code Opus |
- | Production deploy gate | 0.9 | → Claude Code + deploy-check skill |
+ 7. **Use it.** For progress posts, call `hermes.send(channel, msg)` after each completed step, only to channels the user named in this conversation, with the first post per channel confirmed by the send picker. For inbound and escalated tasks, work them on the session's selected model and return the result through the same channel. Bridge tool calls go through normal permission prompts.
- Override per-task via `hermes.escalate({ force: "opus" | "hermes" })`.
+ ## Decision points
- ## Tools used
+ `AskUserQuestion`, single select; in Codex or any surface without it, ask the same options as one plain-text question.
- - **Bash** — `curl` health probe, `openclaw gateway status`, `op` vault reads
- - **Read** — inspect `.mcp.json`, `~/.openclaw/openclaw.json` (read-only — never edit)
- - **Edit/Write** — only against project `.mcp.json` (never `~/.openclaw/*`)
- - **MCP tools (when bridge active)** — `hermes.send`, `hermes.list_channels`, `hermes.escalate`, `hermes.subscribe`
+ - **Wiring scope** (header "Bridge"): 🟢 **Project `.mcp.json`** (this repo only; token by reference; visible to collaborators once committed) · **User scope** (every project; the user runs one command) · **Status only** (no change) · **Cancel**.
+ - **Send** (header "Post", first post to each channel): 🟢 **Send** (posts the shown message) · **Edit message** · **Skip this channel**.
- ## When NOT to use
+ ## Done when
- - Single-machine workflow with no chat surface — adds latency for nothing
- - Cloud Claude Code on a remote box — Hermes is local-loopback only
- - Cross-org collaboration — Hermes is a personal AI platform, not multi-tenant
- - High-frequency event streams (>10 req/sec) — use Paperclip task queue instead
+ - The probe result was reported as observed (`hermes:up` or the stop message).
+ - When wiring ran: `claude mcp get hermes-bridge` shows the server, `.mcp.json` (project scope) parses with `node -e 'JSON.parse(require("fs").readFileSync(".mcp.json","utf8"))'`, and the user was told how to approve and reload.
+ - When sending: each post reported the channel and the bridge's response.
- ## Limitations
+ ## Guardrails
- - **Loopback only:** Hermes binds 127.0.0.1:18789. To reach it from elsewhere on your network, terminate TLS via the Caddy reverse proxy (see `~/clawd/tools/caddy/Caddyfile`). Never expose :18789 publicly.
- - **Session correlation:** Each Claude Code session needs a unique ID propagated via the `X-CC-Session` header. The bridge generates one if absent, but cross-session memory requires the same ID.
- - **Auth model:** Single shared bridge token. No per-user scoping yet — anyone with the token has full agent access.
- - **Anthropic MAX disabled-billing edge case:** If the MAX account is `disabled_billing`, escalations to Opus fall back per ClaudeSwap chains. The bridge surfaces a `degraded` status but continues.
- - **Restart blast radius:** Restarting the gateway drops all in-flight bridge calls. Do not restart without Kevin's explicit approval.
- - **Codex sandbox:** Codex `exec` is read-only by default. If Hermes routes a write task to a Codex worker, either pass `-s workspace-write` (inside an isolated worktree only) or escalate to Claude Code. There is no third-party fallback — Commander routes to Anthropic models and the Codex CLI only.
+ - Posting to a channel is an external message: send only to channels the user named in this conversation, and confirm the first post per channel.
+ - Editing `.mcp.json` changes project configuration: only after the wiring picker.
+ - Never start, stop or restart the gateway from this skill; gateway lifecycle belongs to the user, and a restart drops every in-flight bridge call.
+ - Never read or edit the gateway's own configuration files; this skill touches only the project `.mcp.json`.
+ - Never write, echo or log the bridge token; reference `${HERMES_BRIDGE_TOKEN}` only.
+ - Never expose the gateway port beyond loopback; remote access needs a TLS reverse proxy the user controls.
- See `~/clawd/CLAUDE.md` and `~/clawd/shared/INTER-AGENT-PROTOCOL.md` for the full Hermes architecture and channel-to-agent binding table.
+ ## See also
- ---
+ - `$ccc-connect` — wire other opt-in MCP servers.
+ - `$ccc-relay` — chain work across Claude Code sessions without a gateway.
+ - `$ccc-mission-control` — watch agent activity from Desktop.
> ⚙️ **Fable contract:** plan before build · verifier ≠ worker · prove before alarm · loops need gates · leave durable state — `rules/fable-method.md`
+
+ > (On Codex, present these options as a numbered list and ask the user to reply with a number — AskUserQuestion is Claude-only.)