plugins/feed-hermit/skills/hatch/SKILL.md
One-time feed hermit setup. Seeds the source registry and FEEDS.md tone spec, configures brief slots/tone/enrichments, drops routine prompts, and wires routines + a source-scout scheduled check into config.json. Run once per project after /claude-code-hermit:hatch.
npx skillsauth add gtapps/claude-code-hermit hatchInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Idempotent setup wizard for the feed plugin. Run after /claude-code-hermit:hatch has already completed.
Check whether .claude-code-hermit/config.json exists.
If it does not:
"The base hermit is not set up in this project yet. Run
/claude-code-hermit:hatchfirst, then return here."
Use AskUserQuestion: "Would you like to run /claude-code-hermit:hatch now? (yes / no)"
claude-code-hermit:hatch):
.claude-code-hermit/state/hatch-resume.json with { "skill": "feed-hermit:hatch" }./feed-hermit:hatch.)"/claude-code-hermit:hatch via the Skill tool — terminal action, stop after the call.If it does exist, run .claude-code-hermit/bin/hermit-run domain-hatch preflight feed-hermit and parse the JSON verdict. Branch on action:
upgrade-core-package / upgrade-core-applied → relay the remedy string verbatim to the operator and stop.
verify → say:
"feed-hermit {self_version} is already installed. Skip to Step 6 to re-verify, or reply 'full' to re-run the full wizard."
Use AskUserQuestion: "(verify / full)" — verify → skip to Step 6; full → continue from Step 2.
full → continue from Step 2.
ok: false → relay message and stop.
The operator owns three files at the project root: feed-sources.md, feed-categories.md, FEEDS.md. Seed each from the plugin template only if it does not already exist (never overwrite operator content).
For each of:
${CLAUDE_PLUGIN_ROOT}/state-templates/feed-sources.md → feed-sources.md${CLAUDE_PLUGIN_ROOT}/state-templates/feed-categories.md → feed-categories.md${CLAUDE_PLUGIN_ROOT}/state-templates/FEEDS.md → FEEDS.mdRead the destination first: if it exists, skip (report ⊘ skipped <file> (already present)); if not, Read the template and Write it to the root (report ✓ seeded <file>).
Starter pack (opt-in). If feed-sources.md and feed-categories.md were freshly seeded (both empty), offer the starter pack with AskUserQuestion (header: "Starter sources"): Start empty (recommended — add your own with /feed-hermit:add-source) / Seed a small generic tech/AI starter pack. If the operator opts in, Read ${CLAUDE_PLUGIN_ROOT}/state-templates/starter-pack.md and merge its Categories rows into feed-categories.md and its Sources rows into the ## Active Sources table in feed-sources.md. Never seed the starter pack over a non-empty registry.
gitignore. Read the project .gitignore. Ensure tmp/ is present (the fetch scratch dir); append it if missing. (feed-sources.md/feed-categories.md/FEEDS.md are operator content — do NOT gitignore them.)
Ask the operator (use AskUserQuestion, one prompt per decision or batched):
HH:MM, operator's local timezone). Defaults: morning 09:00, evening 21:30. Either slot can be disabled.10:30. Can be disabled.feed-brief skill (e.g. default, concise, deep). Default default. (The full voice lives in FEEDS.md; this is a coarse dial.)story_arcs (cross-reference developing stories into briefs) on/off; follow_up_cta (append a /deep-dive reply prompt to top-tier items) on/off. Defaults: both off.docs/schema.md.)Convert each HH:MM to a cron expression for Step 6 (M H * * * for daily slots; M H * * 0 for a Sunday weekly). Hold the answers in context.
Copy the three routine prompt templates from ${CLAUDE_PLUGIN_ROOT}/state-templates/compiled/ into the consumer's .claude-code-hermit/compiled/:
routine-feed-brief-morning.mdroutine-feed-brief-evening.mdroutine-weekly-digest.mdFor each: Read the source, check the destination (.claude-code-hermit/compiled/<filename>). If absent → Write it (✓ dropped <filename>). If present → skip (⊘ skipped <filename> (already present)).
Resolve target file: Step 1's preflight already returned target, target_file, target_default and needs_target_question.
If needs_target_question is true, ask with AskUserQuestion (header: "Visibility") — target_default at position 0 with (recommended): .local files (gitignored, operator-personal) / Committed files (shared with teammates). Then record it:
.claude-code-hermit/bin/hermit-run domain-hatch ensure-target feed-hermit --target <choice>
Then write the block:
.claude-code-hermit/bin/hermit-run domain-hatch sync-block feed-hermit
It appends the <!-- feed-hermit: Feed Workflow --> block when the marker is absent (creating target_file if needed) and skips when it is already present; hermit-evolve handles block replacement on upgrade.
Re-read .claude-code-hermit/config.json now — the wizard has been running since Step 1 and the on-disk file may have changed. Apply the merges below to that fresh copy.
Set _hermit_versions["feed-hermit"] to self_version from Step 1's preflight.
Set config.feed from the Step 3 answers:
{
"slots": [
{"name": "morning", "cron": "<morning cron>", "enabled": <bool>},
{"name": "evening", "cron": "<evening cron>", "enabled": <bool>}
],
"weekly": {"cron": "<weekly cron>", "enabled": <bool>},
"tone_preset": "<preset>",
"enrichments": {"story_arcs": <bool>, "follow_up_cta": <bool>},
"reaction_feedback": <bool>
}
If config.feed already exists, merge (keep operator edits; only fill absent keys).
In the routines array, for each of these IDs that is absent (by id), add it using the crons from Step 3; skip any already present. Set enabled from the slot/weekly enable answers.
{
"id": "feed-brief-morning",
"schedule": "<morning cron>",
"skill": "claude-code-hermit:session-start",
"enabled": <morning enabled>,
"run_during_waiting": true,
"prompt_file": "compiled/routine-feed-brief-morning.md"
},
{
"id": "feed-brief-evening",
"schedule": "<evening cron>",
"skill": "claude-code-hermit:session-start",
"enabled": <evening enabled>,
"run_during_waiting": true,
"prompt_file": "compiled/routine-feed-brief-evening.md"
},
{
"id": "weekly-digest",
"schedule": "<weekly cron>",
"skill": "claude-code-hermit:session-start",
"enabled": <weekly enabled>,
"run_during_waiting": true,
"prompt_file": "compiled/routine-weekly-digest.md"
}
In config.scheduled_checks, check for id: "source-scout". If absent, append; if present, skip.
{"id": "source-scout", "plugin": "feed-hermit", "skill": "feed-hermit:source-scout", "enabled": true, "trigger": "interval", "interval_days": 30}
Ensure config.storage_drift is an object and config.storage_drift.ignore is an array. If either is
absent or malformed, normalize it while preserving any valid sibling keys and existing array entries.
Append the bare directory name "briefs" when it is absent; if already present, leave the array
unchanged. This registers feed-hermit's plugin-owned archive without teaching core about a
domain-specific directory.
Write the updated config.json using the Write tool (full-file replacement to keep valid JSON).
Read .claude-code-hermit/knowledge-schema.md. If the string brief-summary: is absent, append under ## Work Products (create the header if only a stub exists):
- brief: a delivered morning/evening brief. Triggered by feed-brief-morning/evening routines. location: briefs/YYYY-MM-DD-<slot>.md
- weekly-brief: weekly synthesis over the week's briefs. Triggered by weekly-digest routine. location: briefs/weekly/YYYY-WNN.md
- brief-summary: one-line last-brief summary injected at session start. Triggered by feed-brief. location: compiled/brief-summary-last-<YYYY-MM-DD>.md
- story-arcs: developing-story tracker. Triggered by story-arcs skill. location: compiled/story-arcs-<YYYY-MM-DD>.md
- pending-delivery: queued brief awaiting redelivery. Triggered by feed-brief on send failure. location: compiled/pending-delivery.md
And under ## Raw Captures (create if absent):
- source-items: raw fetched items from the source-fetcher agent. Feeds feed-brief scoring. Retention: 3 days. location: tmp/feed-source-items-<slot>.json
If already present: skip. Use Edit.
Print a structured summary:
feed-hermit {version} setup complete.
Installation summary:
✓ Prerequisite: claude-code-hermit {base_version} confirmed
✓ Registries: feed-sources.md / feed-categories.md / FEEDS.md seeded (or already present){; starter pack applied if opted in}
✓ .gitignore: tmp/ covered
✓ config.json: feed block written, _hermit_versions stamped, {K}/3 routines added, source-scout check registered, briefs archive registered in storage_drift.ignore
✓ Routine prompts: {N}/3 dropped, {M}/3 already present
✓ CLAUDE.md: Feed Workflow block injected (or already present)
✓ knowledge-schema.md: brief types added (or already present)
Next steps:
- Add your sources: /feed-hermit:add-source (or edit feed-sources.md directly)
- Run a brief now: /feed-hermit:feed-brief --morning
- Optional Chrome: chrome/reddit-home/x sources need a running Chrome; they skip gracefully when it's down.
- Optional reddit auth: see docs/reddit.md (works unauthenticated by default).
Suggested HEARTBEAT check (add to HEARTBEAT.md if you run heartbeats):
- If .claude-code-hermit/compiled/pending-delivery.md exists and is older than 30 min, a brief failed to deliver — retry or alert.
Go always-on (recommended):
- Docker: /claude-code-hermit:docker-setup
- Bare tmux: .claude-code-hermit/bin/hermit-start
Interactive test drive: /claude-code-hermit:hermit-routines load
Installed skills:
/feed-hermit:feed-brief — the 7-phase brief pipeline (--morning|--evening|--slot)
/feed-hermit:weekly-digest — weekly synthesis + source performance
/feed-hermit:add-source — add a source (type inference + validation)
/feed-hermit:source-scout — gap-driven source discovery (scheduled, interval_days: 30)
/feed-hermit:source-health — dead-source + cost-efficiency audit
/feed-hermit:story-arcs — track developing stories
/feed-hermit:deep-dive — follow-up analysis on a briefed item
Installed subagent:
@feed-hermit:source-fetcher — Haiku web/RSS raw-collection fetcher
Read by /claude-code-hermit:docker-security when the operator enables LAN containment + DNS policy. Each entry is surfaced as a per-entry confirmation prompt; nothing here is auto-applied.
feed-sources.md (the fetch targets). Re-run /docker-security after adding sources so new domains are allowlisted.reddit.com and oauth.reddit.com — only if any reddit-typed source uses the bundled reddit-fetch.ts.(none — all sources are public cloud endpoints)
tools
Composes and delivers the daily fitness brief — a forward-looking morning read (readiness + today's plan) or a backward-looking evening read (today's training, or an earned-rest note, + tomorrow's setup) — in the operator's configured voice. Invoke with /claude-code-fitness-hermit:fitness-brief --morning|--evening|--slot <name>. Becomes the plugin's two daily beats — the morning Strava connectivity check and the evening activity sync, RPE binding, and Run deep-dive.
development
Renew the hermit's long-lived Claude login token over the channel, before it expires. Relays a one-time sign-in link to the operator, takes the code back, installs the new token, and restarts. Activates on messages like 'relogin', 'renew my login', 'reauth', 'the login is expiring', or when doctor's credential-expiry check flags setup-token.
development
Synthesizes the past 7 days of archived briefs into a weekly digest — top stories, emerging vs faded themes, category activity, and per-source performance built from archive frontmatter. Delivers to the operator's configured channel and archives a weekly note. Designed as a weekly routine. Invoke with /feed-hermit:weekly-digest.
development
Manage developing story arcs tracked across briefs — add, resolve, and list active arcs in compiled/story-arcs-*.md. Arc Watch keywords drive the feed-brief arc-tagging enrichment. Invoke with /feed-hermit:story-arcs add|resolve|list.