Skip to content

Repository files navigation

Cycling Fitness Coach

A Claude Code skill that turns Claude into a data-driven cycling coach. It connects to intervals.icu for real training data, generates Zwift workouts, builds periodized training plans, and provides weekly adaptive reviews — all through natural conversation.

What It Does

Capability Description
Ride Analysis Fetch activity data from intervals.icu and get coaching feedback with execution ratings, interval review, and next-session recommendations
Weekly Summary Aggregate the past 7 days — total TSS, zone distribution, power profile, FTP detection
Training Plans Multi-week periodized plans with PMC tracking (CTL/ATL/TSB/ACWR), progressive overload, and block periodization
Weekly Reviews Compare planned vs actual training load, apply adaptation decision trees, adjust the next week
Zwift Workouts Generate .zwo workout files with structured warm-up, intervals, and cool-down — single or batch
Training Advice Zone/FTP questions, race peaking & taper protocols, mid-week check-ins
Power Profiling Coggan-based W/kg classification (sprinter, pursuiter, time trialist, all-rounder) with strength/weakness analysis

Prerequisites

pip install requests

fitparse is an optional dependency — install it only if you need the .fit file fallback path (scripts/fit_ingest.py): pip install fitparse.

Installation

1. Clone the repo

git clone https://github.com/chuazj/cycling-fitness-coach.git

2. Install as a Claude Code skill

Copy or symlink into your Claude Code skills directory:

# macOS / Linux
cp -r cycling-fitness-coach ~/.claude/skills/cycling-fitness-coach

# Windows (PowerShell)
Copy-Item -Recurse cycling-fitness-coach "$env:USERPROFILE\.claude\skills\cycling-fitness-coach"

3. Configure intervals.icu credentials

Create a .env file in the skill root (the directory containing SKILL.md):

INTERVALS_ICU_ATHLETE_ID=i12345
INTERVALS_ICU_API_KEY=your_api_key_here

Get your API key from intervals.icu Settings (permanent key, no refresh needed).

Usage

Once installed, Claude Code automatically activates the skill when you mention cycling topics. Just talk to it naturally:

Analyze a ride

Analyze my workout: https://intervals.icu/activities/i126468486

or just paste an activity ID:

How did my ride go? i126468486

Claude fetches the data via API, computes NP/IF/TSS/zones/peaks, and returns coaching analysis with an execution rating and next-session recommendation.

Weekly summary

How was my training week?

Aggregates the last 7 days: total TSS, zone distribution, power profile, and auto-FTP detection.

Create a training plan

Build me a 6-week training plan to improve my FTP

Claude bootstraps your PMC from 90-day history, selects the appropriate periodization block, designs week-by-week TSS progression, and generates Zwift workouts for Week 1.

Weekly check-in

Review my week — how did I do?

Compares planned vs actual training load, checks CTL/ATL/TSB trends, applies adaptation decision trees, and adjusts the upcoming week.

Generate a Zwift workout

Create a sweet spot workout, 60 minutes

Generates a .zwo file with structured warm-up, main set, and cool-down. Power targets are expressed as FTP fractions for automatic scaling in Zwift.

Training advice

I have a race in 3 weeks — help me peak
What zone should I focus on if I plateau at sweet spot?
My RPE was 9 but IF was only 0.72 — what's going on?

Architecture

SKILL.md                        <- Skill entry point (triggers, coaching rules, workflow dispatch)
workflows/
  analyze.md                    <- Activity analysis + weekly summary
  plan.md                       <- Plan creation + weekly review
  generate.md                   <- Zwift workout generation
  advise.md                     <- Training advice + mid-week check-in + race peaking
scripts/
  intervals_icu_api.py          <- Thin re-export façade + CLI entry point (preserves `from intervals_icu_api import …` surface)
  intervals_icu/                <- intervals.icu API client package (split from former monolith)
    api_client.py               <- HTTP client, auth, .env loading
    metrics.py                  <- Pure metric computation (NP, IF, TSS, zones, peaks, cardiac drift, power profile)
    activity.py                 <- Single-activity analysis + weekly training summary
    wellness.py                 <- WHOOP wellness fields, baselines, Yellow/Red flag detection
    readiness.py                <- Pre-ride readiness verdict engine
    cli.py                      <- argparse definition + mode dispatch
  generate_zwo.py               <- Zwift .zwo XML generator
  pmc_calculator.py             <- PMC bootstrap (90-day history) + weekly update (planned vs actual)
  batch_generate_zwo.py         <- Batch .zwo generation from JSON array
  sparkline.py                  <- Pure-Python ASCII sparkline for Peak Power Trends in plans/active_plan.md
  rpe_trend.py                  <- RPE trend aggregator (Obsidian frontmatter scan + functional-overreaching detection)
  fit_ingest.py                 <- .fit file fallback: parses local ride files when activity is not on intervals.icu
  zwo_lint.py                   <- Linter for existing .zwo files (hygiene rules, ERG design, modeled TSS)
  prediction_tracker.py         <- W5 validation loop: predict/reconcile/seed forecasts, recalibration triggers
references/
  training_zones.md             <- Power/HR zone definitions, weekly structure
  workout_analysis.md           <- Analysis framework, coaching response templates
  zwo_format.md                 <- Zwift XML element spec, ERG design constraints, and examples
  intervals_icu_api.md          <- intervals.icu API endpoints, data models, and dependency notes
  block_templates.md            <- Block templates, TSS distribution, progressive overload, FTP test protocols, Block Selection Logic
  weekly_adaptation.md          <- Weekly adaptation decision trees (load, ACWR, TSB, RPE, illness/injury)
  race_taper.md                 <- Race / event peaking — taper structures and TSB projection
  durability_strength.md        <- Concurrent strength training, heat adaptation, durability concept
  fueling.md                    <- Pre/during/post-ride nutrition, carb targets, GI troubleshooting
  adaptation_rules.md           <- Per-activity forward-cascade adaptation rules (signals -> severity -> next-session)
  plan_state_schema.md          <- Structure spec for active training plan state
  obsidian_templates.md         <- Frontmatter templates for workout reviews, plans, weekly reviews
  menstrual_cycle_training.md   <- Hormonal-cycle autoregulation protocol for female athletes
  rule_registry.md              <- Orphan-prevention catalogue of standing coaching rules (W4)
  prediction_calibration.md     <- W5 predict→measure→calibrate loop: models, ledger, recalibration triggers
plans/
  active_plan.md                <- Active training plan (generated by Create Plan workflow; gitignored — local-only)
  block_history.md              <- Archive of completed blocks (gitignored — local-only)
tests/                          <- Unit tests (pure functions, mocks, CLI, PMC integration, fit ingest, linter, predictions)
assets/
  template_sweetspot.zwo        <- Example Zwift workout XML

Data Flows

Activity analysis: intervals.icu link -> intervals_icu_api.py fetches activity/intervals/streams/power-curve -> computes metrics -> JSON output -> Claude provides coaching analysis

Plan creation: pmc_calculator.py --bootstrap -> PMC baseline -> Claude designs periodized block -> writes plans/active_plan.md -> batch_generate_zwo.py generates week's .zwo files

Weekly review: pmc_calculator.py --weekly-update -> planned vs actual comparison -> Claude applies adaptation rules -> updates plan -> generates next week's workouts

Scripts Reference

intervals_icu_api.py

# Analyze a single activity
python scripts/intervals_icu_api.py --activity i126468486 --ftp 200 --weight 70

# Auto-fetch FTP/weight from athlete profile
python scripts/intervals_icu_api.py --activity i126468486 --use-athlete-profile

# List recent activities
python scripts/intervals_icu_api.py --list-recent 10

# Weekly summary (last 7 days)
python scripts/intervals_icu_api.py --weekly-summary -o summary.json

# Compact output (fewer tokens for LLM consumption)
python scripts/intervals_icu_api.py --activity i126468486 --compact

pmc_calculator.py

# Bootstrap: 90-day history with current CTL/ATL/TSB + peak powers
python scripts/pmc_calculator.py --bootstrap --days 90

# Weekly update: compare planned vs actual
python scripts/pmc_calculator.py --weekly-update \
  --week 1 --plan-start 2025-03-16 \
  --prev-ctl 42.3 --prev-atl 51.2 \
  --planned-tss '{"Tue":65,"Thu":70,"Sat":80,"Flex":55}'

generate_zwo.py

# Generate a single Zwift workout from JSON definition
python scripts/generate_zwo.py --json workout_def.json --output workout.zwo --ftp 200

batch_generate_zwo.py

# Generate all .zwo files for a training week.
# --output-dir should be your Zwift custom workouts folder so the files show up in-game:
#   Windows:       %LOCALAPPDATA%\Zwift\Workouts\<athlete_id>\week1\
#   macOS/Linux:   ~/Documents/Zwift/Workouts/<athlete_id>/week1/
python scripts/batch_generate_zwo.py --input week_workouts.json --output-dir "<ZWIFT_WORKOUTS_DIR>/week1/" --ftp 200

# Dry run (validate + compute stats without writing files — any --output-dir is fine here)
python scripts/batch_generate_zwo.py --input week_workouts.json --dry-run --ftp 200

fit_ingest.py

Fallback for rides that did not sync to intervals.icu (e.g., Strava-only or local .fit files). Emits the same analysis JSON as the API path.

# Requires: pip install fitparse
python scripts/fit_ingest.py --file <ride.fit> --ftp 188 --weight 74 -o output.json

zwo_lint.py

Validates an existing .zwo file against the canonical element reference and hygiene rules (ERG design, textevent placement, power range, ramp direction). Reports modeled NP-based TSS.

python scripts/zwo_lint.py path/to/workout.zwo --ftp 188
# Exit code: 0 = clean, 1 = errors, 2 = unreadable

prediction_tracker.py

W5 validation loop: log forecasts before a session, reconcile against actuals after, and surface recalibration triggers when the model drifts.

# Seed baseline from existing Obsidian reviews (one-time)
python scripts/prediction_tracker.py --mode seed-baseline --vault-path "<CYCLING_VAULT_PATH>/workout-reviews"

# Log a forecast before a session
python scripts/prediction_tracker.py --mode predict --type rpe_at_if --if 0.84 --slot morning --session-date 2026-06-02 --session-type Threshold

# Reconcile open predictions against actuals
python scripts/prediction_tracker.py --mode reconcile --vault-path "<CYCLING_VAULT_PATH>/workout-reviews" -o prediction_report.json

Training Methodology

The coaching approach is grounded in established sport science:

  • Power zones: 7-zone model based on FTP (Coggan)
  • Sweet spot training: 88-94% FTP — high stimulus, manageable fatigue, optimal for time-crunched athletes
  • Periodization: Block periodization with base, build, peak, and recovery phases
  • Load management: PMC-based tracking with CTL (fitness), ATL (fatigue), TSB (form), and ACWR (acute:chronic workload ratio)
  • Adaptation: Decision trees for weekly plan adjustments based on compliance, RPE:IF mismatch, and TSB trends
  • Race peaking: Taper protocols with progressive volume reduction while maintaining intensity

Coaching Process

The skill follows strict coaching process rules:

  1. Validate before prescribing — presents an assessment of the athlete's current state and waits for confirmation before making recommendations
  2. Establish zones first — never prescribes zone-specific workouts without confirmed FTP; recommends field tests for unvalidated zones
  3. Explain the "why" — every prescription includes the physiological purpose and how it connects to the athlete's goal
  4. Adaptation requires approval — proposes changes based on data but waits for athlete confirmation before modifying the plan

Optional Integrations

Integration Purpose
Obsidian Persistent storage for workout analyses, plans, and weekly reviews with frontmatter metadata
Zwift Load generated .zwo files into custom workouts

Running Tests

python -m unittest discover tests -v

691 tests across 8 files: pure function unit tests, mocked HTTP responses, CLI argument parsing, PMC integration, .fit file ingestion, ZWO linting, and prediction tracker tests. Expected runtime ~0.1s.

Customization

To adapt this skill for your own use:

  1. FTP/Weight: Run scripts with --use-athlete-profile to auto-fetch from your intervals.icu profile, or set values in plans/active_plan.md → Athlete Profile after the Create Plan workflow runs (this file is gitignored — your data stays local)
  2. Training days: Auto-detected from your recent activity pattern; can be overridden during plan creation
  3. Obsidian vault: Set the CYCLING_VAULT_PATH environment variable (see references/setup.md → Obsidian Integration)
  4. Zwift workout folder: Set the ZWIFT_WORKOUT_DIR environment variable (see references/setup.md → Zwift Workout Directory)

License

MIT

About

Claude Code skill for cycling coaching: intervals.icu analysis, Zwift workout generation, PMC tracking, and periodized training plans

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages