Files
2026-07-12 10:17:17 -04:00

6.7 KiB
Raw Permalink Blame History

name, description, version, author, tags
name description version author tags
recurring-briefings Set up cron jobs for periodic data-gathering and formatted briefings — weather checks, market updates, monitoring digests, or any recurring "fetch → analyze → deliver" workflow. 1.0.0 Hermes Agent
cron
scheduling
briefings
automation
weather
stocks
monitoring

Recurring Briefings

Set up automated daily/weekly briefings that fetch external data, analyze it against user-defined constraints, and deliver formatted reports on a schedule.

Core Pattern

Every recurring briefing follows the same shape:

cron job (scheduled) → fetch data (terminal/curl) → analyze → format → deliver (Telegram)

Step 1 — Define the Briefing

Clarify with the user:

Question Example
What data? Weather, stock chatter, news headlines
What constraints/filters? "After 6pm on weekdays," "most talked-about stocks"
When? "Every Monday 8 AM," "Weekdays 8:30 AM"
Format? Bullet list, table-free (Telegram), emoji headers

Step 2 — Create the Cron Job

Use cronjob(action='create') with these settings:

schedule: cron expression or human string like "0 8 * * 1" (Mon 8 AM)
enabled_toolsets: ["web", "terminal"]  # for fetching data

Cron Schedule Cheatsheet

Pattern Meaning
30 8 * * 1-5 MonFri at 8:30 AM
0 8 * * 1 Monday at 8 AM
0 9 * * * Every day at 9 AM
0 9 1 * * 1st of every month at 9 AM

Prompt Structure in the Cron Job

The prompt should be self-contained (cron jobs have no conversation context). Include:

  1. The agent's role — "You are a [weather/stock/market] briefing agent"
  2. Explicit data sources to call — e.g. "curl -s wttr.in/Knoxville+TN+37919 to get the JSON forecast, then parse hourly data"
  3. User constraints — "Weekdays only after 6pm, weekends anytime"
  4. Format instructions — "Output as a Telegram-friendly message with emoji headers"
  5. Anti-hallucination rule — "Actually call the sources. Do not make up data. If a source fails, note it and move on."

Step 3 — Data Sources

Firecrawl setup: see references/firecrawl-setup.md for installation, API key config, and SDK usage patterns for scraping JS-heavy financial sites.

Weather (free, no API key)

# JSON forecast (3 days)
curl -s "wttr.in/LOCATION?format=j1&days=3"

# Formatted terminal output
curl -s "wttr.in/LOCATION?0&days=3"

Parse hourly data to find dry windows matching the user's time constraints (after 6pm weekdays, anytime weekends). Key fields: chanceofrain, precipMM, tempF, windspeedMiles, weatherDesc.

Stock/Financial Buzz

  • Firecrawl (preferred) — see references/firecrawl-setup.md for setup. Bypasses JS-rendered paywalls and blocks that kill raw curl on Yahoo Finance, MarketWatch, etc. Free tier: 500 credits/month (~4 sources × 21 weekdays = 84 credits). Firecrawl explicitly blocks Reddit — do not attempt Reddit URLs with it.
  • Yahoo Finance quote APIquery1.finance.yahoo.com/v8/finance/chart/SYMBOL still works with a proper User-Agent header. Use for real-time prices on discovered tickers.
  • Google News RSSnews.google.com/rss/search?q=stock+market+today works reliably via curl.
  • Reddit (r/wallstreetbets only) — the .rss endpoint at https://www.reddit.com/r/wallstreetbets/.rss returns valid Atom XML (HTTP 200). This is the ONLY working Reddit access path. Full Reddit scraping landscape: references/reddit-scraping.md. All other subs (r/stocks, r/StockMarket, r/pennystocks) return 403 on both .rss and .json. Parse XML titles for ticker mentions ($TICKER or all-caps symbols).

Formatting for Telegram Delivery

Cron jobs auto-deliver to the home channel. Format for Telegram:

📈 **Morning Stock Buzz — [Date]**

**🔥 Most Talked-About Stocks**
- **TICKER** — why it's trending

**📰 Top Headlines**
- Headline 1
- Headline 2

**📊 Market Snapshot**
- Futures direction, sector trends, notable movers

Use: bold, bullet lists, emoji headers. No tables (Telegram rewrites them to bullets anyway).

Pitfalls

  • Self-contained prompts — cron jobs have no memory of past conversations. Every detail the agent needs must be in the prompt.
  • Rate-limited APIs — GitHub and Reddit may 429 without proper auth. Use GITHUB_TOKEN env var for GitHub API calls.
  • CGNAT blocks external access — if a briefing references a self-hosted service (like Immich), the link must use the local LAN IP, not the CGNAT address.
  • Reddit is perma-blocked — the JSON API (old.reddit.com/r/*.json, reddit.com/r/*/hot.json) returns HTML blocks, not JSON. Firecrawl explicitly rejects Reddit (403: "We do not support this site"). The only working fallback: r/wallstreetbets/.rss returns valid Atom XML. All other subreddit RSS feeds (r/stocks, r/StockMarket, r/pennystocks) return HTTP 403. Use WSB .rss as the sole Reddit source in briefing prompts; skip Reddit entirely if WSB isn't sufficient.
  • Firecrawl API key setup — must be added directly to ~/.hermes/.env (terminal echo/append), NOT via hermes config set env.FIRECRAWL_API_KEY which rejects the dotted key format. See references/firecrawl-setup.md.
  • Weekend vs weekday — stock/market briefings should run MonFri only (30 8 * * 1-5). Weather/mowing briefings can include weekends.
  • No web_search tool — use terminal with curl instead. Design the prompt to use curl-based data collection.

Alternative: no_agent=True (Watchdog Pattern)

For deterministic monitoring where the output is pre-defined (drive health checks, service uptime, disk space alerts), use no_agent=True with a script instead of the LLM-driven approach:

cronjob(
    action="create",
    name="daily-disk-check",
    schedule="0 5 * * *",
    script="disk-alert.py",      # ~/.hermes/scripts/disk-alert.py
    no_agent=True                 # script stdout = delivery message
)

When to use no_agent=True vs default (LLM-driven):

Aspect no_agent=True (watchdog) no_agent=False (briefing)
Cost Zero tokens per run LLM inference per run
Output Script stdout verbatim Agent formats the message
Use case Threshold checks, alerts, pings Summaries, digests, formatted briefings
Silent on no-op Script prints nothing = silent Agent always produces output

Key behaviors:

  • Non-empty stdin → delivered as the message
  • Empty stdout → SILENT (nothing sent to user — perfect for "all clear" checks)
  • Non-zero exit / timeout → error alert sent to user (can't fail silently)