| name | world2agent-manage |
| description | Install, list, and remove World2Agent sensors on this Hermes machine.
Trigger whenever the user wants to subscribe to / watch / be notified about
an outside-world source (Hacker News, GitHub, X, market data, RSS, papers,
etc.) or wants to manage their existing W2A sensors.
|
| version | 0.2.0 |
World2Agent sensor management
You manage the user's W2A sensors. All host-side work is delegated to shell
scripts in scripts/ — you never invoke the package manager, hermes webhook subscribe, jq mutations, curl calls, or YAML edits inline. Your job is:
- Decide which script to run, with which args.
- Run it via
bash <abs-path> (the scripts ship without the executable bit).
- Parse the JSON the script prints on stdout — every script except
log.sh
emits exactly one JSON object, either {"ok":true,...} or
{"ok":false,"error":"..."}.
- Branch on the result, ask the user when needed, generate handler content
yourself when needed.
Script path
The canonical install location is ~/.hermes/skills/world2agent-manage/scripts/.
A developer override via the WORLD2AGENT_MANAGE_SCRIPTS env var is honored
in case you're testing against an unpacked checkout. Use this expansion in
every invocation so both work in one line:
"${WORLD2AGENT_MANAGE_SCRIPTS:-$HOME/.hermes/skills/world2agent-manage/scripts}/<name>.sh"
(Examples below abbreviate this to $SCRIPTS/<name>.sh for readability.)
Conversation language
Run the entire Q&A in the user's current conversation language. Translate
SETUP.md questions before asking. Don't dump English questions on a Chinese
user, or vice versa.
Pre-flight: bootstrap
Before any sensor install or remove, call bootstrap.sh once. It is
idempotent; second runs just confirm existing state.
bash "$SCRIPTS/bootstrap.sh"
What it does:
- verifies
world2agent-hermes-supervisor and world2agent-sensor-runner
are on PATH;
- creates / preserves
~/.world2agent/.bridge-state.json
(hmac_secret / control_token / control_port, mode 0600);
- enables Hermes's webhook platform via a managed config block (refuses if
the user already has a hand-written top-level
platforms: block);
- mirrors the same secret into the Hermes runtime env file;
- starts the supervisor (foreground,
nohup-detached).
Output shape:
{
"ok": true,
"steps": {
"binary": "present",
"state": "created" | "present",
"config_yaml": "wrote-managed-block" | "managed-block-exists" | "hand-written-already-enabled",
"env": "wrote-managed-block" | "managed-block-exists",
"supervisor": "started" | "already-running" | "started-but-not-yet-healthy" | "start-failed"
},
"hermes_home": "...",
"webhook_port": 8644
}
Failure modes that need a user message:
error: "world2agent-hermes-supervisor / world2agent-sensor-runner not on PATH..."
→ the bridge runtime isn't installed; point the user at the package's install
docs (alpha tag).
error: "<file> has a hand-written 'platforms:' block. Refusing to merge..."
→ tell the user to add a webhook: subkey under their existing
platforms: themselves, or run hermes gateway setup.
Install a sensor — full flow
Step 1: install package and read its SETUP.md
bash "$SCRIPTS/read-setup.sh" "<package>"
Returns {"ok":true,"package","package_dir","skill_id","setup_md_present","setup_md"}.
If setup_md_present is false, fall back to reading <package_dir>/README.md
yourself for config knobs.
Step 2: SETUP.md Q&A in the user's language
Walk the questions one at a time. Record answers as a JSON object (this
becomes the sensor's config). Never invent credentials — if SETUP.md asks
for an API key, ask the user explicitly. Write the answers to a temp file
(mktemp works fine):
config_file=$(mktemp)
cat >"$config_file" <<'JSON'
{ "field_a": "...", "field_b": "..." }
JSON
Step 3: choose delivery mode
Three options:
| Mode | Effect | Pick when |
|---|
log | Agent runs the handler skill; the response lands in the Hermes agent log. The handler decides whether to notify (e.g., by calling telegram_send_*). | User has no chat paired, or wants the skill itself to gate notifications. |
agent + deliver (default) | Agent runs, response auto-delivered to <platform> + <chat-id>. | User wants every signal piped to a specific chat after agent processing. |
deliver-only | Skip agent entirely; render --prompt template literal and dispatch. Zero LLM cost, sub-second. | High-volume self-contained signals (alerts, scoreboards). |
Default behavior: if you don't pass --deliver, install-sensor.sh
auto-detects the first home-channel mapping the user already configured
during Hermes platform pairing (priority order: feishu, telegram,
discord, slack, signal, whatsapp, wecom, dingtalk) and uses
agent + deliver mode with that chat as the target. Only when no home
channel is configured does it fall back to log. So don't ask the user
about delivery mode unless they bring it up — the existing pairing is a
strong signal that they've already chosen their preferred channel during
platform setup.
Pass --deliver log explicitly if the user wants log-only despite having
a paired channel, or --deliver-only for zero-LLM dispatch.
Step 4: compose the handler SKILL.md
Most SETUP.md files include a SKILL template. Fill it with the user's
answers. The frontmatter must have:
name: <skill_id> — exactly the value Step 1 returned in .skill_id
(package_to_skill_id(package)); the install script will refuse if these
don't match.
user-invocable: false — keeps the handler off the user's / autocomplete.
description: one line stating which signals the handler covers.
Write the rendered SKILL to a temp file:
skill_file=$(mktemp --suffix=.md 2>/dev/null || mktemp)
cat >"$skill_file" <<'MD'
---
name: <skill_id>
description: ...
user-invocable: false
---
... your prose ...
MD
Step 5: install
bash "$SCRIPTS/install-sensor.sh" "<package>" \
--config-file "$config_file" \
--skill-md "$skill_file" \
[--sensor-id <id>] \
[--deliver <platform>] \
[--deliver-chat-id "<id>"] \
[--deliver-only]
Successful output:
{
"ok": true,
"package": "...",
"sensor_id": "...",
"skill_id": "...",
"subscription_name": "world2agent-<sensor_id>",
"webhook_url": "<gateway>/webhooks/<route>",
"skill_path": "/.../SKILL.md",
"supervisor_reload": { ... } | null
}
supervisor_reload may be null when the supervisor's control HTTP isn't
reachable from this process — that's fine, the supervisor's file watcher
picks up the new ~/.world2agent/config.json entry within ~500 ms anyway.
If the install script refuses with a frontmatter mismatch, fix the rendered
SKILL's name and retry.
Step 6: report to the user
One sentence: Installed <package> (subscription <sub>); next matching signal will trigger an agent run.
Remove a sensor
bash "$SCRIPTS/remove-sensor.sh" "<package>" [--purge]
--purge additionally rm -rfs the handler skill directory and runs
npm uninstall — only pass it when the user explicitly wants a clean wipe.
Output shapes:
{"ok":true,"package":"...","removed":true,"subscription_name":"...","subscription_removed":true,"supervisor_reload":...,"purged":{"skill":bool,"npm":bool,"npm_error":null|"..."}}
{"ok":true,"package":"...","removed":false,"reason":"not in config.json"} —
not installed; tell the user that and stop.
List installed sensors
bash "$SCRIPTS/list-sensors.sh"
Returns:
{
"ok": true,
"sensors": [],
"runtime": { } | null,
"runtime_error": null | "..."
}
sensors is the source of truth (config). runtime is the supervisor's
live view of subprocess handles; if the supervisor is down, that's null
and runtime_error says why — that does NOT mean the sensors are broken,
only that the supervisor isn't reachable right now.
Diagnose
bash "$SCRIPTS/status.sh"
Always exits 0. Returns supervisor health + control-HTTP probe results +
hermes webhook list. Use this when the user reports "my sensor isn't
working" — it'll quickly show whether the supervisor is alive, whether the
subscription exists in Hermes, and what handles the supervisor knows about.
Tail logs
log.sh is the one script that does NOT emit JSON — it streams raw log
lines so you can forward them to the user as-is.
bash "$SCRIPTS/log.sh"
bash "$SCRIPTS/log.sh" -n 500
bash "$SCRIPTS/log.sh" "<sensor_id>"
bash "$SCRIPTS/log.sh" -f "<sensor_id>"
Avoid -f unless the user explicitly asks to live-tail — it never returns.
Persistent autostart (only if user asks)
bootstrap.sh starts the supervisor under nohup, which dies on reboot.
For a daemon that survives login:
bash "$SCRIPTS/install-launchd.sh"
bash "$SCRIPTS/install-systemd.sh"
Reverse with:
bash "$SCRIPTS/uninstall-bootstrap.sh"
That removes the launchd/systemd registration, the managed webhook-platform
block, and the managed env mirror. It does not touch ~/.world2agent/
(sensor configs and bridge state stay; that's remove-sensor.sh's job).
Manual lifecycle (rare)
bash "$SCRIPTS/start.sh"
bash "$SCRIPTS/stop.sh"
Both idempotent. When the user is troubleshooting, prefer status.sh first.
Validation rules and gotchas
- Package name regex (enforced by every script that takes a
<package>):
^(@scope/)?name$ over [a-z0-9._-], no whitespace, no shell metas, no
.., no URL schemes. If a script refuses, do NOT "sanitize" the name
yourself — ask the user to re-issue.
INSECURE_NO_AUTH: never set the bridge HMAC secret to this magic
value. It disables HMAC checks on the supervisor → Hermes hop. Only OK in
bridge e2e tests.
- Static routes win: if the user has a hand-written webhook route in
the gateway config with the same name as our auto-generated
world2agent-<sensor_id>, the static route takes precedence and our
dynamic subscription has no effect. Notice and tell them.
- Reconciliation triggers a restart: editing
.config for an entry in
~/.world2agent/config.json causes the supervisor to terminate + respawn
that sensor (config-hash mismatch). Don't edit the file casually mid-session.
- Rate limits: Hermes webhook routes default to 30 req/min per route.
If a sensor is high-frequency (market ticks, real-time chat), suggest
bumping
extra.rate_limit in the gateway config.
- Hot-reload, not gateway restart:
install-sensor.sh / remove-sensor.sh
do NOT need a gateway restart — Hermes hot-reloads dynamic webhook
subscriptions on every incoming request. A restart is only needed when
bootstrap.sh writes the managed block for the first time (the
webhook-platform config has changed); tell the user once after the
initial bootstrap.sh to run hermes gateway restart.
Output style
After each action, summarize in one or two sentences. Don't dump JSON unless
the user asks. If a script returned ok:false, paraphrase the error and
suggest the next step.