Local Strava analytics CLI with native support for LLM operations
Project description
FitOps
Your fitness data is yours. FitOps gives you two first-class ways to work with it: a local dashboard for human exploration and a structured CLI for AI agents — both reading from the same SQLite database on your machine.
You (human) → Dashboard (browser) ─┐
├─ ~/.fitops/fitops.db
AI Agent → CLI (Rich + JSON) ─┘
Sync your Strava activities once, then explore them however you like — visually in the browser or programmatically through an agent. No cloud, no subscriptions. Your data never leaves your machine.
Current data source: Strava (via OAuth sync). Direct file import (GPX / TCX / FIT) and native integrations with Garmin, Coros, Apple Health, and Huawei are planned — see the Roadmap.
Why FitOps?
- For humans: A local dashboard with charts, period filters, and training analytics at
http://localhost:8888— activities, analytics, workouts, notes, weather, and race simulation. - For agents: Every data view is also a CLI command. Commands output Rich formatted tables for quick human scanning, or structured JSON with
_metacontext blocks anddata_availabilityhints so an LLM always knows what to fetch next. JSON output is token-optimized — 21.9% fewer tokens vs v0.1.0 (~56 tokens saved per activity). Training notes are Markdown files an agent can read and write — persistent, tagged, activity-linked memory that survives across sessions. - Same data, always. The dashboard and the CLI are two views into the same truth. If you can see it in the browser, an agent can query it from the terminal.
- Everything is local. Your data lives in
~/.fitops/fitops.db. No cloud, no subscriptions. - Strava sync today, more providers coming. Authenticate once with Strava and run
fitops sync runfor fresh data. Direct GPX / TCX / FIT file import and native Garmin, Coros, Apple Health, and Huawei sync are on the roadmap. - Async-first. Built on
httpxandaiosqlitefor non-blocking I/O throughout.
How FitOps Compares
✅ Supported · ⚡ Partial / limited · ❌ Not available · 🔜 Planned
Based on real user experiences from r/running, r/cycling, r/triathlon, the Intervals.icu forum, TrainerRoad, and Slowtwitch. See docs/comparison.md for the full breakdown with sources.
| FitOps | TrainingPeaks | Intervals.icu | Strava | Garmin Connect | |
|---|---|---|---|---|---|
| Price | Free | ~$135/yr | Free | ~$132/yr | Free (device req.) |
| Open source | ✅ | ❌ | ❌ | ❌ | ❌ |
| Local / offline | ✅ | ❌ | ❌ | ❌ | ⚡ device only |
| Data ownership | ✅ Your machine | ❌ | ❌ | ❌ Sold to 3rd parties | ⚡ |
| Open API / scripting | ✅ Native CLI | ❌ | ✅ documented | ✅ rate-limited | ❌ $5K fee |
| LLM / AI agent native | ✅ Designed for it | ❌ | ⚡ via API | ❌ | ❌ |
| Training load (CTL/ATL/TSB) | ✅ | ✅ | ✅ | ⚡ premium | ⚡ own model |
| VO2max estimation | ✅ 3-formula composite | ❌ | ✅ | ⚡ premium | ✅ on-device |
| HR + pace zones | ✅ LTHR/MaxHR/HRR | ✅ | ✅ | ⚡ premium | ✅ |
| Workout compliance scoring | ✅ per-segment | ✅ premium | ✅ | ❌ | ✅ on-device |
| Race simulation (per-km splits) | ✅ full engine | ⚡ pace calc | ❌ | ❌ | ❌ |
| Weather-adjusted pace (WAP) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Workout simulation on course | ✅ | ❌ | ❌ | ❌ | ❌ |
| Training calendar / planner | ❌ | ✅ | ✅ | ❌ | ✅ |
| Coach–athlete platform | ✅ AI / agent coach | ✅ human coach | ✅ human coach | ❌ | ❌ |
| Mobile app | ❌ | ✅ | ❌ | ✅ | ✅ |
| Social / segments / community | ❌ | ❌ | ❌ | ✅ core feature | ⚡ |
| Device integrations | 1 (5+ 🔜) | 80+ | 15+ | 10+ | Garmin only |
| HRV / sleep / body battery | ❌ | ❌ | ❌ | ❌ | ✅ |
What the community says: TrainingPeaks raised prices to ~$135/yr in April 2025 and is widely described as stagnating — the most common reason users cite for switching to Intervals.icu, which covers the same analytics for free. Strava has faced repeated privacy controversies including selling user location data to governments and a 2025 incident where it published private coaching notes without consent. Garmin Connect's API requires a $5,000 fee and is effectively closed to personal use.
FitOps unique capabilities not found in any alternative:
- Weather-Adjusted Pace (WAP) — per-activity historical weather from Open-Meteo (no API key), WBGT heat stress model, Pugh 1971 wind drag model. Makes a hot humid run directly comparable to a cool calm one.
- True Pace — single effort-normalized metric combining grade (GAP) and weather (WAP) adjustments, enabling cross-season VO2max trending without weather noise.
- Race simulation engine — per-km split plan adjusted for elevation, temperature, humidity, and wind per segment. Supports pacer strategy (sit-then-push) and forecast weather for race day.
- Workout simulation on course — simulate how a structured workout plays out on a GPX course with terrain and weather adjustments per segment.
- LLM-native output — every CLI command returns structured JSON with
_metablocks, explicit units in field names, anddata_availabilityhints so any AI agent can chain commands without parsing hacks. - Persistent agent memory — training notes are Markdown files in
~/.fitops/notes/, tagged and optionally linked to activities. An AI agent can write observations, flag patterns, and read them back in future sessions — durable memory that outlives any single conversation.
Roadmap
| Phase | Status | Description |
|---|---|---|
| 1 — Foundation | ✅ Done | Auth, sync, activities, athlete profile |
| 2 — Analytics | ✅ Done | CTL/ATL/TSB, VO2max, LT1/LT2 thresholds, dashboard |
| 3 — Workouts | ✅ Done | Workout plans, compliance scoring, simulation |
| 4 — Multi-Provider | 🔜 Planned | GPX / TCX / FIT file import; Garmin, Coros, Apple Health, Huawei native sync |
| 5 — Cloud Backup | 🔜 Planned | Google Drive, OneDrive, Dropbox, Mega |
| 6 — Notes | ✅ Done | Markdown training notes with tags and activity linking |
| 7 — Weather-Adjusted Pace | ✅ Done | WAP, True Pace, Open-Meteo historical/forecast weather |
| 8 — Race Simulation | ✅ Done | Course import, per-split pacing, pacer strategy |
Quick Setup for AI Agents
One command installs FitOps and places the skill in the right directory for your agent.
curl -fsSL https://raw.githubusercontent.com/brunov21/fitops/main/install.sh | bash
The script auto-detects Claude Code, Cursor, Codex, Windsurf, Cline, OpenCode, and GitHub Copilot. To target a specific agent:
AGENT=cursor bash <(curl -fsSL https://raw.githubusercontent.com/brunov21/fitops/main/install.sh)
AGENT=codex bash <(curl -fsSL https://raw.githubusercontent.com/brunov21/fitops/main/install.sh)
AGENT=claude bash <(curl -fsSL https://raw.githubusercontent.com/brunov21/fitops/main/install.sh)
What the script does:
- Checks for
uvx→ installs viauvx fitops(isolated, no global install) - Falls back to
pip install fitopsif Python 3.11+ is available - Errors with instructions if neither is found
- Downloads
.claude/commands/fitops.mdfrom this repo into every detected agent directory - Prints the Strava auth steps so you're never left guessing
After installation:
fitops auth login # Enter your Strava Client ID + Secret when prompted
fitops sync run # Pull your activity history
Need a Strava API key? Go to strava.com/settings/api and follow the full authentication guide.
Then invoke the skill from Claude Code with /fitops <your question> — it has the full command reference, error recovery table, and coaching workflows built in.
The skill uses fitops notes as persistent memory: observations written in one session are readable in the next via fitops notes list --tag <tag>.
Installation
Requirements: Python 3.11+ (or uv — manages Python for you)
Recommended — one-line installer (CLI + skill)
curl -fsSL https://raw.githubusercontent.com/brunov21/fitops/main/install.sh | bash
Installs FitOps and places the agent skill file for every detected coding assistant. See Quick Setup for AI Agents above.
Run without installing — uvx
uvx fitops auth login
uvx fitops sync run
uvx fitops dashboard serve
uvx runs FitOps in an isolated environment. Install uv once with curl -LsSf https://astral.sh/uv/install.sh | sh.
Install from PyPI
pip install fitops
Install from source
git clone https://github.com/brunov21/fitops.git
cd fitops
pip install -e .
Quick Start
Start the Dashboard
fitops dashboard serve
# → Opens http://localhost:8888
The dashboard covers: activity history, analytics (training load, VO2max, trends, performance), workouts, training notes, weather conditions, and race course simulation.
1. Create a Strava API Application
To ensure the best experience and bypass rate limits, beta users must use their own Strava API credentials. Follow the Strava Getting Started Guide to create an app.
Go to https://www.strava.com/settings/api and use these exact settings:
| Field | Value |
|---|---|
| Application Name | Surge |
| Category | Performance Analysis |
| Website | https://brunov21.github.io/Surge/ |
| Authorization Callback Domain | mclovinittt-kinetic-run-api.hf.space |
Copy your Client ID and Client Secret from the Strava API Settings page. See the full authentication guide for details.
2. Authenticate
fitops auth login
# → Prompts for Client ID and Secret on first run
# → Opens your browser to Strava OAuth
# → Captures callback on localhost:8080
# → Saves tokens to ~/.fitops/config.json
3. Sync Activities
fitops sync run # Incremental sync (from last sync date)
fitops sync run --full # Full historical sync (all time)
fitops sync streams # Backfill GPS/HR streams for all activities
fitops sync status # Show sync state
4. Query Your Data
# List recent activities (Rich table)
fitops activities list
fitops activities list --sport Run --limit 50 --after 2026-01-01
# Full detail for one activity (JSON)
fitops activities get 12345678901
# Stream data — HR, pace, power over time (JSON)
fitops activities streams 12345678901
# Athlete profile and physiology settings
fitops athlete profile
fitops athlete stats
fitops athlete zones
fitops athlete set --ftp 280 --max-hr 185
Commands Reference
fitops auth — Authentication
fitops auth login Authenticate with Strava (OAuth)
fitops auth logout Revoke token and clear credentials
fitops auth status Show token validity and athlete info
fitops auth refresh Force token refresh
fitops sync — Data Sync
fitops sync run Incremental sync (since last sync − 3 days)
fitops sync run --full Full historical sync
fitops sync run --after DATE Sync from specific date (YYYY-MM-DD)
fitops sync streams Backfill GPS/HR/power streams for all activities
fitops sync status Show sync state and last sync timestamp
fitops activities — Activity Data
fitops activities list Last 20 activities (Rich table)
fitops activities list --sport Run --limit 50 Filter by sport type
fitops activities list --after 2026-01-01 Filter by date
fitops activities get ID Full activity detail (JSON)
fitops activities streams ID Time-series stream data (JSON)
fitops athlete — Athlete Profile
fitops athlete profile Profile + bikes/shoes (Rich)
fitops athlete stats Cumulative Strava statistics (JSON)
fitops athlete zones HR and power training zones (Rich)
fitops athlete set --ftp N --max-hr N Set physiology values for analytics
fitops athlete equipment Equipment with distance and activity counts
fitops analytics — Training Analytics
fitops analytics training-load CTL/ATL/TSB trend (last 90 days, Rich)
fitops analytics training-load --today Today's snapshot: fitness, fatigue, form
fitops analytics vo2max VO2max estimate from recent hard efforts
fitops analytics zones Heart rate zone boundaries
fitops analytics zones --method lthr Zone method: lthr | max_hr | hrr
fitops analytics trends Volume, consistency, seasonal patterns
fitops analytics performance Running economy, FTP, efficiency scores
fitops analytics power-curve Mean maximal power + critical power model
fitops analytics pace-zones Show/configure running pace zones
fitops analytics snapshot Compute and save today's analytics snapshot
Output example (training-load --today):
Training Load 2026-03-19
CTL (Fitness) 32.5
ATL (Fatigue) 32.0
TSB (Form) +0.5 [Fresh — optimal race readiness window]
7d Ramp Rate +12.88%
fitops workouts — Workout Plans & Compliance
Workouts are Markdown files in ~/.fitops/workouts/ with optional YAML frontmatter. Segments can be defined as time-in-zone, pace-range, or HR-range steps.
fitops workouts list List workout files (JSON)
fitops workouts show NAME Display workout definition (Rich)
fitops workouts create Create a workout from a JSON definition
fitops workouts link NAME ACTIVITY_ID Link a workout to a completed activity
fitops workouts get ACTIVITY_ID Workout linked to a specific activity (JSON)
fitops workouts history All linked workouts with compliance scores
fitops workouts compliance NAME Score each segment vs actual HR stream
fitops workouts unlink NAME Remove workout–activity link
fitops workouts simulate NAME Simulate workout on a course (see below)
Workout Simulation
Simulate how your workout will play out on a specific course — per-segment paces adjusted for terrain grade and weather:
fitops workouts simulate threshold-tuesday --course 3
fitops workouts simulate tempo-run --activity 17716814972 # use any Strava activity as course
fitops workouts simulate long-run --course 1 --base-pace 5:30
fitops workouts simulate tempo-run --course 3 --temp 28 --humidity 70
fitops workouts simulate long-run --course 1 --date 2026-04-06 --hour 8 # auto-fetch weather
fitops race — Race Courses & Simulation
Import courses from .gpx, .tcx, Strava activity URLs, or Strava activity IDs.
fitops race import FILE_OR_URL --name "Berlin Marathon" Import a race course
fitops race import --activity 17716814972 --name "Local 10K" Import from Strava activity
fitops race courses List all imported courses (JSON)
fitops race course ID Course profile + per-km segments
fitops race delete ID Remove a course
fitops race splits ID --target-time 3:15:00 Quick even-split table
fitops race simulate ID --target-time 3:15:00 Full simulation (Rich table)
fitops race simulate ID --target-pace 4:37
fitops race simulate ID --target-time 3:15:00 --pacer-pace 4:40 --drop-at-km 35
fitops race simulate ID --target-time 3:15:00 --temp 22 --humidity 65 --wind 3.5
fitops race simulate ID --target-time 3:15:00 --date 2026-10-25 --hour 9 # forecast weather
Output example (race simulate):
12KM Salvaterra — Simulation 4:52/km avg
km | Elev | Grade | Headwind | Effects | Target Pace | Split | Elapsed
1 | +18m | +1.8% | 3.2 m/s ↑ | hill +3% env+2% | 5:04/km | 5:04 | 0:05:04
2 | -6m | -0.6% | 1.1 m/s → | — | 4:51/km | 4:51 | 0:09:55
...
fitops weather — Activity Weather
Fetches historical and forecast weather from Open-Meteo (no API key required).
fitops weather fetch ACTIVITY_ID Fetch + store weather for one activity
fitops weather fetch --all Backfill weather for all activities
fitops weather show ACTIVITY_ID Display conditions + WAP factors
fitops weather forecast --lat L --lon L --date D Race-day forecast + pace adjustment
fitops weather set ACTIVITY_ID --temp 28 --humidity 70 --wind 12 --wind-dir 270
fitops notes — Training Notes
Markdown notes stored in ~/.fitops/notes/ — tagged, optionally linked to activities, queryable via CLI or browsable in the dashboard.
fitops notes create --title "Post-race thoughts" --tags race,review
fitops notes create --activity 12345678 --tags fatigue
fitops notes list All notes (newest first)
fitops notes list --tag threshold Filter by tag
fitops notes get SLUG Display full note content
fitops notes edit SLUG Open in $EDITOR, then re-sync DB
fitops notes delete SLUG Remove note file and DB row
fitops notes tags All tags with usage counts
fitops notes sync Re-index note files into DB
fitops dashboard — Local Dashboard
Dashboard = human interface. CLI = agent interface. Every feature is available on both — 1:1 parity.
If you can chart it in the browser, an agent can query it from the terminal. If an agent can query it, a human can see it visually. No analytics are CLI-only; no visualisations are dashboard-only.
fitops dashboard serve Start dashboard at http://localhost:8888
fitops dashboard serve --port 8080 Custom port
Dashboard pages and their CLI equivalents:
| Dashboard page | URL | Equivalent CLI command |
|---|---|---|
| Overview | / |
fitops analytics snapshot |
| Activities | /activities |
fitops activities list / get ID |
| Training Load | /analytics/training-load |
fitops analytics training-load |
| Trends | /analytics/trends |
fitops analytics trends |
| Performance | /analytics/performance |
fitops analytics performance |
| Workouts | /workouts |
fitops workouts list / compliance ID |
| Workout Simulate | /workouts/simulate |
fitops workouts simulate NAME --course ID |
| Notes | /notes |
fitops notes list / get SLUG |
| Weather | /weather |
fitops weather show ID / fetch --all |
| Race Courses | /race |
fitops race courses |
| Course Detail | /race/course/ID |
fitops race course ID |
| Race Simulate | /race/simulate/ID |
fitops race simulate ID --target-time H:MM:SS |
| Import Course | /race/import |
fitops race import FILE --name "..." |
| Athlete Profile | /profile |
fitops athlete profile / zones / equipment |
Storage Layout
~/.fitops/
├── config.json # Strava credentials + physiology settings
├── sync_state.json # Sync history and last sync timestamp
├── fitops.db # SQLite database (all data)
├── workouts/ # Markdown workout definition files
└── notes/ # Markdown training note files
Override the base directory with the FITOPS_DIR environment variable.
Output Format
FitOps uses Rich for human-facing output (formatted tables, colour-coded metrics, panels). Commands that return structured data for agent consumption can output JSON with a _meta block by passing the --json flag:
{
"_meta": {
"tool": "fitops",
"version": "0.1.0",
"generated_at": "2026-03-19T22:00:00+00:00",
"total_count": 3,
"filters_applied": {"sport_type": "Run"}
},
"activities": [
{
"strava_activity_id": 12345678901,
"name": "Morning Run",
"sport_type": "Run",
"start_date_local": "2026-03-19T07:30:00",
"duration": {"moving_time_seconds": 3720, "moving_time_formatted": "1:02:00"},
"distance": {"meters": 10250, "km": 10.25},
"pace": {"average_per_km": "6:03"},
"heart_rate": {"average_bpm": 148, "max_bpm": 172},
"data_availability": {
"has_heart_rate": true,
"has_power": false,
"streams_fetched": true
}
}
]
}
Design principles:
- Units explicit in every field name (
_m,_km,_s,_bpm) - Gear IDs resolved to names
- Paces formatted as
"6:03"(not raw m/s) data_availabilitytells an agent what additional detail can be fetched
Development
pip install -e ".[dev]"
pytest
License
MIT — see LICENSE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fitops-0.1.1.tar.gz.
File metadata
- Download URL: fitops-0.1.1.tar.gz
- Upload date:
- Size: 200.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7136a8cd5ac1967386f7aed24834a30f1a5c65af3a534a2ceaff79db1dedd33d
|
|
| MD5 |
03da833529acdbdb4e5df5d5cde70680
|
|
| BLAKE2b-256 |
c74946a02c0cd07a2a899be23f84b5490e8cf85265218a37d769675cba6db92b
|
File details
Details for the file fitops-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fitops-0.1.1-py3-none-any.whl
- Upload date:
- Size: 215.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a46cbabb8f7b01b4c0622ecc04fa104094d685433cbef36ebdca14f162445cb4
|
|
| MD5 |
d68793a2f9554ddcf3a0bca606009e92
|
|
| BLAKE2b-256 |
41e34ce8375d3fc2be36692f437e67bf7451c359ac22c0c60c2329332adf7c86
|