Daily briefing
Skills for use with Hermes that work with local models
npx -y skills add fnord123/hermes-skills --skill daily-briefingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Manages the configuration behind the user's daily briefing — which news sources are trusted or blocked, which topics are watched, and which stock tickers are tracked. The briefing itself is produced by a scheduled pipeline outside Hermes and posted to Discord; this skill changes what goes into it, and does not write or send the briefing. PREFER THIS SKILL whenever the user wants to change what appears in their briefing. Use `stock-investment- analysis` instead to actually analyse a ticker rather than add it to the list. Activate on any of: "add <source> to my briefing", "stop showing me <source>", "block <publisher>", "trust <publisher>", "follow <topic>", "track <ticker>", "add <ticker> to my briefing", "remove <ticker>", "what's in my briefing", "change my news sources", "my morning briefing".
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
11.5 KB, as published. Nobody here has run it
Daily Briefing — Configuration Companion
When to use
Activate when the user wants to read or modify the config files
that drive the daily briefing pipeline. The briefing itself is
generated entirely by ~/daily-briefing/morning-briefing.sh under
user cron — Hermes plays no role in producing it. This skill exists
only to handle natural-language management of the policy files that
the pipeline reads.
Do not use this skill for:
- Generating a briefing on demand (it's a cron pipeline; just wait for tomorrow, or run the script manually outside Hermes).
- Answering follow-up questions about today's briefing (the briefing is in conversation context; just respond normally without a skill).
Files this skill manages
| Path | Purpose | Schema |
|---|---|---|
~/daily-briefing/news-source-prefs.json | Trusted (priority-ordered) and untrusted (blocklist) news sources by domain. | { "trusted": ["domain", ...], "untrusted": ["domain", ...] } |
~/daily-briefing/news-topics.json | Keywords searched on the Brave News API each morning. | { "topics": ["keyword", ...] } |
~/daily-briefing/tickers.json | Stock tickers shown in the __Markets__ block (prior session close + day-over-day Δ). | [{"ticker": "<symbol>"}, ...] |
~/daily-briefing/calendar-people.json | Organizer-email → display-name mapping for the [Name] calendar prefix. | { "default": "Name", "organizers": { "email": "Name", ... } } |
These files are plain JSON — read with Read, edit with Write (or
edit-in-place tools). Always preserve 2-space indent and a trailing
newline so diffs stay clean.
Operations
For any operation, the procedure is: read the relevant JSON file, mutate the in-memory dict, write it back. Always preserve the existing formatting so diffs stay clean.
Trusted / untrusted news sources (news-source-prefs.json)
The trusted list is priority-ordered: lower index = higher
priority. The untrusted list is unordered.
| User intent | Operation |
|---|---|
"Add <source> to my trusted list" | Resolve to domain. Append to trusted. Remove from untrusted if present. |
"Add <source> to my trusted list at the top" | Insert at index 0 of trusted. Remove from untrusted if present. |
"Add <source> to my trusted list above <other>" | Find <other> index in trusted, insert <source> at that index. Remove from untrusted if present. |
"Add <source> to my trusted list below <other>" | Find <other> index, insert at index+1. |
"Move <source> up / down in my trusted list" | Find current index, swap with neighbor. |
"Add <source> to my untrusted list" | Append to untrusted. Remove from trusted if present. |
"Move <source> from trusted to untrusted" / "untrust <source>" | Remove from trusted, append to untrusted. |
"Remove <source> from my trusted/untrusted list" | Drop from the named list. (If user doesn't say which, infer from where it is.) |
| "Show my source preferences" | Read and pretty-print, with priority numbers on the trusted list. |
| "Untrust the source of that last article" | Identify the URL from the most recent briefing in conversation context, parse the domain, append to untrusted. |
Domain resolution (<source> → domain): Use general web knowledge:
"Reuters" → reuters.com, "NYT" or "New York Times" → nytimes.com,
"Bloomberg" → bloomberg.com, etc. Always normalize to lowercase, no
www. prefix. If the publisher name is ambiguous (e.g. "Reuters" vs
"Reuters UK" → uk.reuters.com), ask the user to clarify before
writing.
Selection-rule recap (so you can explain effects of edits):
- Stories from
untrusteddomains are dropped entirely. - Among surviving candidates, the one with the lowest index in
trustedwins. - If no candidate's domain is in
trusted, the first surviving candidate (Brave's relevance order) wins. - If everything was untrusted for a topic, the topic produces no story that day.
So adding to trusted raises a source's priority; adding to
untrusted blocks it; reordering inside trusted shifts which
trusted source wins on overlapping coverage.
Watched news topics (news-topics.json)
The topics array is the list of keyword strings sent to Brave News
search. Order doesn't affect priority — every topic gets one story
slot per day if a survivor exists.
| User intent | Operation |
|---|---|
"Add <topic> to my news topics" / "follow <topic>" | Append to topics if not already present. |
"Stop following <topic>" / "remove <topic>" | Drop matching entry (case-insensitive match, but preserve the original entry's case if you keep it). |
| "What topics do I follow" | Read and list. |
"Rename <old> to <new>" | Find and replace one entry in place. |
Topics are passed verbatim to Brave; quoted strings (e.g.
"AI capex") work as phrase searches.
Watched stock tickers (tickers.json)
The array drives the __Markets__ block in the morning briefing. Each
entry is a single object with one required field: ticker, the symbol
in Yahoo Finance convention — bare for US listings, with an exchange
suffix for international: SU.PA, 7203.T, 0700.HK, RIO.L.
fetch-tickers.sh dispatches by suffix:
- Bare ticker (no dot) → Twelve Data REST quote API. Reliable and official; covers US listings only on the free tier.
- Suffixed ticker (has a dot) → yfinance via the local Python venv. Covers any market Yahoo Finance does, but unofficial — Yahoo can break the lib until maintainers patch.
Class shares like BRK.B should be entered as BRK-B (Yahoo's
hyphen form for share classes) so they route to Twelve Data instead
of being treated as a foreign exchange suffix.
| User intent | Operation |
|---|---|
"Add <ticker> to my watchlist" / "track <ticker>" | First web_search to resolve the ticker → company name (see "Verify ticker ↔ company" below). Then append {"ticker": "<ticker>"} to the array if not already present. |
"Stop tracking <ticker>" / "remove <ticker> from my watchlist" | Drop the matching entry (case-insensitive ticker match; preserve original case if you keep). No lookup needed. |
| "What tickers am I watching" / "show my watchlist" | Read and list each entry's ticker. No lookup needed. |
"Move <ticker> to the top" | Reorder to index 0. (Order in the file is the order in the briefing table.) |
"Replace <old> with <new>" | Same lookup rule as Add — web_search for <new> before writing. |
Verify ticker ↔ company
Always web_search to ground the ticker ↔ company mapping before
writing the file or naming the company in your response. This applies
in both directions:
- User gives a ticker (e.g. "Add GEV"): web_search "GEV stock
ticker" → confirm GE Vernova → write tickers.json with
GEV→ respondAdded GEV (GE Vernova) to your watchlist. - User gives a company name (e.g. "Add GE Vernova" / "Add Schneider
Electric"): web_search "GE Vernova stock ticker" → confirm
GEV(orSU.PA) → write tickers.json → respondAdded GEV (GE Vernova) to your watchlist.
The lookup is mandatory even for tickers you think you know. Your training data may not match current ticker reality, and confidently- asserted-but-wrong company names are the canonical failure mode here. Canonical confusion sources:
- GE family:
GE(General Electric, post-spinoff parent),GEV(GE Vernova, energy spin),GEHC(GE Healthcare, separate spin). All three trade independently — none inherits the others' symbol. - Class shares:
GOOGvsGOOGL,BRK-AvsBRK-B— different voting/economic rights, both real tickers. - Multi-exchange listings:
SU.PA(Schneider Electric, Euronext Paris primary) vsSBGSF(US OTC ADR). If the user names the company without specifying exchange, ask which they want before writing.
On user correction, web_search before re-writing. If the user says "you got that wrong, I meant X," do a web_search to confirm X's actual ticker before changing tickers.json. Don't pile a second wrong assertion on the first.
Confirmation format for ticker changes
After adding or replacing, respond with the format Added <TICKER> (<Company Name>) to your watchlist. — both the symbol and the
verified company name. After removing, ticker-only is fine
(Removed GEV from your watchlist.) since the company isn't in
the file.
Calendar organizer mapping (calendar-people.json)
| User intent | Operation |
|---|---|
"Map <email> to <name> in my calendar" | Add email -> name to organizers. |
"Remove <email> from my calendar mapping" | Drop the key. |
"Set the default calendar name to <name>" | Update default. |
| "What names are mapped in my calendar" | Read and list. |
Emails are stored lowercased; the lookup in fetch-calendar.sh
lowercases before matching, so always lowercase on write.
Verification
Before saving any edit, confirm:
- The file is one of
news-source-prefs.json,news-topics.json,tickers.json, orcalendar-people.jsonunder~/daily-briefing/. Editing anything else through this skill is out of scope. - The JSON parses after the edit (no trailing commas, no schema drift — only the documented top-level keys).
- For source-prefs edits: the same domain does not appear in both
trustedanduntrusted. The latest action wins; remove from the other list. - For source-prefs edits where the user named a publisher rather than a domain: the inferred domain matches a well-known mapping, or the user has confirmed.
- After writing, briefly summarize what changed (the operation, the target file, and the resulting priority order if relevant).
When an operation reports an error
- The config file is missing → the briefing pipeline isn't set up on this
machine. Tell the user which file is absent and point them at
README.md. - The file doesn't parse as JSON → report that it is malformed and name the file. Leave it as it is; do not rewrite it from memory.
- A
web_searchfor a ticker returns nothing conclusive → say the symbol couldn't be confirmed and ask the user which company they mean. Don't write an unverified ticker. - The user names a publisher that maps to more than one domain → ask which one before writing.
Always ask the user for guidance when there is an error; do not proactively try to resolve errors yourself.
Empty results
An empty trusted, untrusted, topics, tickers or organizers list means
nothing is configured yet — say so plainly ("you aren't tracking any tickers
yet") and offer to add the first entry.