Files
hermes-config/skills/productivity/recurring-briefings/SKILL.md
T
2026-07-12 10:17:17 -04:00

140 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: recurring-briefings
description: Set up cron jobs for periodic data-gathering and formatted briefings — weather checks, market updates, monitoring digests, or any recurring "fetch → analyze → deliver" workflow.
version: 1.0.0
author: Hermes Agent
tags: [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:
```yaml
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)
```bash
# 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 API** — `query1.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 RSS** — `news.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:
```python
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)