Mastering Hermes Automation with Cron Jobs: From Daily Briefings to Custom Watchers


If you run Hermes Agent and have never touched its cron scheduler, you are leaving one of its most powerful features on the table. Cron in Hermes is not a simple “run this script every hour” utility — it is a full-fledged automation framework that spans natural-language scheduling, skill-backed agents, multi-job pipelines, and zero-token script-only watchdogs.

In this guide, you will learn how to:

  • Schedule recurring and one-shot tasks with plain English or cron expressions
  • Attach skills to a job so it inherits expert workflows
  • Chain multiple jobs so one feeds another (the pipeline pattern)
  • Run script-only watchers that consume zero LLM tokens
  • Set up production-grade daily briefings, GitHub monitors, and website health checks

Let’s start from the ground up.

What Makes Hermes Cron Different?

Most cron implementations run a single script on a timer. Hermes cron runs a full agent session on a timer — your prompt becomes the task, the agent has access to all its tools (terminal, file, web, browser, delegation), and the result is delivered to the chat platform of your choice.

The key insight: A cron job in Hermes is “a scheduled Hermes session.” Whatever you can ask Hermes to do in chat, you can schedule it to do autonomously.

On top of that, Hermes cron adds:

  • Skill injection — load one or more skills into the session before the prompt runs
  • Pipeline chaining — one job’s output becomes the next job’s context
  • No-agent mode — run a plain script on a schedule, zero LLM tokens, stdout delivered verbatim
  • Multi-platform delivery — send results to Telegram, Discord, Slack, email, SMS, Feishu, or any configured platform
  • Full lifecycle management — pause, resume, edit, trigger-on-demand, all from chat or CLI

The scheduler runs inside the Hermes gateway daemon, ticking every 60 seconds. Jobs are stored in ~/.hermes/cron/jobs.json and use a file lock (~/.hermes/cron/.tick.lock) to prevent overlapping ticks.

Basic Scheduling: Three Ways to Create a Job

You can create a cron job three ways. All paths lead to the same scheduler.

1. From Chat with /cron

The quickest way — type the /cron slash command during a chat session:

/cron add "every 2h" "Check server status and report any issues"
/cron add "0 9 * * *" "Summarize yesterday's commits from the project repo"
/cron add "30m" "Remind me to stand up and stretch"

With a skill attached:

/cron add "every 1h" "Check feeds for new posts" --skill blogwatcher

2. From the Standalone CLI

The CLI version works identically and is scriptable:

hermes cron create "every 2h" "Check server status"
hermes cron create "0 9 * * *" "Summarize yesterday's commits" --name "daily-summary"

With multiple skills:

hermes cron create "every 1h" "Monitor feeds and maps" \
  --skill blogwatcher \
  --skill maps \
  --name "Multi-skill watcher"

3. Through Natural Conversation

Just tell Hermes what you want:

“Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.”

Hermes uses the cronjob tool internally to wire it up — no CLI, no syntax to remember.

Schedule Format Reference

Hermes accepts four schedule formats:

Format Example Behavior
Relative delay 30m, 2h, 1d One-shot, runs once after the delay
Interval every 30m, every 2h, every 1d Recurring until removed
Cron expression 0 9 * * * (daily), 0 9 * * 1-5 (weekdays), 0 */6 * * * (every 6h) Recurring on fixed schedule
ISO timestamp 2026-12-25T09:00:00 One-shot at a specific future time

You can override the default repeat count:

cronjob(
    action="create",
    prompt="Check mailbox for urgent messages",
    schedule="every 2h",
    repeat=5,        # run 5 times only, then auto-remove
)

Skill-Backed Cron Jobs

The real power begins when you attach skills. A skill encodes a reusable workflow — when a cron job loads it, the agent inherits that expertise without you cramming instructions into the prompt.

Single Skill

cronjob(
    action="create",
    skill="blogwatcher",
    prompt="Check the configured feeds and summarize anything new.",
    schedule="0 9 * * *",
    name="Morning feeds",
)

Multiple Skills

Skills load in order. The prompt becomes the final instruction on top of all loaded skills:

cronjob(
    action="create",
    skills=["blogwatcher", "maps"],
    prompt="Look for new local events and interesting nearby places, then combine them into one short brief.",
    schedule="every 6h",
    name="Local brief",
)

Practical tip: Use skills to separate concerns. A “data collector” skill fetches raw data; a “formatter” skill beautifies it; a “delivery” skill routes it. Mix and match them on different jobs.

Running Inside a Project Directory

By default, cron jobs run detached — no CLAUDE.md or AGENTS.md is loaded. Pass --workdir (CLI) or workdir= (tool call) to make the job run inside a specific repo:

hermes cron create "every 1d at 09:00" \
  "Audit open PRs, summarize CI health, and post to #eng" \
  --workdir /home/me/projects/acme

When workdir is set, project context files from that directory are injected into the system prompt, and all file/terminal tools use that directory as their working base.

Serialization note: Jobs with a workdir run sequentially (not in the parallel pool) because they change process-global terminal state. Workdir-less jobs still run in parallel.

Advanced: Multi-Job Pipelines with context_from

Cron jobs run in isolated sessions with no memory of previous runs. But sometimes one job’s output is exactly what the next job needs. The context_from parameter wires that connection automatically.

The Pipeline Pattern

Here is a 3-stage AI news pipeline — collection, triage, and delivery:

# Step 1: Find the collector job's ID
cronjob(action="list")

# Step 2: Create a triage job that receives the collector's output
cronjob(
    action="create",
    prompt="Read ~/.hermes/data/briefs/raw.md. Score each story 1-10 for engagement and novelty. Output top 5 to ~/.hermes/data/briefs/ranked.md.",
    schedule="30 7 * * *",
    context_from="<collector_job_id>",
    name="AI News Triage",
)

# Step 3: Create a shipper that receives the triage output
cronjob(
    action="create",
    prompt="Read ~/.hermes/data/briefs/ranked.md. Write 3 tweet drafts (hook + body + hashtags).",
    schedule="0 8 * * *",
    context_from="<triage_job_id>",
    deliver="telegram:7976161601",
    name="AI News Brief",
)

How context_from works:

  • When Job B fires, Hermes reads Job A’s most recent output from ~/.hermes/cron/output/{job_a_id}/*.md
  • That output is prepended to Job B’s prompt automatically
  • The chain can be any length: A → B → C → …
  • You can pass a single ID (string) or a list of IDs for fan-in patterns

When to use pipelines:

  • Multi-stage processing (collect → filter → format → deliver)
  • Dependent tasks where step N needs step N−1’s results
  • Fan-out/fan-in: one aggregator job collects results from several collectors

No-Agent Mode: Script-Only Watchdogs

For recurring tasks that don’t need an LLM — classic system watchdogs, disk/memory alerts, heartbeats, CI pings — pass no_agent=True. The scheduler runs your script on schedule and delivers stdout directly, zero tokens, zero inference calls.

CLI Setup

hermes cron create "every 5m" \
  --no-agent \
  --script memory-watchdog.sh \
  --deliver telegram \
  --name "memory-watchdog"

Agent-Driven Setup

Just tell Hermes in chat:

“Ping me on Telegram if RAM is over 85%, every 5 minutes.”

Hermes writes the check script to ~/.hermes/scripts/ and wires the cron job automatically.

Semantics of No-Agent Mode

Condition Behavior
Script stdout (non-empty) Delivered verbatim as the message
Empty stdout Silent tick — nothing is sent (the watchdog pattern)
Non-zero exit or timeout Error alert delivered (so broken watchdogs can’t fail silently)
Last line {"wakeAgent": false} Silent tick (same gate LLM jobs use)

Script files:

  • .sh / .bash → runs under /bin/bash
  • Anything else → runs under the current Python interpreter (sys.executable)
  • Must live in ~/.hermes/scripts/

Real-world example — a memory watchdog script:

#!/bin/bash
# ~/.hermes/scripts/memory-watchdog.sh
THRESHOLD=85
USAGE=$(free | awk '/^Mem:/ {printf "%.0f", $3/$2 * 100}')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
    echo "⚠️  RAM alert: ${USAGE}% used (threshold: ${THRESHOLD}%)"
    echo "Top processes:"
    ps aux --sort=-%mem | head -6
fi
# If under threshold, script produces no stdout → silent tick

This script produces output only when memory exceeds 85%. On quiet days, it sends nothing — no spam, no wasted attention.

Lifecycle Management

Every cron job has a full lifecycle. You manage it all from the CLI or from chat.

CLI Commands

hermes cron list              # List all jobs (--all for disabled ones)
hermes cron pause my-digest   # Pause by name or ID
hermes cron resume my-digest  # Re-enable
hermes cron run my-digest     # Trigger on next scheduler tick
hermes cron edit my-digest --schedule "every 4h"  # Change schedule
hermes cron edit my-digest --prompt "Revised task"
hermes cron edit my-digest --add-skill maps       # Add a skill
hermes cron edit my-digest --remove-skill maps    # Remove a skill
hermes cron edit my-digest --clear-skills          # Remove all skills
hermes cron remove my-digest  # Delete entirely
hermes cron status            # Scheduler status
hermes cron runs my-digest --limit 20  # Execution history

From Chat

/cron list
/cron pause <job_id>
/cron resume <job_id>
/cron run <job_id>
/cron edit <job_id> --schedule "every 4h"
/cron remove <job_id>

Name-based lookup: All commands accept either the hex job ID or the job’s name (case-insensitive). If a name matches multiple jobs, the command prints the candidates so you can disambiguate.

Execution History

Hermes records every cron run in ~/.hermes/cron/executions.db. Each attempt moves through claimedrunning → one of completed, failed, or unknown (after process restart). Inspect with hermes cron runs [job-id] --limit 20.

Provider Recovery and Model Pinning

Cron jobs inherit your configured fallback providers and credential pool rotation. If the primary API key is rate-limited, the job automatically falls back to an alternate provider or rotates to the next credential in the pool.

Important — model pinning behavior: When you create a cron job without specifying a provider/model, Hermes snapshots your current global default on the job. If you later change the global default, the job fails closed — it skips the run and alerts you to pin the provider/model explicitly. This prevents unattended jobs from silently switching to a paid provider or a different model:

# Pin a specific model to a job
cronjob(
    action="update",
    job_id="<job_id>",
    provider="openrouter",
    model="anthropic/claude-sonnet-4",
)

For unattended runs, hermes setup --portal (Nous Portal OAuth) is the lowest-friction option — OAuth refresh is automatic.

Safety rule: Cron-run sessions cannot create new cron jobs. Hermes disables cron management tools inside cron executions to prevent runaway scheduling loops.

Delivery Configuration

Platform Targeting

When scheduling a job, specify where the result goes via the deliver parameter:

# Deliver to Telegram
cronjob(action="create", ..., deliver="telegram")

# Deliver to a specific Discord channel
cronjob(action="create", ..., deliver="discord:#engineering")

# Deliver to multiple platforms
cronjob(action="create", ..., deliver="telegram,discord")

# Fan out to every connected home channel
cronjob(action="create", ..., deliver="all")

# Deliver to origin plus all channels
cronjob(action="create", ..., deliver="origin,all")

Supported targets include Telegram, Discord, Slack, WhatsApp, Signal, SMS, email, Feishu, DingTalk, WeCom, Matrix, and others.

The Silent Pattern

If the agent’s final response contains [SILENT], delivery is suppressed entirely. The output is still saved locally for audit, but no message is sent:

# Prompt text:
"Check if nginx is running. If everything is healthy, respond with only [SILENT]. Otherwise, report the issue."

Failed jobs always deliver regardless of the silencer — only successful runs can be silenced.

Response Wrapping

By default, delivered cron output is wrapped with a header/footer:

Cronjob Response: Morning feeds
-------------

<agent output here>

Note: The agent cannot see this message, and therefore cannot respond to it.

To deliver raw output without the wrapper:

# ~/.hermes/config.yaml
cron:
  wrap_response: false

Continuable Jobs (Reply to a Cron)

By default, a cron delivery is fire-and-forget. Set a job continuable (via attach_to_session=True) and you can reply to it — the brief becomes a conversation:

# ~/.hermes/config.yaml
cron:
  mirror_delivery: true

Or per-job via the tool:

cronjob(
    action="create",
    ...,
    attach_to_session=True,
)

On thread-capable platforms (Telegram topics, Discord threads), each delivery opens a dedicated thread. On DM-only platforms (WhatsApp, Signal), the brief is mirrored into the DM session.

Production Playbook: Three Battle-Tested Setups

1. Personal Daily Briefing

A morning briefing that collects GitHub activity, weather, and calendar items:

hermes cron create "0 8 * * 1-5" \
  "1. Check my GitHub notifications for any PRs requesting my review
   2. Check the weather forecast for today
   3. Summarize anything from my calendar that needs attention
   4. Format everything into a clean morning brief" \
  --deliver telegram \
  --name "daily-briefing"

Why this works: It runs only on weekdays (1-5), uses Hermes’ built-in web search and file tools, and delivers straight to Telegram where you can read it over coffee.

2. GitHub Repository Watchdog

A no-agent script that pings you when a repo’s latest release changes:

#!/bin/bash
# ~/.hermes/scripts/github-watchdog.sh
REPO="NousResearch/hermes-agent"
CACHE_FILE="$HOME/.hermes/cron/output/latest_release.txt"
LATEST=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" | grep -o '"tag_name": *"[^"]*"' | head -1)

if [ ! -f "$CACHE_FILE" ]; then
    echo "$LATEST" > "$CACHE_FILE"
    echo "📦 Initialized watcher for $REPO — latest: $LATEST"
    exit 0
fi

PREVIOUS=$(cat "$CACHE_FILE")
if [ "$LATEST" != "$PREVIOUS" ]; then
    echo "$LATEST" > "$CACHE_FILE"
    echo "🚀 New release detected for $REPO!"
    echo "   Previous: $PREVIOUS"
    echo "   Latest:   $LATEST"
    echo "   View: https://github.com/$REPO/releases/tag/$LATEST"
fi

Wire it up:

hermes cron create "every 6h" \
  --no-agent \
  --script github-watchdog.sh \
  --deliver telegram \
  --name "github-release-watchdog"

Zero token cost. The script runs every 6 hours, only sends a message when a release actually changes.

3. Website Health Checker

A multi-stage pipeline: website check → log analysis → alert delivery.

Stage 1 — Collector (no-agent script):

#!/bin/bash
# ~/.hermes/scripts/health-check.sh
URL="https://hermes-agent-lab.com"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$URL")
TIME=$(curl -s -o /dev/null -w "%{time_total}" --max-time 10 "$URL")
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] $URL → HTTP $STATUS (${TIME}s)"
hermes cron create "every 30m" \
  --no-agent \
  --script health-check.sh \
  --name "site-health-collector"

Stage 2 — Analysis (LLM-powered, chained from collector):

hermes cron create "0 */2 * * *" \
  "Review the last 4 health checks for hermes-agent-lab.com.
   Are any failures or slow responses apparent?
   If everything is healthy, respond with only [SILENT].
   If there is an issue, write a summary of the problem and deliver it." \
  --context_from "<collector_job_id>" \
  --name "site-health-analyst"

The collector runs every 30 minutes (free, no tokens). The analyst runs every 2 hours, gets the collector’s output as context, and only fires a message when something is wrong.

Common Pitfalls and How to Avoid Them

1. Forgetting the Gateway Must Be Running

Cron execution is handled by the gateway daemon. If the gateway is not running, your jobs won’t fire:

hermes gateway install     # Install as a user service
hermes gateway status      # Verify it is running
hermes gateway run         # Or run in foreground for testing

2. Relative Delay vs Interval Confusion

  • 30m = one-shot in 30 minutes
  • every 30m = recurring every 30 minutes

This is a common slip. Use every explicitly when you want recurrence.

3. Silent Jobs That Never Speak

If your job runs but you never see output, the agent likely responded with [SILENT] (success case) or the script produced no stdout (no-agent case). Check the local output:

ls ~/.hermes/cron/output/
cat ~/.hermes/cron/output/<job_id>/*.md

4. Model/Provider Suddenly Not Working

Unpinned jobs snapshot the current default at creation. If you changed providers (hermes model), the job alerts you to pin explicitly. Always pin production jobs:

hermes cron edit my-job --provider openrouter --model anthropic/claude-sonnet-4

5. Overlapping Pipeline Timings

When chaining jobs with context_from, make sure the upstream job finishes before the downstream one starts. If Job A runs at 0 7 * * * (7:00) and Job B runs at 0 7 * * * (also 7:00), Job B gets Job A’s output from the previous day — or an empty file if it’s the first run. Offset them by at least 15–30 minutes.

6. Workdir Jobs Blocking Each Other

Jobs with workdir set run sequentially. Design your pipelines so workdir jobs don’t become a bottleneck — keep them short, or use workdir-less intermediary jobs for heavy processing.

Summary

Hermes cron transforms you from a manual operator to someone who sets and forgets. Here is the cheat sheet:

Task Approach LLM Cost
Personal daily briefing Single LLM job with web search Low
GitHub release monitor No-agent script Zero
Website health check + alert Pipeline: no-agent collector → LLM analyst Low (2-hourly)
AI news pipeline Multi-job chain with context_from Moderate
Disk/memory watchdog No-agent script Zero
Multi-platform broadcast Single job with deliver="all" Low

The combination of full agent sessions, skill injection, no-agent script mode, and multi-job pipelines makes Hermes cron one of the most versatile automation tools available in any AI agent framework.

Quick Start Commands

# 1-minute onboarding: schedule your first job
hermes cron create "every 1d at 09:00" "Give me a 3-sentence summary of what happened on GitHub with NousResearch/hermes-agent since yesterday" --deliver telegram

# List and verify
hermes cron list
hermes cron status

# Watch it run in real time
hermes cron run my-job-name

For more on Hermes automation, see the official cron documentation, our installation guide, and the features overview.