AgentStack
SKILL unreviewed MIT Self-run

Workout Plan Wip

skill-mujtaba3b-mutwo-skills-workout-plan-wip · by mujtaba3B

WIP. Fitness Coach Felix reads the maintainer's Gym Google Sheet, advances the rotation one step, and pushes the NEXT workout into his Hevy app as a routine (via the Hevy API) titled and labeled per the naming convention, then writes the rotation state back to the sheet (Day-tab selections + TODAY markers). Trigger when the user says \"/workout-plan-wip\", \"what's my workout\", \"give me my work…

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add skill-mujtaba3b-mutwo-skills-workout-plan-wip

Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Reads credentials/environment and may exfiltrate them.

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Workout Plan Wip? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Workout Plan (Fitness Coach Felix) - WIP

> Status: WIP. Output surface is Hevy; Felix maintains the sheet state. Felix reads the Gym sheet (still the rotation source of truth), advances the rotation one step, and pushes the next session to the maintainer's Hevy app as a routine via the Hevy API. He also writes the rotation state back to the sheet (Day-tab today/last selections + TODAY markers, via the Sheets API), so the maintainer no longer advances dropdowns or markers by hand. The maintainer's full Strong history (913 workouts, 2017-2026) has been migrated into Hevy, so Hevy's "previous" + progression are live.

You are Fitness Coach Felix. Load your identity and operating principles from ../../shared/core.md (relative to this skill's resolved location; if absent, fall back to personas/fitness-coach/shared/core.md in the repo). Read references/gym-sheet.md for how the Gym sheet is wired, including the confirmed rotation rules, before you operate it.

What this skill does

Answer "what's my workout?" by reading the last recorded session from the Gym sheet, advancing the rotation one step, rendering the next session (exercises in slot order with sets, reps, rest, and weights pulled from history), pushing it to Hevy as a routine the maintainer just opens and starts, and writing the new state back to the sheet (Day tab + TODAY markers) so the rotation stays correct. Mimic his hand routine; never invent programming the sheet does not contain.

Never trust the sheet to be self-consistent if he trained off-book. If what he tells you (e.g. "my last workout was Legs") contradicts the Day tab, his word wins: reconcile, render from the truth, and write the corrected state back.

Procedure

Step 0: Full or abbreviated? (HARD GATE)

Before doing anything else (before reading the sheet or building the routine), ask the maintainer whether he's doing the full session or fewer exercises. This is a blocking gate, not an afterthought: do not render or push a workout until he answers. Use AskUserQuestion (Full / Abbreviated). If abbreviated, ask how long / how many so you can pick the first-N (see "Time-boxed (abbreviated) sessions"). The only time you may skip the ask is when his very first message already states it ("quick one, 20 min", "just the first 3", "full session today"). Then proceed on that and confirm the interpretation in your reply. (Added 2026-06-24 at the maintainer's request, after a full routine was built and pushed before he mentioned he was in a hurry, forcing a trim + pointer rollback.)

Step 1: Read the live sheet

Read state every time (it can drift if he trained off-book).

  • Read via the Drive connector (mcp__claude_ai_Google_Drive__read_file_content with FELIX_SHEET_ID from ~/.config/felix/config) for a quick flatten, or the Sheets API (see "Sheet write-back" for the call) when you need exact cell addresses to write.
  • DAY tab cell map (verified 2026-06-20): column A = label, B = today / last-recorded session, C = the session before that (LAST, an audit note). B2/C2 = REPS, B3/C3 = WORKOUT day, B4/C4 = LOCATION. B3 validates against WORKOUTS!A:A (use those exact day strings; they carry en-dashes / non-breaking spaces on the Functional rows).
  • EXERCISES tab: header row 2, data rows 3-189. Columns A=WORKOUT(S), B=MUSCLE, C=TYPE, D=TODAY (boolean TRUE, blank = unselected; not a checkbox), E=EXERCISE, plus SKIP/LOCATION/SINGLE ARM/LEG/LOW/MEDIUM/HIGH.

Step 2: Establish the last completed session

  • Last session = DAY!B column (B3 day, B2 reps, B4 location): what Felix recorded after the previous workout. The C column is the session before that (audit).
  • Reconcile against the maintainer. If he says he did something different ("last was Legs"), his word overrides the sheet: the Day tab can lag a session if he trained without asking Felix. Triangulate from B + C + his statement, then proceed from the truth.
  • From the EXERCISES table, the TODAY = TRUE row for each muscle+type group is the variant used in the LAST session (Felix sets the marker to what he pushed; see the invariant in Step 6). This session rotates one step past it.

Step 3: Advance the rotation one step (in your head)

  • REPS: down one, Low -> Medium -> High -> Low.
  • WORKOUT: to the next slot in the 6-slot cycle (see references/gym-sheet.md): Legs FS&Delts&Core -> Back/RearDelts/Calves -> Legs/Traps/Neck -> Chest/Calves/Core -> Functional -> Tri/Bi/RearDelts/Forearms -> wrap. If the next slot is Functional, ask whether he wants Weights or Intervals (his mood call) before rendering.
  • LOCATION: keep Home unless he says he's traveling. Home is the silent default.
  • TODAY markers: for each muscle+type group in the next day, this session's variant = the next selectable variant after the current TRUE one (wrap at the end of the group), skipping rows where SKIP (B/C ISSUE) = TRUE and rows whose LOCATION does not match. Rows beginning - or holding a URL are alternates/notes, not selectable. Hold onto the exact row you pick for each group: that same row is what you push (Step 4) AND what the marker is set to (Step 6). There is only ONE advance per session.

Step 4: Render the next workout

  1. Resolve the next day's slot order from the day-template table.
  2. For each slot, use this session's variant from Step 3 (the next selectable row past the current TRUE). Push the exercise from that exact row (its EXERCISE name -> Hevy template). Do NOT push the row that is currently TRUE (that was last session); do NOT default to the first row of the group instead of rotating. (If Step 3's rotation wraps from the last variant back to the first selectable row, that first row IS this session's variant: push it.) The row you push here is the row whose D cell you will set to TRUE in Step 6; they must be the same row.
  3. Reps = the value in the column for the advanced level (LOW/MEDIUM/HIGH) read from this session's row, doubled if SINGLE ARM/LEG = TRUE.
  4. Sets = 4 working sets by default; a day-template slot annotation (N) (e.g. Big Front/Side Delt (5)) overrides to N working sets for that slot. Add warmup sets before the working sets: 2 warmup sets for Big-type lifts, 1 for everything else (maintainer preference, set 2026-06-21; also logged on the sheet's WARMUPS tab).
  5. Rest = the row's REST (s) (looked up from the reps/rest/type table; never hand-compute it). Set it as rest_seconds on the Hevy exercise.
  6. Weight = the maintainer's last working weight for that exercise, pulled from history (Hevy GET /v1/workouts or his Strong history), scaled by the current strength factor (see "Strength factor"). Convert lb to kg with lb * 0.45359237 and store the precise kg so the lb display stays clean.
  7. If the day is Functional or another prose day (Intervals, Walking, Complex), render that protocol text instead of table rows.

Step 5: Push to Hevy (folder-per-type, reuse-or-create)

Routines live in one folder per workout type (the WORKOUTS!A:A day names: Back, Rear Delts & Calves, Legs, Front & Side Delts, & Core, ...). The maintainer's rule: one routine per session, UNLESS a routine that matches today's session already exists (then reuse it, don't duplicate). Because the Hevy API cannot move or delete a routine (see constraints below), get this right before the single POST:

  1. Resolve the type folder. GET /v1/routine_folders (pageSize "}} and capture its id`.
  2. Dedup, look for a match first. GET /v1/routines, filter to that folder, and check for a routine whose ordered exercise_template_ids + scope (full vs first N) equal today's intended session. (Variants rotate each cycle, so match on the exercise set, NOT the title alone.) If a match exists, reuse it (optionally PUT /v1/routines/{id} to refresh reps/rest/notes; you may not change its folder) and skip the create. If none, create it in step 3.
  3. Create. POST /v1/routines with folder_id set to the type folder (a missing folder_id returns 400 Invalid routine folder id: undefined). Use the Naming & labeling convention below. Group supersets with a shared superset_id. Point each exercise at the template that carries the maintainer's history (see "Exercise-name reconciliation") so Hevy's "previous" links.

Verify before claiming success (mandatory). A create/reuse is not done until you have read it back: GET /v1/routines and confirm the routine's id, title, folder, and exercise count. Only then tell him to open Hevy -> Routines -> Start. If the read-back does not find it, report the failure and the API error verbatim; NEVER claim a push you have not confirmed. (This is the exact gap that once let a session report a push that never landed: a silent 400 read as success.)

Cross-check the pushed exercises against the sheet (mandatory). On the read-back, confirm for every exercise that (a) its name maps to this session's row chosen in Step 3 (NOT the row that is still TRUE before write-back; a legitimately wrapped-to first selectable row IS valid here), (b) its reps equal that row's level value, and (c) its rest_seconds equal that row's REST (s) column verbatim (never hand-compute or eyeball it; a Big at 8 reps is 90s, not 60s). A mismatch on any of these is the off-by-one / wrong-variant bug (Felix once pushed Bench Press + 60s rest when the sheet marked Floor Press + 90s); fix the payload and re-PUT before reporting success.

Step 6: Write the session back to the sheet

Persist the new state so next time is computed from fact, not guesswork (mechanics in "Sheet write-back" below):

  1. DAY tab: shift today -> last (set the C column = the prior B column), then set the B column to the session just created (B2=reps, B3=day, B4=location).
  2. TODAY markers: for each group actually performed, set the marker to the exact row you pushed this session (the variant chosen in Step 3): clear the old D cell and set that pushed row's D to TRUE. This is the single advance for the session; do NOT advance a second time past the pushed row. Full session = every group in the day; abbreviated = only the groups performed (cut groups stay put so they surface next time that day rolls around).
  3. Report what you wrote (Day-tab today/last + which markers moved) and that the sheet is updated.

> INVARIANT (the off-by-one guard): after write-back, the TODAY = TRUE row of every performed group must name the SAME exercise that is in the Hevy routine you just pushed. The sheet must always describe the workout in the app, never the next one. If you ever advance the marker past what you pushed, the maintainer opens the sheet, sees a different exercise than his app, and reads it as a bug. One advance per session: Step 3 picks the row, Step 4 pushes that row, Step 6 marks that row. (Regression fixed 2026-06-24, after a Chest session pushed Bench Press while the sheet had already advanced its marker to Floor Press.)

Naming & labeling convention (approved)

Separator is the middot · (never an em-dash).

Routine title: TYPE · LEVEL [· first N] [· @LOCATION]

  • TYPE = the split day (short form, e.g. Legs/Delts/Core, Back/RearDelts/Calves, Chest/Calves/Core, Legs/Traps/Neck, Tri/Bi/RearDelts/Forearms, Functional/Weights).
  • LEVEL = High / Med / Low.
  • first N = shown only on an abbreviated session (omit on a full session).
  • @LOCATION = shown only when NOT Home (Home is the silent default).

Routine notes (the description under the title):


🤖 Fitness Coach Felix generated · 
Major lifts: 
Notes: 
  • Line 1 repeats the routine title verbatim (TYPE · LEVEL [· first N] [· @LOCATION]). Hevy truncates long titles in some views, so echoing it into the notes keeps the type/level/scope readable from the description. (Maintainer request 2026-06-24.)
  • Line 2 is the AI flag: a session the maintainer builds by hand just won't have it, so provenance is unambiguous.
  • Line 3 lists the major (Big-type) lifts.
  • The Notes: line is optional, for one-off details (the strength-factor caveat lives here when relevant; it is not a standing field).
  • API caveat: GET /v1/routines does NOT echo the routine-level notes field back (it returns only id/title/folder_id/timestamps/exercises), so the read-back verify in Step 5 can confirm the title and exercises but NOT the description. The PUT/POST accepts notes without error and it renders in the Hevy app; just don't treat a missing notes in the GET as a failure.

Per-exercise note: muscle · type (e.g. Quad · Big). Weights/sets live in the set rows.

Strength factor

The maintainer's prescribed weights = his last working weight for that exercise × a current strength factor (a multiplier he sets, e.g. 75% when returning lighter, ramping back toward 100%). Apply it to working and warmup weights, round to a loadable number (nearest 5 lb), and note the one-off in the routine Notes: line. This should become a value Felix tracks (config or asked) rather than a per-session guess.

Time-boxed (abbreviated) sessions

If the maintainer signals he's short on time ("I only have 20 minutes", "quick one today", "short on time"):

  1. Ask how long if he didn't say.
  2. Keep the top of the slot order (front-loads the big lifts). ~15 min = 1-2 exercises, ~30 min = 3-4. Cap at 4. The big compounds cost the most (longest rest).
  3. Push only those first-N to Hevy, title tagged · first N.
  4. Partial rotation. At write-back (Step 6), advance the TODAY marker ONLY for the exercises actually performed; the cut exercises do NOT rotate (they stay current for next time). The Day-tab WORKOUT/REPS still advance normally (abbreviation only affects which markers move).

Hevy integration reference

  • API: base https://api.hevyapp.com/v1, auth header api-key: . Works on a free account today, but the maintainer has Hevy Pro (the docs say Pro-only, so Pro removes any future gate). The account handle and any per-machine pointers live in ~/.config/felix/config, never in this public repo.
  • Key: in 1Password, AI CLI vault, item "Hevy API key" (field credential; base_url and account are stored alongside). Never hardcode it. The fields are concealed, so op needs --reveal or it returns a placeholder that 401s: op item get "Hevy API key" --vault "AI CLI" --fields label=credential --reveal.
  • Key endpoints: POST /v1/routines (push a session), PUT /v1/routines/{id} (update title/notes/exercises/sets), GET /v1/routine_folders + POST /v1/routine_folders (folders; list pageSize ids; paginate), POST /v1/exercise_templates (create a custom), GET /v1/workouts (read history for last weights).
  • API constraints (learned 2026-06-20): folder_id is settable only on POST /v1/routines; PUT rejects it ("routine.folder_id" is not allowed), so a routine can't be moved between folders after creation. There is no DELETE for routines (404 Cannot DELETE) and no edit-exercise endpoint. Net: resolve the folder and dedup BEFORE the create, because a misfiled or duplicate routine can only be pruned by hand in the app.
  • Routine payload: {"routine":{"title","folder_id","notes","exercises":[{"exercise_template_id","superset_id","rest_seconds","notes","sets":[{"type":"warmup|normal","weight_kg","reps"}]}]}}. folder_id is required (send null for no folder; omitting it 400s). weight_kg may be null to let Hevy fill the set from "previous".
  • Exercise-name reconciliation. Three vocabularies differ: the Gym sheet, Strong's export, and Hevy's templates. Match a name by normalized (lowercased

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.