agentleFS
spearworksaispearworksai/strava-ftp-skill/ftp/SKILL.md

ftp

Skill0 starsChanged 24 days agoMITView on GitHub

Estimate a cyclist's FTP (functional threshold power) from their recent Strava rides, no ramp test needed. Reads best 3, 5, 10, 20, 30 and 60 minute power over the last 42 and 84 days through Strava's official Claude connector, then gives one FTP number with a range, the rides behind it, and sanity checks (95% of best 20 min, a 2-parameter critical power fit, long steady efforts as a floor). Read-only. Use for /ftp, 'what's my FTP', 'estimate my FTP from my rides', 'is my FTP still right', 'should I change my FTP', or after a hard ride. Needs a Strava account with a power meter and the official Strava connector.

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. jurapascal/claude-code-hub★ 1same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

The file

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 2 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read-only: Use only Strava's read tools: list_activities, get_activity_performance, get_athlete_profile, get_athlete_zones.

    line 12
  2. Distinctive:

    Don't guess numbers: Every watt in the input file comes from a tool result.

    line 17
  3. Very specific:

    Look for Strava's tools (list_activities, get_activity_performance).

    line 24
  4. Very specific:

    Call list_activities with range_start = today minus 84 days (YYYY-MM-DDT00:00:00), range_end = today (YYYY-MM-DDT23:59:59), first: 100, …

    line 31
  5. Very specific:

    Keep sport_type Ride, GravelRide, MountainBikeRide and VirtualRide.

    line 35
  6. Very specific:

    If more than about 40 rides are left, read only the last 42 days and set "windows": [42] in the input file.

    line 39
  7. Very specific:

    Call get_activity_performance for each kept ride.

    line 45
  8. Very specific:

    Follow examples/example-input.json in this skill's folder:

    line 64
  9. Very specific:

    Add --out <file.md> to save the report, or --json for the raw numbers.

    line 88
  10. Very specific:

    If it exits with "These best efforts can't all be right", a value was copied wrong (a longer duration came out higher than a shorter one it …

    line 91
  11. Distinctive:

    If they want a sharper number without a ramp test: one evenly paced, hard 20 to 30 minute effort, ideally on a steady climb.

    line 115

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

FTP from Strava rides

Estimates FTP from rides the user has already done. No ramp test, no new workout.

Rules

  • Read-only. Use only Strava's read tools: list_activities, get_activity_performance, get_athlete_profile, get_athlete_zones. Never change FTP or anything else in Strava or any other app. Give the user the number; they enter it where they want it.
  • Never use Strava's own FTP estimate as an input. Show the FTP stored in Strava for comparison only.
  • Don't guess numbers. Every watt in the input file comes from a tool result. If a value is missing, leave it out.
  • The rides are the user's personal data. Keep the input file in a temporary or scratch folder, and don't share it anywhere.

Step 0: is the Strava connector there?

Look for Strava's tools (list_activities, get_activity_performance). Depending on the app they may carry a prefix, such as <something>__list_activities. If they aren't available, stop and tell the user: "Connect Strava's official connector in Claude (Settings, Connectors, Strava), turn it on for this chat, then run /ftp again."

Step 1: list the rides

Call list_activities with range_start = today minus 84 days (YYYY-MM-DDT00:00:00), range_end = today (YYYY-MM-DDT23:59:59), first: 100, include_tags: true. Follow has_next_page until done.

Keep sport_type Ride, GravelRide, MountainBikeRide and VirtualRide. Drop e-bikes, runs, walks, strength and generic "Workout" entries. Drop rides with moving_time under 180 seconds. is_trainer: true means indoor.

If more than about 40 rides are left, read only the last 42 days and set "windows": [42] in the input file. Each performance answer is 2 to 10 KB, and the 42-day window is the one that sets the number.

Step 2: read each ride's best efforts

Call get_activity_performance for each kept ride. For each one record:

  • has_device_watts. If false, the ride has no power meter: add it with "has_power": false and no values.
  • From best_efforts, the value of GreatestPower3min, GreatestPower5min, GreatestPower10min, GreatestPower20min, GreatestPower30min and GreatestPower1hr, as "3", "5", "10", "20", "30", "60". Skip any the ride doesn't have.
  • From segment_efforts, any effort with moving_time of 3000 seconds (50 minutes) or more, as {"name", "minutes": moving_time / 60, "watts": avg_watts}. A long, steady climb is the best floor for FTP there is.

Strava's per-ride best efforts agree with a second-by-second recomputation of the power stream to within about 1 W, so there's no need to pull streams.

Optional extras: get_athlete_profile gives weight (kg) for W/kg. get_athlete_zones gives the FTP stored in Strava and ftp_is_estimated.

Step 3: write the input file

Follow examples/example-input.json in this skill's folder:

{
  "as_of": "YYYY-MM-DD",
  "windows": [42, 84],
  "weight_kg": 70,
  "current_ftp": {"watts": 240, "estimated": false},
  "rides": [
    {"date": "YYYY-MM-DD", "name": "ride name", "indoor": false, "has_power": true,
     "best": {"3": 330, "5": 318, "10": 296, "20": 272, "30": 262, "60": 248},
     "long_efforts": [{"name": "segment name", "minutes": 56, "watts": 250}]}
  ]
}

as_of is today. Dates are the ride's local start date.

Step 4: run the math

python3 <this skill's folder>/scripts/ftp_calc.py <input.json>

Add --out <file.md> to save the report, or --json for the raw numbers. Standard library only; it runs anywhere Python 3.9+ does.

If it exits with "These best efforts can't all be right", a value was copied wrong (a longer duration came out higher than a shorter one it contains). Re-read that ride's get_activity_performance and fix the file. Don't edit numbers to make the check pass.

What the script does

  1. Best power per duration across each window.
  2. Two primary estimates: 95% of the best 20 minutes, and a 2-parameter critical power fit (work against time, least squares) on the 3, 5, 10 and 20 minute bests.
  3. One sensitivity estimate: the same critical power model fit as power against 1/time. It leans on short efforts and usually reads higher, so it only sets the top of the range.
  4. Floors: the best 60 minutes, and the hardest steady effort of 50 minutes or more.
  5. FTP = median of the 42-day primary estimates, rounded to 5 W. Range = the higher of the floor and the lowest estimate, up to the highest estimate.
  6. Sanity ratios: 20 min / 5 min is typically 0.85 to 0.88, and 60 min / 20 min 0.90 to 0.95. Outside those bands, one of the efforts probably wasn't all-out, and the report says which estimate to trust.

Step 5: tell the user

Lead with the FTP and range. Name the rides behind the 5, 20 and 60 minute bests and the long effort floor. If Strava's stored FTP is 5 W or more away, say so; changing it is the user's call. Mention any indoor bests (trainer power can read differently from a power meter).

If they want a sharper number without a ramp test: one evenly paced, hard 20 to 30 minute effort, ideally on a steady climb. 95% of a true 20-minute effort, or the full 30-minute average, beats any model.

Limits

  • Needs a power meter or smart trainer. Strava's estimated watts are not used.
  • Bests set late in long rides understate fresh power, so the estimate leans conservative.
  • It's an estimate from riding data, not a lab test.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.