plugins/claude-code-fitness-hermit/skills/hatch/SKILL.md
One-time fitness hermit setup. Configures Strava MCP access, drops routine prompt templates, and wires routines 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 fitness plugin. Run after /claude-code-hermit:hatch has already been 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": "claude-code-fitness-hermit:hatch" }./claude-code-fitness-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 claude-code-fitness-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:
"claude-code-fitness-hermit {self_version} is already installed. Skip to Step 7 to re-verify the installation, or reply 'full' to re-run the full wizard."
Use AskUserQuestion: "(verify / full)" — verify → skip to Step 7; full → continue from Step 2.
full → continue from Step 2.
ok: false → relay message and stop.
IMPORTANT: Do NOT use grep, cat, echo, or any Bash command to read .env. Three of the four required variables contain the literal string TOKEN in their name, which triggers the base hermit's deny-patterns hook on any Bash command argument. Use the Read tool only.
Tell the operator:
"This plugin needs four Strava OAuth credentials in
.env. If you haven't done this yet:
- Create
.envat the project root- Open
.envand fill in all four values from your Strava developer app:
STRAVA_CLIENT_ID— numeric app IDSTRAVA_CLIENT_SECRET— app secretSTRAVA_ACCESS_TOKEN— initial access tokenSTRAVA_REFRESH_TOKEN— refresh tokenSee https://developers.strava.com/docs/authentication/ to create a Strava app if you haven't. Reply 'done' when the file is filled in, or 'abort' to stop."
Use AskUserQuestion: "(done / abort)"
Use the Read tool to read .env. Parse each line of the form KEY=VALUE. Verify that all four of these keys are present and their values are not empty and not replace_me:
STRAVA_CLIENT_IDSTRAVA_CLIENT_SECRETSTRAVA_ACCESS_TOKENSTRAVA_REFRESH_TOKENIf any value is missing or still set to replace_me, report which ones and loop back to the AskUserQuestion above. Do not proceed until all four are valid.
Step 3.0 — Detect an ambient Strava MCP server. The operator may already run a Strava MCP server (user-scoped, or configured by another tool). Run claude mcp list (Bash) and scan for a server that talks to Strava — a name containing strava, or an entry whose command/args reference a Strava MCP package (e.g. strava-mcp-server).
AskUserQuestion (header: "Strava MCP"): Reuse the existing <name> server (recommended — no duplicate) / Install the bundled @r-huijts/strava-mcp-server.
.mcp.json write entirely. Record which server key the skills should target (if it is not strava, note in the final report that skill/settings matchers assume the key strava, so the operator should either rename their server to strava or accept that the fitness skills call mcp__strava__*). Continue to Step 4.This is a local reuse-vs-install choice only — do not attempt to reconcile or edit the operator's other MCP configs.
Using the four values you parsed from .env in Step 2 (held in working context — do not re-read .env), write the Strava MCP server entry into the project's .mcp.json.
Do not embed ${VAR} placeholders — substitute literal values. Claude Code passes MCP env blocks as literal environment variables to the child process; it does not expand shell variable syntax.
Read the project root .mcp.json (use the Read tool; treat as { "mcpServers": {} } if the file does not exist).
Check if a strava key already exists under mcpServers:
${...} placeholders → rewrite just the strava entry with literal values.The strava entry shape:
{
"strava": {
"command": "npx",
"args": ["-y", "@r-huijts/strava-mcp-server"],
"env": {
"STRAVA_CLIENT_ID": "<literal STRAVA_CLIENT_ID from .env>",
"STRAVA_CLIENT_SECRET": "<literal STRAVA_CLIENT_SECRET from .env>",
"STRAVA_ACCESS_TOKEN": "<literal STRAVA_ACCESS_TOKEN from .env>",
"STRAVA_REFRESH_TOKEN": "<literal STRAVA_REFRESH_TOKEN from .env>"
}
}
}
Write the updated .mcp.json using the Write tool.
Add .mcp.json and .env to .gitignore if not already present. Read the project .gitignore, check for each entry; append any that are missing on new lines using Edit.
Copy the four routine prompt templates from the plugin's state-templates/compiled/ into the consumer's .claude-code-hermit/compiled/.
For each of the four files:
${CLAUDE_PLUGIN_ROOT}/state-templates/compiled/routine-fitness-brief-morning.md${CLAUDE_PLUGIN_ROOT}/state-templates/compiled/routine-fitness-brief-evening.md${CLAUDE_PLUGIN_ROOT}/state-templates/compiled/routine-weekly-load-review.md${CLAUDE_PLUGIN_ROOT}/state-templates/compiled/routine-monday-planning.mdRead the source file (using Read tool), then check if the destination exists (.claude-code-hermit/compiled/<filename>):
✓ dropped <filename>.⊘ 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 claude-code-fitness-hermit --target <choice>
Then write the block:
.claude-code-hermit/bin/hermit-run domain-hatch sync-block claude-code-fitness-hermit
It appends the <!-- claude-code-fitness-hermit: Fitness 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.
Stray-block migration (block stranded in the non-target file after a target flip) is handled one-shot by the Upgrade Instructions in this version's CHANGELOG entry, executed by hermit-evolve Step 7. Hatch itself stays focused on target-aware setup.
Read .claude-code-hermit/knowledge-schema.md.
Check if activity-streams: is present in the file. This string only appears in the Raw Captures section, which is the last thing written — so its presence means both blocks were fully written on a prior run.
If absent, append the following block under ## Work Products (create the section header if the base schema only has a template stub):
- weekly-plan: weekly training structure suggestion (7-day breakdown). Triggered by monday-planning routine (Mon 09:30). location: compiled/weekly-plan-<YYYY-MM-DD>.md
- weekly-summary: week-over-week training load review. Triggered by weekly-load-review routine (Sun 18:00). location: compiled/weekly-summary-<YYYY-MM-DD>.md
- recovery-assessment: recovery indicators from recent activity data. Triggered by operator request or an evening-brief flag. location: compiled/recovery-assessment-<YYYY-MM-DD>.md
- fitness-snapshot: current fitness state snapshot. Triggered by operator request. location: compiled/fitness-snapshot-<YYYY-MM-DD>.md
- activity-note: per-activity coaching analysis. Triggered by activity-deep-dive skill. location: compiled/activity-<id>-<YYYY-MM-DD>.md
And under ## Raw Captures (create if absent):
- activity-fetch: raw activity list from Strava. Feeds weekly-plan, weekly-summary, recovery-assessment. Retention: 3 days. location: raw/activity-fetch-<date>.json
- activity-streams: HR/pace/power time-series for a specific activity. Feeds recovery-assessment, activity-note, fitness-snapshot. Retention: 7 days. location: raw/activity-streams-<id>-<date>.json
If already present: skip (idempotent).
Use Edit to make the changes.
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["claude-code-fitness-hermit"] to self_version from Step 1's preflight.
If the key already exists: update the value. If absent: add it alongside the existing _hermit_versions["claude-code-hermit"] entry.
In the routines array, check for each of these four IDs. For any that are absent, add the entry. For any that are present (by id), skip (do not clobber existing operator edits).
{
"id": "morning-brief",
"schedule": "30 7 * * *",
"skill": "claude-code-hermit:session-start",
"enabled": true,
"run_during_waiting": true,
"prompt_file": "compiled/routine-fitness-brief-morning.md"
},
{
"id": "evening-brief",
"schedule": "30 21 * * *",
"skill": "claude-code-hermit:session-start",
"enabled": true,
"run_during_waiting": true,
"prompt_file": "compiled/routine-fitness-brief-evening.md"
},
{
"id": "weekly-load-review",
"schedule": "0 18 * * 0",
"skill": "claude-code-hermit:session-start",
"enabled": true,
"run_during_waiting": true,
"prompt_file": "compiled/routine-weekly-load-review.md"
},
{
"id": "monday-planning",
"schedule": "30 9 * * 1",
"skill": "claude-code-hermit:session-start",
"enabled": true,
"run_during_waiting": true,
"prompt_file": "compiled/routine-monday-planning.md"
}
In config.scheduled_checks, check for an entry with id: "weekly-coaching-patterns". If absent, append it. If present (by id), skip — do not clobber existing operator edits.
{"id": "weekly-coaching-patterns", "plugin": "claude-code-fitness-hermit", "skill": "claude-code-fitness-hermit:weekly-coaching-patterns", "enabled": true, "trigger": "interval", "interval_days": 7}
No prompt needed — this is a read-only analysis. The core daily scheduled-checks routine picks it up; interval_days: 7 gates cadence. Findings surface as proposals automatically via the existing pipeline.
Write the updated config.json using Write tool (full file replacement to ensure valid JSON).
Run bun ${CLAUDE_PLUGIN_ROOT}/scripts/automode-env.ts .claude/settings.local.json — always .claude/settings.local.json, regardless of hatch_target: Claude Code's auto-mode classifier reads autoMode config only from local/user scope, never a committed project .claude/settings.json. This names www.strava.com as a trusted external service, so the classifier stops treating the nightly evening-brief routine's read-only fetches as unrecognized outbound calls. Additive and idempotent; safe to re-run on every hatch. No prompt needed.
Print a structured summary:
claude-code-fitness-hermit {version} setup complete.
Installation summary:
✓ Prerequisite: claude-code-hermit {base_version} confirmed
✓ .env: all four Strava credentials present
✓ .mcp.json: strava server entry written (or was already present)
✓ .gitignore: .mcp.json and .env covered
✓ Routine prompts: {N}/4 dropped, {M}/4 already present
✓ CLAUDE.md: Fitness Workflow block injected (or was already present)
✓ knowledge-schema.md: fitness types added (or were already present)
✓ config.json: _hermit_versions stamped, {K}/4 routines added, weekly-coaching-patterns check registered
Manual steps remaining:
- Restart Claude Code so the `strava` MCP server loads from .mcp.json
- Approve the `strava` server when prompted on first use
- Run /mcp to confirm `strava` is connected
- Verify connectivity: call mcp__strava__check-strava-connection
Go always-on (recommended):
- Docker: /claude-code-hermit:docker-setup
Builds the container and walks you through channel pairing in one go.
- Bare tmux: .claude-code-hermit/bin/hermit-start
For channels (Discord/Telegram) with tmux, run
/claude-code-hermit:channel-setup first.
Prefer to test interactively first? After restarting, run:
/claude-code-hermit:hermit-routines load
— activates the four fitness routines in the current Claude session.
The always-on runtime activates routines automatically — the interactive
steps are only for a test drive before handing over to the runtime.
Installed skills:
/claude-code-fitness-hermit:fitness-brief — daily morning/evening brief (--morning|--evening|--slot)
/claude-code-fitness-hermit:activity-deep-dive — per-activity coaching analysis
/claude-code-fitness-hermit:weekly-coaching-patterns — weekly cardiac-drift trend check (scheduled, interval_days: 7)
Installed subagent:
@claude-code-fitness-hermit:strava-data-cruncher — bulk Strava data aggregation (Haiku)
Security reminder: .env and .mcp.json contain real Strava credentials. Both are gitignored — verify before any git push.
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.
server=/strava.com/... matches both www.strava.com (web) and api.strava.com (API) via dnsmasq's subdomain-match semantics. The Strava MCP server (@r-huijts/strava-mcp-server via npx) also needs npmjs.org, but that's already in the wizard's seed allowlist for npm/plugin installs.
(none — Strava is a cloud service)
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.