You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Use when Eleanor invokes `/dog` or asks to track an online changing thing and be kept updated: service status pages, outages, live blogs, release/status dashboards, shipment/event pages, feeds, or any periodically-refreshing page/API. Establish a quiet Hermes cron subscription that detects meaningful changes, notifies only on changes/new items, and stops/cleans up when the watched situation is over.
version
1.0.0
author
Fnord
license
MIT
metadata
hermes
tags
related_skills
monitoring
cron
watchdog
slash-command
status
live-updates
hermes-agent
hermes-runtime-operations
snooze
tavily
agent-browser
Dog — Online Change Watchdogs
Use this skill to turn /dog ... into a short-lived, quiet subscription to an online stream of changes.
The user intent is: “watch this thing for me, tell me when something changes, and stop when it is done.”
Command shape
/dog <what to track> [optional hints]
Examples:
/dog GitHub Actions status until resolved
/dog this live blog for new updates: https://example.com/live/...
/dog https://status.openai.com/ every 1m until the API outage is resolved
/dog this package release page and tell me when 2.4.0 is available
Core behaviour
Identify the target, the meaningful change signal, and the terminal condition.
Prefer machine-readable endpoints over page scraping.
Pick a refresh cadence. Default to 3m, but adjust when the source or situation warrants it.
Create a persisted Hermes cron job that is silent when nothing worth reporting changed.
Deliver updates to discord by default unless Eleanor explicitly names another destination.
Stop and clean up when the terminal condition is reached.
Confirm what was set up: target, signal, cadence, delivery, stopping condition, job name/id.
Triage before creating anything
Parse the request and determine:
Target: URL, service/status page, live blog, API endpoint, release page, social feed, etc.
Scope: whole page/service or a named component/incident/item?
Signal: status transition, latest incident update, new live-blog entry, new release/version, changed text block, changed JSON field.
Terminal condition: incident resolved, component operational, live blog closed/no longer updating, desired release appears, event ended, user-specified stop condition.
Cadence: choose from the source and urgency:
1m for active outages, fast live blogs, incident bridges, or explicit urgent requests.
3m default for most status pages and live streams.
5m–15m for slower pages, release pages, shipment tracking, or sites with rate limits.
Never poll aggressively if robots/rate-limit signals or source etiquette suggest otherwise.
Lifetime guard: set finite repeats unless the user explicitly asks for permanent monitoring. For example: repeat=240 at 3m is roughly 12 hours; repeat=1440 at 1m is one day. Choose a guard appropriate to the situation and mention it.
Ask a clarifying question only if missing information changes the monitoring implementation in a material way and cannot be inferred or looked up. Otherwise act.
Implementation strategy
Prefer script-only no_agent cron jobs
For /dog, prefer a deterministic no-agent cron job:
no_agent=True
script under ~/.hermes/scripts/dog-<slug>.py or .sh
state/config under ~/.hermes/state/dog-<slug>.json
stdout empty when unchanged
concise stdout when changed or done
non-zero exit only for real monitor breakage
This shape is important because no-agent cron jobs with empty stdout are silent. Agent-backed cron jobs tend to produce a final response every run, which is wrong for a quiet subscription.
Use an agent-backed cron only when change detection genuinely requires reasoning that cannot be encoded in a small script. If you do, make the prompt explicitly require no response unless there is a meaningful change, but treat it as a fallback, not the default.
GitHub releases/tags API for GitHub release watches.
For web pages with dynamic rendering, use browser tools or agent-browser to inspect the page once, then make the cron script fetch the stable underlying endpoint if possible.
State and signatures
Store compact state in JSON. Compare signatures, not raw pages.
Generic pages: extracted watched text block hash plus a short rendered/extracted summary.
Avoid signatures from unstable fields such as tracking parameters, ad markup, random IDs, page build hashes, or relative “3 minutes ago” strings.
Notifications
Print one compact message when something changes. Include:
What changed.
Current status/new item(s).
Source URL.
Timestamp in Europe/Zurich as YYYY-MM-DD HH:mm when available or when reporting the check time.
Whether monitoring continues or has stopped.
Do not print routine “checked; unchanged” messages.
Cleanup when done
A /dog job should clean up after itself when the terminal condition is reached.
Recommended pattern:
Create a config/state JSON file that includes job_id after the cron job is created.
The monitor script reads that file.
When the terminal condition is reached, the script prints the final update, then disables/removes its own cron job if job_id is present.
Keep the final state file for audit/debugging; do not delete logs/state unless Eleanor asks. “Clean up” means stop the recurring cron job and avoid future runs.
If self-removal fails, the script should still print the final update plus a concise cleanup warning. Then you should fix/remove the cron job manually if you are present in the initial setup flow.
Self-removal may be done with the cron tool during setup if the job is already known to be done, or from the script with the Hermes CLI, for example:
Run the script once manually if safe. It may emit an initial baseline notification; that is acceptable if useful. If baseline spam is undesirable, seed state before creating the job.
Create the cron job.
Update state/config with the returned job_id.
Call cronjob(action='list') and verify name, schedule, delivery, no_agent, script, enabled status, and next run.
If practical, trigger one scheduled run or wait past a scheduler tick and verify last_status=ok. Do not wait excessively for slow/lower-priority monitors.
Tell Eleanor exactly what is now being watched and when it will stop.
Bare /dog or management requests
If Eleanor sends bare /dog with no target, list active dog jobs:
Call cronjob(action='list').
Filter names beginning with dog- or prompts containing /dog/Dog monitor.
Show active job name/id, target summary if visible, cadence, next run, and last status.
If none exist, say there are no active dog subscriptions.
If Eleanor asks to stop a dog subscription:
List jobs first; never guess job ids.
Remove the matching job with cronjob(action='remove').
Leave the state file unless she asks to delete it.
Confirm the removed job.
Common patterns
Statuspage component/incident
Use the Statuspage API endpoints when present. Watch both component status and unresolved incidents affecting or mentioning the component. Stop when the component is operational and no relevant unresolved incident remains.
For a specific Statuspage incident URL (/incidents/<id>), prefer /api/v2/incidents/<id>.json, seed the current incident signature before scheduling, and compare status, impact, resolved_at, updated_at, and the latest incident_updates[0] fields. See references/statuspage-incident-api.md for the reusable pattern and schedule pitfall.
Live blog
Extract stable item identifiers. Prefer embedded JSON or RSS. If scraping HTML, use IDs, canonical timestamps, or headline+timestamp hashes. Notify with only new items since the last run, newest first or chronological depending on readability. Stop when the page marks the live blog as closed/ended, or after a conservative inactivity guard if the event is over.
Release/version watch
Use GitHub/GitLab/package-registry APIs where possible. Stop when the requested version/tag appears. For “latest release changed” watches, continue until the finite repeat guard expires unless the user names a stop condition.
Generic page block
Extract a stable content block and compare its normalised text hash. Notify with a short excerpt and URL. Avoid reporting cosmetic changes.
Common pitfalls
Creating an agent cron that speaks every run. Use no-agent with empty stdout for unchanged state.
Polling rendered HTML when an API exists. Find the API first.
Forgetting delivery. Set deliver='discord' by default for Eleanor’s proactive updates.
No terminal condition. If none is obvious, set a finite repeat guard and say so.
No job_id in state. Without it, self-cleanup cannot work reliably.
Raw page hashes. They are noisy; extract meaningful stable fields.
Using rm for cleanup. Do not use rm; leave state files or use trash only if Eleanor explicitly asks to delete artifacts.
Confirmation format
After setup, respond compactly:
Dog is watching: <target>
Signal: <what counts as an update>
Cadence: <schedule>; guard: <repeat/lifetime>
Delivery: <destination>
Stops when: <terminal condition>
Job: <name> (<id>)
Script/state: <paths>
Mention any assumptions, especially if the stop condition is inferred.
This catches meaningful changes without hashing volatile page markup.
Terminal condition
Treat the monitor as complete when either:
incident.resolved_at is non-null, or
incident.status is one of resolved, completed, or postmortem.
When done, print the final update and remove the cron job if job_id is present in state.
Seeding
Before scheduling, fetch the incident once and seed the current signature into state. Then a manual verification run should be silent when unchanged, avoiding an immediate baseline notification.
Cron schedule pitfall
For recurring short-interval jobs, create/update with schedule="every 1m". Passing schedule="1m" can be interpreted as a one-shot “once in 1m”; if that happens, immediately cronjob(action="update", schedule="every 1m") and verify with cronjob(action="list").