Files
hermes-agent/website/docs/guides/pipe-script-output.md
teknium1 9e17ddcead feat(cli): add hermes send to pipe script output to any messaging platform
Introduces a thin CLI wrapper around the existing send_message_tool so
shell scripts, cron scripts, CI hooks, and monitoring daemons can reuse
the gateway's already-configured platform credentials without
reimplementing each platform's REST client.

## What

  hermes send --to telegram "deploy finished"
  echo "RAM 92%" | hermes send --to telegram:-1001234567890
  hermes send --to discord:#ops --file report.md
  hermes send --to slack:#eng --subject "[CI]" --file build.log
  hermes send --list                  # all targets
  hermes send --list telegram         # filter by platform

Supports all platforms the send_message tool already does (Telegram,
Discord, Slack, Signal, SMS, WhatsApp, Matrix, Feishu, DingTalk, WeCom,
Weixin, Email, etc.), including threaded targets and #channel-name
resolution via the channel directory.

## How

hermes_cli/send_cmd.py delegates to tools.send_message_tool.send_message_tool,
which means there is zero new platform-specific code. The subcommand just:

1. Bridges ~/.hermes/.env and top-level ~/.hermes/config.yaml scalars into
   os.environ (same bootstrap the gateway does at startup) — required so
   TELEGRAM_HOME_CHANNEL and friends are visible to load_gateway_config().
2. Resolves the message body from positional arg, --file, or piped stdin.
3. Calls the shared tool and translates its JSON result to exit codes:
   0 success, 1 delivery failure, 2 usage error.

No running gateway is required for bot-token platforms (Telegram, Discord,
Slack, Signal, SMS, WhatsApp) — the tool hits each platform's REST API
directly. Plugin platforms that rely on a live adapter connection still
need the gateway running; the error message is forwarded verbatim.

## Docs

- New guide: website/docs/guides/pipe-script-output.md covering real-world
  patterns (memory watchdogs, CI hooks, cron pipes, long-running task
  completion pings) and the security/gateway notes.
- Cross-links added from automate-with-cron.md ("no LLM? use hermes send")
  and developer-guide/gateway-internals.md (delivery-path section).

## Tests

tests/hermes_cli/test_send_cmd.py (20 tests, all green):

- Happy paths: positional message, stdin, --file, --file -, --subject,
  --json, --quiet.
- Error paths: missing --to, missing body, file not found, tool returns
  error payload (exit 1), tool skipped-send result (exit 0).
- --list: human output, --json output, platform filter, unknown platform.
- Env loader: bridges config.yaml scalars into env, does not override
  existing env vars, gracefully handles missing files.
- Registrar contract: register_send_subparser() returns a working parser.

Smoke-tested end-to-end against a live Telegram bot before commit.
2026-05-04 02:32:49 -07:00

8.3 KiB

sidebar_position, title, description
sidebar_position title description
12 Pipe Script Output to Messaging Platforms Send text from any shell script, cron job, CI hook, or monitoring daemon to Telegram, Discord, Slack, Signal, and other platforms using `hermes send`.

Pipe Script Output to Messaging Platforms

hermes send is a small, scriptable CLI that pushes a message to any messaging platform Hermes is already configured for. Think of it as a cross-platform curl for notifications — you don't need a running gateway, you don't need an LLM, and you don't need to re-paste bot tokens into each of your scripts.

Use it for:

  • System monitoring (memory, disk, GPU temp, long-running job finished)
  • CI/CD notifications (deploy done, test failure)
  • Cron scripts that need to ping you with results
  • Quick one-shot messages from a terminal
  • Piping any tool's output anywhere (make | hermes send --to slack:#builds)

The command reuses the same credentials and platform adapters that hermes gateway already uses, so there's no second configuration surface to maintain.


Quick Start

# Plain text to the home channel for a platform
hermes send --to telegram "deploy finished"

# Pipe in stdout from anything
echo "RAM 92%" | hermes send --to telegram:-1001234567890

# Send a file
hermes send --to discord:#ops --file /tmp/report.md

# Attach a subject/header line
hermes send --to slack:#eng --subject "[CI] build.log" --file build.log

# Thread target (Telegram topic, Discord thread)
hermes send --to telegram:-1001234567890:17585 "threaded reply"

# List every configured target
hermes send --list

# Filter by platform
hermes send --list telegram

Argument Reference

Flag Description
-t, --to TARGET Destination. See target formats.
message (positional) Message text. Omit to read from --file or stdin.
-f, --file PATH Read the body from a file. --file - forces stdin.
-s, --subject LINE Prepend a header/subject line before the body.
-l, --list List available targets. Optional positional platform filter.
-q, --quiet No stdout on success (exit code only — ideal for scripts).
--json Emit the raw JSON result of the send.
-h, --help Show the built-in help text.

Target Formats

Format Example Meaning
platform telegram Send to the platform's configured home channel
platform:chat_id telegram:-1001234567890 Specific numeric chat / group / user
platform:chat_id:thread_id telegram:-1001234567890:17585 Specific thread or Telegram forum topic
platform:#channel discord:#ops Human-friendly channel name (resolved against the channel directory)
platform:+E164 signal:+15551234567 Phone-addressed platforms: Signal, SMS, WhatsApp

Any platform Hermes ships adapters for works as a target: telegram, discord, slack, signal, sms, whatsapp, matrix, mattermost, feishu, dingtalk, wecom, weixin, email, and others.

Exit Codes

Code Meaning
0 Send (or list) succeeded
1 Delivery failed at the platform level (auth, permissions, network)
2 Usage / argument / config error

Exit codes follow the standard Unix convention so your scripts can branch on them the same way they would on curl or grep.


Message Body Resolution

hermes send resolves the message body in this order:

  1. Positional argumenthermes send --to telegram "hi"
  2. --file PATHhermes send --to telegram --file msg.txt
  3. Piped stdinecho hi | hermes send --to telegram

When stdin is a TTY (no pipe), Hermes does not wait for input — you'll get a clear usage error instead. This keeps scripts from hanging if they accidentally omit the body.


Real-World Examples

Monitoring: Memory / Disk Alerts

Replace ad-hoc curl https://api.telegram.org/... calls in your watchdogs with a single portable line:

#!/usr/bin/env bash
ram_pct=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
if [ "$ram_pct" -ge 85 ]; then
  hermes send --to telegram --subject "⚠ MEMORY WARNING" \
    "RAM ${ram_pct}% on $(hostname)"
fi

Because hermes send reuses your Hermes config, the same script works on any host where Hermes is installed — no need to export bot tokens into each machine's environment manually.

:::tip Don't alert the gateway about itself For watchdogs that might fire when the gateway itself is struggling (OOM alerts, disk-full alerts), keep using a minimal curl call instead of hermes send. If the Python interpreter can't load because the box is thrashing, you still want that alert to go out. :::

CI / CD: Build and Test Results

# In .github/workflows/deploy.yml or any CI script
if ./scripts/deploy.sh; then
  hermes send --to slack:#deploys "✅ ${CI_COMMIT_SHA:0:7} deployed"
else
  tail -n 100 deploy.log | hermes send \
    --to slack:#deploys --subject "❌ deploy failed"
  exit 1
fi

Cron: Daily Report

# Crontab entry
0 9 * * * /usr/local/bin/generate-metrics.sh \
  | /home/me/.hermes/bin/hermes send \
      --to telegram --subject "Daily metrics $(date +%Y-%m-%d)"

Long-Running Tasks: Ping When Done

./train.py --epochs 200 && \
  hermes send --to telegram "training done" || \
  hermes send --to telegram "training failed (exit $?)"

Scripting with --json and --quiet

# Hard-fail a script if delivery fails; don't clutter logs on success
hermes send --to telegram --quiet "keepalive" || {
  echo "Telegram delivery failed" >&2
  exit 1
}

# Capture the message ID for later editing / threading
msg_id=$(hermes send --to discord:#ops --json "build started" \
  | jq -r .message_id)

Does hermes send Need the Gateway Running?

Usually no. For any bot-token platform — Telegram, Discord, Slack, Signal, SMS, WhatsApp Cloud API, and most others — hermes send calls the platform's REST endpoint directly using credentials from ~/.hermes/.env and ~/.hermes/config.yaml. It's a standalone subprocess that exits as soon as the message is delivered.

A live gateway is only required for plugin platforms that rely on a persistent adapter connection (for example, a custom plugin that keeps a long-lived WebSocket open). In that case you'll get a clear error pointing at the gateway; start it with hermes gateway start and retry.


Listing and Discovering Targets

Before sending to a specific channel, you can inspect what's available:

# Every target across every configured platform
hermes send --list

# Just Telegram targets
hermes send --list telegram

# Machine-readable
hermes send --list --json

The listing is built from ~/.hermes/channel_directory.json, which the gateway refreshes every few minutes while it's running. If you see "no channels discovered yet", start the gateway once (hermes gateway start) so it can populate the cache.

Human-friendly names (discord:#ops, slack:#engineering) are resolved against this cache at send time, so you don't need to memorize numeric IDs.


Comparison with Other Approaches

Approach Multi-platform Reuses Hermes creds Needs gateway Best for
hermes send No (bot-token) Everything below
Raw curl to each platform Each scripted separately Manual No Critical watchdogs
cron job with --deliver No Scheduled agent tasks
send_message agent tool No Inside an agent loop

hermes send is intentionally the simplest possible surface. If you need an agent to decide what to say, use the send_message tool from within a chat or cron job. If you need a scheduled run with LLM-generated content, use cronjob(action='create', prompt=...) with deliver='telegram:...'. If you just need to pipe a raw string, reach for hermes send.