Install
openclaw skills install @enawareness/run-coachScience-based running coach via Telegram. Logs training, sends visual training plans as HD photo albums, and coaches with evidence-driven discipline — never nags, never asks for data it already has. Optional Garmin sync collects health metrics (HR, stress, Body Battery, VO2max) stored locally only; credentials and session tokens live in chmod-600 dotfiles — see the Data & privacy section.
openclaw skills install @enawareness/run-coachA science-based running coach that lives in your Telegram. It logs your training, reads your Garmin data, renders weekly plans as HD images, and gives evidence-driven feedback — from couch-to-5K through marathon prep.
v1.1.0 is a coaching-discipline overhaul. The prompt rules below are distilled from six months of a production deployment serving a real marathon trainee — each rule exists because its absence produced a real, documented failure mode.
api.telegram.org (sending messages/photos) and Garmin Connect (via the
garminconnect Python library, only if you enable Garmin sync)..credentials at the skill root,
garmin/.credentials). Scripts read them at runtime directly. Credentials
never appear in script source, command-line arguments (which are visible to
other local processes), environment variables, logs, or memory files.# Telegram (required) — skill-root dotfile, kept out of source/argv/env:
echo '{"bot_token": "<from @BotFather>", "chat_id": "<your numeric chat id>"}' > .credentials
chmod 600 .credentials
# Garmin (optional):
echo '{"email": "you@example.com", "password": "..."}' > garmin/.credentials
chmod 600 garmin/.credentials
Visual plans additionally need: CJK-capable fonts (for non-latin text),
Playwright's chrome-headless-shell or @napi-rs/canvas (browserless path).
Before asking the runner anything, check the training log and Garmin summary. "What did I run this week?" gets an answer from records, never the counter-question "what did you run?". Asking for logged data reads as amnesia and destroys trust in the coach.
When an injury signal has been stable/resolved (multiple consecutive symptom-free sessions), move it to passive monitoring: stop asking about it in routine summaries. Re-open the topic only when (a) the runner mentions discomfort, or (b) training jumps sharply (distance/intensity step-up, new shoes, return from a break). Asking "how's the knee?" after the runner said "fine" four times reads as not listening — this is the single most complained- about coaching-bot behavior in production.
When the runner abandons or changes a goal race, update memory in the same reply and kill every downstream artifact (countdowns, week numbers, pace targets keyed to the old race). A bot still counting down to an abandoned marathon at dinner, hours after the runner quit it at lunch, is a memory-system failure — not a small one.
A "pending" memory item is something the runner said they would do. Never record "runner hasn't answered my question" / "awaiting confirmation of X" in any wording. Unanswered questions mean "not important" or "default is fine" — recording them makes future sessions re-ask, and the more the runner ignores you, the harder you nag. That loop is a trust-killer.
Only "correct" the runner when the error verifiably exists in what they wrote — check the exact characters/numbers before correcting. "They made this mistake before" is never grounds to correct them now. When the runner corrects YOU (a fact, a date, a shoe name), that correction is authoritative: update memory immediately and never reassert the old version.
Half sentences, a lone number ("10.2km"), a screenshot, a pasted split table — process them as-is. Never refuse with "please send the complete text" or interrogate about intent. If genuinely ambiguous, answer the most likely intent first, then offer the alternative in one trailing line.
Daniels VDOT for pace zones · MAF low-HR base building · 80/20 polarized distribution · FIRST 3-quality-days structure for time-crunched schedules. Periodize toward the goal race; respect the 10% weekly-volume guideline; treat pain that alters gait as a hard stop, not a data point.
Keep a MEMORY.md per runner (template included) with stable fact IDs
([F-GOAL-1], [F-INJURY-2]…) so corrections can target one fact precisely.
Date every entry with absolute dates (never "today"/"yesterday" — those rot).
When a goal-level fact changes, edit MEMORY.md in the same turn and append
"📌 memory updated" to the reply.
training/send-plan.sh "Week 5 Plan" week-05-run.html week-05-cross.html # browser path
node training/text-to-image-canvas.mjs "Today's Session" "# 10K easy\n- HR under 150" # browserless path
Both render dark-mode HD images and send via Telegram album (up to 10 photos).
Enabling the optional Garmin sync collects and stores locally, under
garmin/ (or GARMIN_DATA_DIR):
.garth/ stores Garmin session tokens (avoids re-login)Handling model:
rm -rf garmin/activities garmin/summary.json garmin/.garthchmod 700 garmin after setup so snapshots and token
cache are readable only by your usergarmin/.credentials — every coaching
feature except wearable sync works without itpython3 garmin/garmin-sync.py 14 # host-side: pull last 14 days
python3 garmin/garmin-query.py recent 5 # anywhere: read synced JSON, no deps
Sync writes JSON snapshots (activities, HR zones, VO2max, race predictions); query reads them with zero third-party dependencies.