Your workouts, in text files you own.

Aski runs strength and rehab training on iPhone and Apple Watch. Let ChatGPT, Claude, or Gemini write your workouts — they come back as text files in your iCloud, ready to start.

No subscriptions, no accounts, no analytics — just big numbers, a loud rest timer, and the four metrics that matter.

A week, end to end

Three screens, in order.

Fig. 01 — 03
Aski mid-set: large rep count and weight, previous set ghosted beneath, coral checkbox.
01 · Mid-set

Big numbers, readable from across the room. The previous set ghosts beneath, pre-filled. One tap confirms.

Aski rest timer: tight coral countdown filling most of the screen.
02 · Rest

A tight coral countdown visible from six feet. Gong at zero, audible tick on the last ten.

Aski session summary: time, sets, heart rate, and volume in a four-up grid.
03 · Summary

Time, sets, heart rate, volume — the four numbers that matter. No badges, no streaks.

  1. 01

    Logging must never interrupt a set.

    Tap the "previous" ghost. Tap the checkbox. Done. No modals, no confirmations, no four-step drawers between you and the next set.

  2. 02

    The rest timer survives everything.

    Live Activity on the Lock Screen, Dynamic Island pill, audible tick on the last 10 seconds, gong at zero. A silent notification with the phone 6 feet away is a failure.

  3. 03

    The watch does what wrists do best.

    Heart rate with zone colour. Session elapsed. Duration timers for timed exercises. Haptic feedback when a rest or duration timer ends. What the watch doesn’t do: log sets. The phone is already doing that, better.

  4. 04

    Onboarding must not block the first set.

    Every question is postponable. Strava, notifications, Watch setup — deferred to Settings until you actually need them. 90 seconds to the first logged rep.

  5. 05

    Track, don’t coach.

    No streaks, no badges, no leaderboards, no daily dopamine push. Aski records what you did; the coaching lives in your chatbot, where it belongs. The app’s job is the schedule and the receipt.

Private by construction

Everything stays on your device.

0 third-party SDKs
0 trackers or analytics
1 opt-in network call · Strava

Workouts live in your iCloud Drive. HealthKit writes stay on the phone. Strava is a single opt-in, one-way upload — with tokens stored in Keychain and revokable any time.

Read the privacy policy
How it works

Let any chatbot write your workouts.

The coach lives in your chatbot — Aski is the gym floor and the receipt. Paste the prompt, answer the interview, and your workouts come back as text files in your iCloud Drive. Portable, auditable, never locked to a subscription.

  1. 01
    Copy the prompt below into any chatbot.

    Claude, ChatGPT, or Gemini. The bot interviews you about goals, equipment, and injuries before writing anything.

  2. 02
    The chatbot writes your workouts as JSON.

    One file per workout — blocks, exercises, sets, loads, rest. Plain text you can read, audit, and version.

  3. 03
    Aski picks them up automatically.

    Save the files into iCloud Drive → Aski → workouts/, or paste the JSON straight into the app (Workouts tab → + → Paste JSON).

Manual route · self-contained prompt

No app yet? Copy the whole prompt.

The block below is everything: the iCloud path, the workout schema, and the interview instructions. Edit the Goal: line, copy the whole thing, paste into Claude, ChatGPT, or Gemini.

# Aski workout handoff

## Your job
You are helping me write workouts for Aski, an iOS strength-and-rehab training app I use solo. **Before you write any JSON, interview me.** Ask one question at a time, in plain language, and wait for each answer before moving on.

Cover at minimum:
1. Primary goal — strength, hypertrophy, rehab, general fitness
2. What I'm doing now — sessions per week, current weights
3. Experience level — lifts I know
4. Available days, session length, equipment / gym access
5. Injuries, things to avoid, current physio constraints
6. Anything I hate doing (so we skip it)

When you have enough, summarise the workouts you intend to build (how many, weekly shape, progression model) in plain English and ask me to confirm. **Only after I confirm** — emit the JSON.

## Where Aski stores workouts
- iCloud container: `iCloud.com.casvanderhoven.aski`
- Workout files live in: **iCloud Drive → Aski → `workouts/`**
  - Full Mac path: `~/Library/Mobile Documents/iCloud~com~casvanderhoven~aski/Documents/workouts/`
- Files are plain UTF-8 JSON. Filename convention: `<uuid>.json` matching the document's `id`.
- Aski auto-imports new `.json` files via NSMetadataQuery — no app restart needed.
- No iCloud access? I can also paste each JSON block directly into the app (Workouts tab → + → Paste JSON from clipboard).

## What I'm giving you (edit these lines before sending)
**Goal:** [e.g. hypertrophy upper body 3 days/week, or return-to-running rehab for my achilles]
**Experience:** [beginner / intermediate / advanced]
**Equipment:** [bodyweight / dumbbells / full gym / mix]
**Sessions per week:** [2 / 3 / 4 / 5]
**Anything I should know:** [injuries, time-of-day, equipment quirks]

## Output format
- After I confirm: emit one fenced ```json``` block per workout file, with the filename on a comment line above each block.
- After the JSON, briefly list the filenames I need to save and where.
- To revise a workout later, re-emit it with the **same `id`** and a **higher `version`** — Aski upserts on id.

## Workout schema (save as `<uuid>.json` in `workouts/`)

```json
{
  "id": "UUID — keep stable across revisions of the same workout",
  "version": 1,
  "name": "Squat day — heavy",
  "createdBy": "Claude (5/3/1-style)",
  "createdAt": "2026-05-04T09:00:00Z",
  "tags": ["lower", "strength"],
  "notes": "Focus on depth and bar speed.",
  "blocks": [
    {
      "type": "warmup",
      "name": "Warm-up",
      "exercises": [
        {
          "exerciseId": "goblet-squat",
          "name": "Goblet Squat",
          "muscleGroups": ["quads", "glutes"],
          "coachNotes": "Slow eccentric. 2× through.",
          "stravaExerciseType": "GOBLET_SQUAT",
          "sets": [
            { "reps": 8, "load": "bodyweight", "rest": 60 }
          ]
        }
      ]
    },
    {
      "type": "main",
      "name": "Main work",
      "exercises": [
        {
          "exerciseId": "back-squat",
          "name": "Back Squat",
          "muscleGroups": ["quads", "glutes", "hamstrings"],
          "coachNotes": "Top set AMRAP.",
          "stravaExerciseType": "BARBELL_BACK_SQUAT",
          "sets": [
            { "reps": 5, "load": "80 kg", "rest": 180 },
            { "reps": 3, "load": "90 kg", "rest": 180 },
            { "reps": 1, "load": "100 kg", "rest": 240, "tempo": "3-1-1-0" }
          ]
        }
      ]
    }
  ]
}
```

### Set variants

- **`{ "reps": N, ... }`** — `reps` (Int), optional `tempo` ("3-1-1-0" = eccentric-bottom-concentric-top), optional `load` ("bodyweight" / "80 kg" / "RPE 7" / "60% 1RM"), optional `rest` (seconds).
- **`{ "durationSeconds": N, ... }`** — for holds and timed work. Optional `load`, `rest`.

### WorkoutBlock.type

`"warmup"` | `"main"` | `"cooldown"`

### stravaExerciseType (optional, per exercise)

Aski uploads finished sessions to Strava's structured strength log (exercises × sets × reps × weight, with muscle maps). Set `stravaExerciseType` to a token from Strava's exercise library so the upload maps exactly. Use the most specific token you're sure of; otherwise use the category's `*_GENERIC` value, or omit the field entirely — Aski then maps the exercise name heuristically.

Category generics: `BENCH_PRESS_GENERIC`, `CALF_RAISE_GENERIC`, `CARDIO_GENERIC`, `CARRY_GENERIC`, `CHOP_GENERIC`, `CORE_GENERIC`, `CURL_GENERIC`, `DEADLIFT_GENERIC`, `FLYE_GENERIC`, `HIP_RAISE_GENERIC`, `HIP_STABILITY_GENERIC`, `HIP_SWING_GENERIC`, `HYPEREXTENSION_GENERIC`, `LATERAL_RAISE_GENERIC`, `LEG_CURL_GENERIC`, `LEG_RAISE_GENERIC`, `LUNGE_GENERIC`, `OLYMPIC_LIFT_GENERIC`, `PLANK_GENERIC`, `PLYO_GENERIC`, `PULL_UP_GENERIC`, `PUSH_UP_GENERIC`, `ROW_GENERIC`, `SHOULDER_PRESS_GENERIC`, `SHOULDER_STABILITY_GENERIC`, `SHRUG_GENERIC`, `SIT_UP_GENERIC`, `SQUAT_GENERIC`, `TOTAL_BODY_GENERIC`, `TRICEPS_EXTENSION_GENERIC`, `WARM_UP_GENERIC`.

Common specific tokens follow the pattern `<EQUIPMENT>_<MOVEMENT>`: `BARBELL_BACK_SQUAT`, `DUMBBELL_BENCH_PRESS`, `GOBLET_SQUAT`, `BARBELL_DEADLIFT`, `ROMANIAN_DEADLIFTS`, `BARBELL_HIP_THRUST`, `GLUTE_BRIDGE`, `LAT_PULLDOWN`, `BENT_OVER_BARBELL_ROW`, `SIDE_PLANK`, `STANDING_CALF_RAISE`, `FLOATING_HEEL_DROP`, `WALL_SIT`, `KETTLEBELL_SWING`. The full list lives at developers.strava.com/docs/uploads — only emit tokens you've seen there.

### MuscleGroup (closed vocabulary)

`chest`, `upper-back`, `lats`, `lower-back`, `shoulders-front`, `shoulders-side`, `shoulders-rear`, `biceps`, `triceps`, `forearms`, `abs`, `obliques`, `glutes`, `quads`, `hamstrings`, `calves`, `hip-flexors`, `adductors`, `abductors`, `neck`. Unknown values are dropped on decode.

### Rehab tag

If I have an active injury, add `"rehab"` to the workout-template `tags` array. Aski activates a session-end pain gate + next-morning reactivity check for any session whose template is tagged `rehab`. Document stop / abort conditions in the template's `notes` field.

Prefer a formal JSON Schema for validation? The single-workout template schema is also available as a downloadable workout.schema.json for tooling that needs a strict $schema reference.