Files
mop-skills/skills/zwift-workout/SKILL.md
T
IT Guy 109162889c feat(skills): add zwift-workout skill (.zwo builder, validator, preview, publishing)
Bike-only Zwift .zwo generator with human-readable preview + confirmation loop.

- scripts/zwo_builder.py: spec<->.zwo build/validate/parse/render/TSS estimate
- scripts/zwo.py: CLI (build, from-template, validate, parse, render, templates)
- references/zwo-format.md: condensed offline format reference
- assets/example-vo2max.zwo + tests/test_zwo.py (9 tests)
- Publishes via hosting skill; files result into Knowledge Wiki

Format ground truth: h4l/zwift-workout-file-reference
2026-09-20 07:47:05 +00:00

4.9 KiB
Raw Blame History

name, description
name description
zwift-workout Create, preview, edit, validate and publish Zwift .zwo workout files for cycling (bike-only). Use when the user asks to build a Zwift workout, generate a .zwo file, convert a training idea into a Zwift session, preview/adjust an existing workout, or publish a workout for import into Zwift.

🚴 Zwift Workout (.zwo) Builder

Builds valid Zwift .zwo files from a small JSON spec, renders a human-readable preview for confirmation, and publishes the finished file. Bike-only.

When to use

  • "Bau mir ein Zwift-Workout …" / "Create a .zwo for 5×3min VO2max"
  • "Zeig mir das Workout" / "änder die Intervalle auf 4×5min"
  • "Lade das Workout in Zwift" (publish to hosting for import)

Core workflow (always follow this order)

  1. Draft the spec. Either pick a deterministic template (zwo.py templates → endurance | sweetspot | vo2max | sprint) or write a JSON spec from the user's request. Keep power as FTP fractions (0.88 = 88 %, 1.15 = 115 %); durations in seconds.
  2. Render the preview and ask for confirmation. Run python3 {baseDir}/scripts/zwo.py render <spec.json> (or build … --preview) and show the table to the user. Do not publish before the user confirms.
  3. Apply changes. Edit the JSON spec and re-render; repeat until confirmed.
  4. Build + publish on confirmation. Run python3 {baseDir}/scripts/zwo.py build <spec.json> -o <name>.zwo --publish. This validates, writes the file, and uploads it via the hosting skill (https://hosting.haumdaucher.de/docs/…).
  5. Deliver. Post the hosting link (and the .zwo as MEDIA:<path> if the user wants the file directly). Optionally offer a QR code (hosting skill rule).
  6. File the result into the wiki under syntheses/ or concepts/ per the Knowledge Wiki protocol (see below).

JSON spec format

{
  "name": "VO2 Max 5x3",
  "author": "IT Guy",
  "description": "5 × 3 min @ 115 % FTP, 3 min Pause.",
  "sportType": "bike",
  "tags": ["vo2max", "structured"],
  "steps": [
    {"type": "Warmup",     "duration": 600, "powerLow": 0.50, "powerHigh": 0.75},
    {"type": "IntervalsT", "repeat": 5, "onDuration": 180, "offDuration": 180,
                           "onPower": 1.15, "offPower": 0.55},
    {"type": "Cooldown",   "duration": 300, "powerLow": 0.75, "powerHigh": 0.40}
  ]
}

Supported step types (bike)

type Required fields Optional
Warmup duration, powerLow, powerHigh cadence
Cooldown duration, powerLow, powerHigh cadence
SteadyState duration, power cadence
Ramp duration, powerLow, powerHigh —
IntervalsT repeat, onDuration, offDuration, onPower, offPower cadence, cadenceResting
FreeRide duration flatRoad
MaxEffort duration —
SolidState duration, power —
RestDay — —

Power values are fractions of FTP; duration is in seconds. See references/zwo-format.md for the full tag/attribute reference.

CLI

python3 {baseDir}/scripts/zwo.py templates                       # list templates
python3 {baseDir}/scripts/zwo.py from-template vo2max --title "VO2 Max 5x3" --publish
python3 {baseDir}/scripts/zwo.py render  my.json                 # preview only
python3 {baseDir}/scripts/zwo.py build   my.json -o out.zwo --preview --publish
python3 {baseDir}/scripts/zwo.py validate existing.zwo           # check a file
python3 {baseDir}/scripts/zwo.py parse   existing.zwo --json     # .zwo -> spec (editable)

Guardrails

  • Power range: 0.20 – 1.50 FTP. Outside → hard error (catches unit mistakes).
  • Durations: integer seconds, > 0; < 5 s and > 4 h produce warnings.
  • Structure: exactly one <workout>, root <workout_file>, name required, sportType must be bike.
  • Round-trip check: output XML is re-parsed with xml.etree before writing; text fields are escaped, so umlauts/quotes never corrupt the file.
  • Never publish without explicit user confirmation of the rendered preview.

Reference & fixtures

  • references/zwo-format.md — condensed tag/attribute reference (offline copy).
  • assets/example-vo2max.zwo — known-good fixture; use for regression tests.

Knowledge Wiki filing

After a workout is confirmed and published, file it per the Karpathy LLM Wiki protocol: create/update a concepts/ or syntheses/ page recording the workout name, structure, power targets, hosting URL, and rationale, then append a log.md entry (ingest/synthesis). Cross-link the Fitness agent page.

Inter-agent synergy

agents/fitness.md owns training intent (Tacx Flux 2, FTP 275 W). This skill is the execution layer for structured cycling sessions. For evidence-based protocols (e.g. SIT 30 s / 4 min), consult the Knowledge Wiki first via wiki_search. A future refactor can share one spec module with garmin-workout (one source spec → Garmin workout + .zwo).