Blogwatcher: Track Blogs and RSS/Atom Feeds in Hermes Agent
Monitor blogs and RSS/Atom feeds via blogwatcher-cli tool.
Written by Neura Market from the official Hermes Agent documentation for Blogwatcher. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationBlogwatcher gives Hermes Agent a persistent feed database it can scan, query, and act on. It wraps blogwatcher-cli, a small Go tool that tracks blogs and RSS/Atom feeds, keeps read/unread state, and can fall back to scraping a page when no feed exists. Reach for it when you want the agent to watch a set of sources over time rather than read one feed once.
What it does
The skill installs and drives blogwatcher-cli, which maintains a local SQLite database of blogs and their articles. You add sources by name and URL, scan them on demand, and list what is unread. Feeds are discovered automatically from a homepage when you do not supply one, and if RSS fails you can point the tool at a CSS selector to scrape article links instead. Categories carried in the feed are stored alongside articles, so you can filter by them later. Bulk setup comes from OPML files exported by other readers.
The important split is that blogwatcher-cli is the database, and Hermes tools do the automation around it. A recurring watch belongs in the cronjob tool's monitor field, not a bare schedule. monitor runs a script each tick and only wakes the agent when the output changes, so you point it at a command that scans and then lists articles. New articles change the output, the agent wakes with the diff, and unchanged ticks cost nothing. deliver routes digests to a chat or channel, and continuity: true lets consecutive digests dedupe.
When a user asks about a specific article, use web_extract([url]) on the URL that blogwatcher-cli articles reported. Do not re-scrape by hand.
Before you start
This is an optional skill, installed on demand rather than bundled. It runs on linux, macos, and windows. The skill path is optional-skills/research/blogwatcher, version 2.0.0, authored by JulienTant as a fork of Hyaxia/blogwatcher, under the MIT license.
You need the blogwatcher-cli binary on the machine. Pick one install method:
- Go:
go install github.com/JulienTant/blogwatcher-cli/cmd/blogwatcher-cli@latest - Docker:
docker run --rm -v blogwatcher-cli:/data ghcr.io/julientant/blogwatcher-cli - Binary (Linux amd64):
curl -sL https://github.com/JulienTant/blogwatcher-cli/releases/latest/download/blogwatcher-cli_linux_amd64.tar.gz | tar xz -C /usr/local/bin blogwatcher-cli - Binary (Linux arm64):
curl -sL https://github.com/JulienTant/blogwatcher-cli/releases/latest/download/blogwatcher-cli_linux_arm64.tar.gz | tar xz -C /usr/local/bin blogwatcher-cli - Binary (macOS Apple Silicon):
curl -sL https://github.com/JulienTant/blogwatcher-cli/releases/latest/download/blogwatcher-cli_darwin_arm64.tar.gz | tar xz -C /usr/local/bin blogwatcher-cli - Binary (macOS Intel):
curl -sL https://github.com/JulienTant/blogwatcher-cli/releases/latest/download/blogwatcher-cli_darwin_amd64.tar.gz | tar xz -C /usr/local/bin blogwatcher-cli
All releases: https://github.com/JulienTant/blogwatcher-cli/releases
Docker with persistent storage
By default the database lives at ~/.blogwatcher-cli/blogwatcher-cli.db. In Docker this is lost on container restart. Use BLOGWATCHER_DB or a volume mount to persist it:
# Named volume (simplest)
docker run --rm -v blogwatcher-cli:/data -e BLOGWATCHER_DB=/data/blogwatcher-cli.db ghcr.io/julientant/blogwatcher-cli scan
# Host bind mount
docker run --rm -v /path/on/host:/data -e BLOGWATCHER_DB=/data/blogwatcher-cli.db ghcr.io/julientant/blogwatcher-cli scan
Migrating from the original blogwatcher
If upgrading from Hyaxia/blogwatcher, move your database:
mv ~/.blogwatcher/blogwatcher.db ~/.blogwatcher-cli/blogwatcher-cli.db
The binary name changed from blogwatcher to blogwatcher-cli.
Common Commands
Managing blogs
- Add a blog:
blogwatcher-cli add "My Blog" https://example.com - Add with explicit feed:
blogwatcher-cli add "My Blog" https://example.com --feed-url https://example.com/feed.xml - Add with HTML scraping:
blogwatcher-cli add "My Blog" https://example.com --scrape-selector "article h2 a" - List tracked blogs:
blogwatcher-cli blogs - Remove a blog:
blogwatcher-cli remove "My Blog" --yes - Import from OPML:
blogwatcher-cli import subscriptions.opml
Adding a blog is the setup step you repeat per source. Give it a name you will recognise in later output and the homepage URL. If the feed is not discoverable, pass --feed-url explicitly. If there is no feed at all, pass --scrape-selector with a selector that matches the article links, and the tool will scrape when RSS fails. blogs lists what is tracked. remove takes the name and, with --yes, skips the confirmation prompt. import reads an OPML file so you can bring a whole subscription list across in one go.
Scanning and reading
- Scan all blogs:
blogwatcher-cli scan - Scan one blog:
blogwatcher-cli scan "My Blog" - List unread articles:
blogwatcher-cli articles - List all articles:
blogwatcher-cli articles --all - Filter by blog:
blogwatcher-cli articles --blog "My Blog" - Filter by category:
blogwatcher-cli articles --category "Engineering" - Mark article read:
blogwatcher-cli read 1 - Mark article unread:
blogwatcher-cli unread 1 - Mark all read:
blogwatcher-cli read-all - Mark all read for a blog:
blogwatcher-cli read-all --blog "My Blog" --yes
Scanning is what pulls new items into the database. Run it across everything, or name a single blog to limit the work. articles then shows what is unread, and the filters narrow that by blog or by category. Articles carry an index number, which is what read and unread take. read-all clears the unread state in bulk, and with --blog and --yes it does so for one source without prompting.
Environment Variables
All flags can be set via environment variables with the BLOGWATCHER_ prefix:
| Variable | Description |
|---|---|
BLOGWATCHER_DB | Path to SQLite database file |
BLOGWATCHER_WORKERS | Number of concurrent scan workers (default: 8) |
BLOGWATCHER_SILENT | Only output "scan done" when scanning |
BLOGWATCHER_YES | Skip confirmation prompts |
BLOGWATCHER_CATEGORY | Default filter for articles by category |
Environment variables matter most in automation. A cron script that runs unattended wants BLOGWATCHER_YES so nothing blocks on a prompt, and BLOGWATCHER_SILENT if you only care about the scan completing. BLOGWATCHER_DB is the one to set in Docker, where the default path does not survive a restart.
Example Output
$ blogwatcher-cli blogs
Tracked blogs (1):
xkcd
URL: https://xkcd.com
Feed: https://xkcd.com/atom.xml
Last scanned: 2026-04-03 10:30
$ blogwatcher-cli scan
Scanning 1 blog(s)...
xkcd
Source: RSS | Found: 4 | New: 4
Found 4 new article(s) total!
$ blogwatcher-cli articles
Unread articles (2):
[1] [new] Barrel - Part 13
Blog: xkcd
URL: https://xkcd.com/3095/
Published: 2026-04-02
Categories: Comics, Science
[2] [new] Volcano Fact
Blog: xkcd
URL: https://xkcd.com/3094/
Published: 2026-04-01
Categories: Comics
When not to use it
For a one-off "watch this page for changes" with no feed semantics, skip this skill. The cronjob tool's monitor field accepts an http(s) URL directly.
For a one-off read of a feed or a site's latest posts, with nothing to install, use the bundled rss-feeds skill (scripts/feed.py read URL). Blogwatcher earns its install when you track many feeds with read/unread state.
For company or competitor tracking that needs analysis and citations, prefer the competitor-news-monitor skill. Blogwatcher is the lighter raw-feed layer it can sit on.
Limits and gotchas
Feed discovery is automatic only when you omit --feed-url. HTML scraping is a fallback, and it only runs if RSS fails and you configured --scrape-selector. Categories are whatever the feed supplies; if a feed carries none, there is nothing to filter on. The database sits at ~/.blogwatcher-cli/blogwatcher-cli.db by default, overridable with --db or BLOGWATCHER_DB, and in Docker that default path is lost on restart unless you mount a volume or set the variable. If you are coming from the original Hyaxia/blogwatcher, the binary was renamed from blogwatcher to blogwatcher-cli, and the database has to be moved. Run blogwatcher-cli --help to discover all flags and options.
What pairs with it
The cronjob tool is the natural companion: its monitor field turns a scan-and-list script into a recurring watch that only wakes the agent on change, with deliver for routing digests and continuity: true for dedupe. web_extract([url]) handles reading an article once you have its URL. The bundled rss-feeds skill covers the one-off read case, and competitor-news-monitor builds analysis and citations on top of the same raw feed layer.