A GitOps-driven, data-centric training system for endurance athletes. Integrates Intervals.icu, wearable data, and AI to plan, evaluate, and adapt training load, readiness, and long-term form.
Find a file
ladislavb fb8a996239
All checks were successful
ci/woodpecker/push/daily-coach Pipeline was successful
ci/woodpecker/cron/daily-coach Pipeline was successful
Fix language key in system settings from 'language' to 'lang'
2026-02-11 18:51:57 +01:00
.woodpecker Fix path to daily.sh script in daily-coach.yml 2026-01-26 09:51:54 +01:00
app Fix language key in system settings from 'language' to 'lang' 2026-02-11 18:51:57 +01:00
athletes training summary 2026-02-09 18:17:23 +01:00
docs Moving AGENTS.md back to root 2026-02-09 22:10:12 +01:00
goals reset history 2026-01-25 19:58:28 +01:00
timeoff/chucknorris Major update / refactor 2026-02-03 10:32:29 +01:00
.gitignore Major update / refactor 2026-02-03 10:32:29 +01:00
.sops.yaml SOPS related fixes 2026-01-26 09:36:34 +01:00
AGENTS.md Moving AGENTS.md back to root 2026-02-09 22:10:12 +01:00
daily.sh reorganizing file structure 2026-02-09 23:03:24 +01:00
README.md Documentation moved to docs folder 2026-02-09 18:40:32 +01:00

TrainOps (PowerShell + Intervals.icu + OpenAI)

This repository contains a deterministic fitness automation pipeline for endurance training workflows.

  • Supports multiple athletes
  • Uses Intervals.icu for activities, wellness, and events
  • Produces deterministic JSON + Markdown outputs for planning and review
  • Optional AI tasks driven by prepared payloads
  • Optional publishing to calendars/email (see PUBLISHING.md)

Quick start (local)

Prereqs:

  • PowerShell 7 (pwsh)
  • Internet access to Intervals.icu and OpenAI API (if AI enabled)
  • API secrets as env vars:

Install module:

pwsh -NoProfile -Command 'Install-Module powershell-yaml -Scope CurrentUser -Force'

Configuration

  • Global defaults live in app/settings.yml at the repo root.
  • Athlete-specific YAML files under athletes/ override only the fields they need, and Settings.ps1 merges them with the shared defaults.
  • The merge preserves the original key order so the resulting settings.debug.yml snapshot mirrors your hand-edited files. Enable it by adding system_settings.debug: true to an athlete profile and inspect the dump under out/<athlete>/00_raw_data/settings.debug.yml.
  • Shared provider defaults (Intervals IDs, OpenAI models, etc.) live under a top-level providers block.

Example snippet:

providers:
  intervals:
    athlete_id: "icuid"
  openai:
    default_model: "gpt-4.1-mini"

Logging

  • All logs are written to a single shared folder: app/logs at the repo root.
  • Each log line includes the athlete id: [athlete=<id>].
  • Log files are date-stamped (trainops-YYYY-MM-DD.log).

Protecting athlete configs with SOPS

  • Install SOPS and generate/store an age key (e.g. age-keygen -o ~/.config/sops/age/keys.txt). Export SOPS_AGE_KEY_FILE (or SOPS_AGE_KEY) so both local runs and CI can decrypt data.
  • .sops.yaml enforces encryption for athletes/*.yml, goals/<athlete>/*.yml, health/<athlete>/*.yml (time-off events live under health/). Replace the age public key with your own.
  • Use the built-in editor to work with encrypted files: sops athletes/lada.yml. When you save/exit, the ciphertext is updated in place and safe to commit.
  • The helper Load-YamlFile automatically detects SOPS-encrypted files and shells out to sops -d at runtime, so pipelines always see plaintext while the repository keeps ciphertext only.
  • CI runners only need the private key exposed as a secret env var; no code changes are required elsewhere.

Optional: Local SOPS vault for API keys

  • Store user-provided API keys in an encrypted .env-style vault (secrets/vault.env by default, ignored by git). Override the path via TRAINOPS_VAULT_PATH if you want to keep the file elsewhere.
  • Run daily.sh instead of calling the PowerShell script directly. The wrapper decrypts the vault via SOPS, sources it with set -a, and then launches app/scripts/daily.ps1 with the original arguments.
  • Example vault contents:
export INTERVALS_API_KEY_CHUCKNORRIS="..."
export OPENAI_API_KEY_CHUCKNORRIS="..."
  • Edit with sops secrets/vault.env (or your custom path) and keep the age key private; the file never touches git thanks to the .gitignore entry.

How it works (high level)

  1. Import raw data (activities, wellness, events) from Intervals.icu
  2. Normalize data into stable schemas
  3. Build daily state payloads and summaries
  4. (Optional) Run AI tasks using prepared payloads
  5. (Optional) Publish outputs (calendar/email/files)

Pipeline docs

  • DATA_IMPORT.md
  • NORMALIZATION.md
  • DAILY_STATE.md
  • PLANNER.md
  • SUMMARIZATION.md
  • AI.md
  • PUBLISHING.md

Daily State V2 payloads

For the exact JSON structure, calculations, and coefficients used by planner_input.json and goal_readiness_report.json, see DAILY_STATE.md.

Woodpecker

See woodpecker.yml. Create a cron named daily-coach in Woodpecker UI.