Install
$ agentstack add skill-osouthgate-agent-plus-skills-hermes-remote ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
hermes-remote
Remote CLI for talking to a Hermes Agent deployment over its REST API. Stdlib-only Python 3. No pip installs.
The binary lives at ${CLAUDE_SKILL_DIR}/../../bin/hermes-remote inside this plugin, and bin/ is auto-added to PATH when the plugin is enabled — so call it as hermes-remote.
When to reach for this
- The user asks about the state of a remote Hermes instance (status, model, crons, env).
- The user wants to pause, resume, trigger, or delete a Hermes cron job.
- The user wants to create a new cron — always use the skillify pattern below, not a prompt-only job.
- The user wants to inspect which env vars Hermes can see vs has missing.
For one-shot chat calls (/v1/chat/completions), use hermes-remote chat "" — needs HERMES_CHAT_API_KEY set (distinct from the admin password).
hermes-remote chat "status?" # quick one-shot
hermes-remote chat --model anthropic/claude-haiku-4-5 ... # override per-call
hermes-remote chat --max-tokens 200 "..." # cap output
hermes-remote chat --system "Be terse." "what is k8s?" # add system prompt
hermes-remote chat --json "..." # full response body
For multi-turn conversations or streaming, use an OpenAI-compatible SDK pointed at $HERMES_URL/v1 — this wrapper is for one-shot calls and cron-friendly scripting.
When NOT to use this — fall back to the Hermes REST API directly
This wrapper covers a deliberately narrow slice: status, model, env list (read-only), config get/set, cron CRUD, and one-shot chat. It does NOT wrap a local CLI — there is no hermes remote-control CLI to fall back to. "Fall back" here means hit the Hermes REST API directly with curl (or http.client / requests) using the same cookie + bearer token flow hermes-remote uses internally. There is no other route.
Specific cases where you should talk to the Hermes endpoint directly, not through hermes-remote:
- Streaming chat responses.
hermes-remote chatbuffers the full response then prints. For live token streaming, POST to$HERMES_URL/v1/chat/completionswith"stream": trueand an SSE-capable client. - Multi-turn conversations / conversation history. The
chatsubcommand is one-shot (system + user only). Maintain amessages[]array yourself and POST to/v1/chat/completionsdirectly, or use an OpenAI-compatible SDK pointed at$HERMES_URL/v1. - File uploads, attachments, or anything multipart. Not wrapped. Hit the relevant Hermes endpoint with
curl -For a multipart-capable HTTP client. - Mutating env vars, editing existing cron jobs, managing deliveries/platforms/skills, or triggering a gateway restart.
hermes-remote env listis read-only;cron createexists but there is nocron edit; gateway/platform/skill/delivery management isn't wrapped at all. Usecurlagainst/api/env,/api/cron/jobs/(PUT/PATCH),/api/gateway/*,/api/platforms/*,/api/skills/*with the session cookie + bearer token. - Authentication/login flows or session management.
hermes-remotelogs in silently on every call usingHERMES_PASSWORD. If you need to inspect the raw/loginresponse, manage cookies manually, or debug auth, hit/logindirectly. - Any Hermes endpoint the wrapper simply doesn't expose yet — job run history, raw logs, delivery channels, webhooks. Grab the cookie + token the same way (
POST /login, scrape__HERMES_SESSION_TOKEN__from/) and call the endpoint directly.
Don't get stuck in a loop. If the user needs something the wrapper clearly doesn't cover, switch to curl against the Hermes REST API immediately rather than contorting hermes-remote flags. The wrapper's job is to make the common read/cron paths ergonomic — not to be the only way in.
Configure
Config is layered. Highest precedence first:
--env-filepassed on the CLI.env.local/.envfound by walking up from the current working directory- Shell environment (including vars exported via Claude Code settings)
Project .env files override the shell — opposite of the usual rule. This lets you drop a .env in whichever repo you're working in and have it win over any globals, without having to unset anything.
Only HERMES_* and COOLIFY_* keys are picked up from .env files.
If no URL or password is configured, the CLI prints a suggestion showing exactly where to put them (project .env preferred, or ~/.claude/settings.json for a global default).
Set one URL mode and one password source.
URL — one of:
export HERMES_URL="https://hermes.example.com"
# OR for reverse-proxy-by-Host setups with flaky DNS:
export HERMES_VPS_IP="1.2.3.4"
export HERMES_HOST="hermes.example.com"
The second form hits the IP on port 80 directly and passes the Host header, letting Traefik/Caddy/Nginx route. Skips DNS.
Password — one of:
export HERMES_PASSWORD="..." # plain value
export HERMES_PASSWORD_CMD="pass show hermes" # shell command, stdout is the password
If the Hermes instance is managed by Coolify, the script can fetch the password automatically:
export COOLIFY_URL="http://your-vps:8000"
export COOLIFY_API_KEY="..."
export HERMES_APP_UUID="..."
Admin user (optional, default admin@example.com):
export HERMES_ADMIN_USER="you@example.com"
Commands
hermes-remote status # gateway state + connected platforms
hermes-remote model # current LLM + context window
hermes-remote env list [--all] # env vars Hermes knows about
hermes-remote config get [key] # read full config, or a dotted-path key
hermes-remote config set # mutate config.yaml via /api/config
hermes-remote cron list # all cron jobs, one line each
hermes-remote cron show # full JSON for one job
hermes-remote cron pause
hermes-remote cron resume
hermes-remote cron trigger # fire on next scheduler tick
hermes-remote cron remove -y
hermes-remote cron create \
--name --schedule "every 15m" \
--script /path/to/work.sh \
--prompt "If script output contains ERROR, report it. Otherwise [SILENT]." \
--deliver origin --model anthropic/claude-haiku-4-5
Most subcommands take --json for machine-parseable output. Recommended: pipe through jq for field extraction and filtering — e.g. hermes-remote cron list --json | jq '.[] | select(.state=="paused") | .id', hermes-remote config get --json | jq -r .model, hermes-remote status --json | jq .gateway_state. Every JSON payload is prefixed with a tool: {name, version} field so you can self-diagnose version drift (hermes-remote --version also works).
Changing the LLM model (READ THIS)
Hermes's config.yaml overrides env vars for keys it knows about. The LLM_MODEL env var only sets the default at first boot. Once config.yaml has a model, LLM_MODEL is ignored — so setting it in Coolify / Docker env and redeploying won't change anything.
To actually change the model, write it via the config API:
hermes-remote config set model anthropic/claude-haiku-4-5
No redeploy needed — it's live. Verify:
hermes-remote model # reads /api/model/info; should match immediately
Same applies to any other config.yaml key (agent.maxturns, compression.threshold, telegram.channelprompts, etc.) — config set is the mutation path.
Skillify cron pattern (READ BEFORE CREATING A CRON)
A Hermes cron job has two input fields: prompt and script.
promptruns inside a full agent LLM session on every tick. Default model, full context, tool loop. Expensive.scriptruns before the agent starts — pure shell/python, zero LLM tokens. Its stdout becomes context for the agent.
If your cron does deterministic work (sync, embed, poll, sanity check), the work belongs in --script. The agent should only be invoked to decide "report or stay quiet" based on the script's output.
Template for any recurring deterministic cron:
hermes-remote cron create \
--name \
--schedule "every 15m" \
--script /path/on/hermes/host/to/worker.sh \
--prompt "If the script output contains ERROR, report the relevant lines. Otherwise respond with only [SILENT]." \
--model anthropic/claude-haiku-4-5 \
--deliver origin
When the agent's response starts with [SILENT], Hermes suppresses delivery — so successful quiet runs cost essentially nothing.
Why this matters (real incident): A gbrain sync cron on our Hermes deployment was created with --prompt only, using the default Sonnet 4.6 model, running every 15 minutes. Each run invoked the full reasoning loop to execute two shell commands masquerading as reasoning work — burning OpenRouter credit fast. Fixing it to --script + minimal Haiku prompt was a roughly three-orders-of-magnitude cost reduction.
Rules of thumb for cron models:
- Cheap recurring jobs →
anthropic/claude-haiku-4-5(fast, cheap, fine for "is this output an error?") - Genuinely latent work (entity extraction, summarisation, non-trivial judgment) → stay on Sonnet but cap
max_tokensin Hermes config.
Quirks
is_build_timeis not a valid field on Hermes env POSTs (will 422). Stick to{"key":..., "value":..., "is_preview": false, "is_literal": true}.- urllib.request overrides Host based on URL netloc; this script uses
http.clientwithskip_host=Trueto honour explicit Host headers. - Hermes's
/api/envonly lists vars that match its config schema. A var can be in the container's shell env but not in this list — they're different surfaces.
Troubleshooting
HTTP 401from /login: password wrong, or you're hitting the:3000port directly and it has a separate basic-auth gate in front. Route through the main reverse-proxy URL.Couldn't extract __HERMES_SESSION_TOKEN__: login worked but the SPA HTML didn't include the token. Probably a version mismatch; upstream Hermes < 0.10 may not expose it in the same place.Unauthorizedon specific/api/*endpoints with cookie + token set: that endpoint isn't wired up in your Hermes version, or you're on a preview that renamed it. Try listing with--jsonon a known-good endpoint first to confirm auth is working.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: osouthgate
- Source: osouthgate/agent-plus-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.