name: liftoscript description: Write idiomatic Liftoscript code for weightlifting programs. Use when creating or editing program files in programs/builtin/. disable-model-invocation: true argument-hint: [program name]
Write Liftoscript Program
Write a Liftoscript program for: $ARGUMENTS
Before writing any code, read these reference files:
llms/liftoscript.md— full language referencellms/liftoscript_examples.md— complete program examplesllms/exercises.md— built-in exercise list (always prefer built-in exercises)src/models/builtinPrograms.ts— valid values for frontmatter fields- Browse
programs/builtin/for format and quality reference
Idiomatic Liftoscript Patterns
Templates are the foundation
Define templates with used: none once, then reuse everywhere with ...templateName. This is the #1 pattern for DRY code.
cluster / used: none / 4x6 / 67% / 60s / progress: custom(increment: 5lb) {~
if (completedReps >= reps && week == 6) {
rm1 += state.increment
}
~}
Bench Press[1-6] / ...cluster
Squat[1-6] / ...cluster / progress: custom(increment: 10lb) { ...cluster }
Key rules:
used: noneonly needs to be declared once per template (in Week 1). Do NOT repeat it when redefining the template in later weeks.- Templates can have arbitrary names — they don't need to match an exercise.
- Override specific parts (progress, weight, timer) on the reusing exercise while inheriting everything else.
Repeat exercises with [1-N], define only in Week 1
Exercises that appear every week should use [fromWeek-toWeek] syntax and only be written out in Week 1. Later weeks only redefine templates:
# Week 1
## Day 1
t1 / used: none / 3x5 / 75% / progress: lp(5lb)
Squat[1-4] / ...t1
Bench Press[1-4] / ...t1
## Day 2
Deadlift[1-4] / ...t1
# Week 2
## Day 1
t1 / 3x4 / 80%
## Day 2
# Week 3
## Day 1
t1 / 3x3 / 85%
## Day 2
Critical: Empty ## Day headers are REQUIRED in later weeks for repeated exercises to show up. Without them, those days won't exist.
Week range syntax [from-to]
Exercise[1-6] means the exercise repeats from week 1 through week 6. Exercise[5-6] means it starts from week 5 and runs through week 6 — useful for exercises that replace others partway through a program.
CRITICAL: [1,3,5] is NOT week selection. Comma-separated numbers in brackets are parsed as forced display ordering, not "only on these weeks." [1,3,5] means "forced order position 1, weeks 3-5." If you need an exercise on non-contiguous weeks (e.g., weeks 1, 3, and 5 only), you must define it explicitly in each of those week headers — there is no shorthand for non-contiguous weeks.
Forced ordering (Exercise[1,1-6]) is usually unnecessary. Use Exercise[1-6] unless you have multiple exercises starting on different days/weeks that need explicit ordering. The first number is a forced display order — only add it when the order would be ambiguous.
Supersets
Use superset: A where A is an identifier shared across exercises in the same group. The scope is per-day, so you can reuse the same label across different days:
Bench Press[1-6] / ...cluster / superset: A
Squat[1-6] / ...cluster / superset: A
Pull Up[1-6] / ...cluster / superset: A
Pre-exhaust supersets (isolation → compound with truly zero rest, e.g., Mike Mentzer's Heavy Duty): set 0s timer on the first exercise and warmup: none on the second. The default superset rest is 15s (enough to move between equipment), but pre-exhaust requires absolutely no recovery time. The second exercise skips warmup since the muscles are already warm from the isolation.
Chest Fly / 1x6 / 20lb 0s / superset: chest / progress: dp(5lb, 6, 10)
Incline Bench Press / 1x6 / 95lb / warmup: none / superset: chest / progress: dp(5lb, 6, 10)
Progression patterns
Linear progression — simplest, for beginners:
Squat / 3x5 / 135lb / progress: lp(5lb)
Double progression — increase reps in range, then bump weight. NEVER use rep ranges in set notation with dp():
Bench Press / 3x8 / 135lb / progress: dp(5lb, 8, 12) // correct
Bench Press / 3x8-12 / progress: dp(5lb, 8, 12) // WRONG
Custom progression — for percentage-based or block programs. Use rm1 += to preserve percentage relationships:
main / used: none / 1x5 58%, 1x5 67%, 1x5+ 76% / progress: custom(increment: 5lb) {~
if (completedReps >= reps && week == 3) {
rm1 += state.increment
}
~}
Reusing progress logic with different parameters:
Squat[1-3] / ...main
Bench Press[1-3] / ...main / progress: custom(increment: 5lb) { ...main }
When reusing, only specify state variables that differ from the template. Omitted variables inherit the template's values.
Percentage-based programs
Use rm1 += state.increment (not weights +=) so that all percentage-based sets adjust proportionally. The weights += approach breaks percentage relationships.
Week/day guards in custom progress
Progress is defined once per exercise/label combination and fires after every workout day that has that exercise. In a 4-day program, the progress fires 4 times per week. Use week and dayInWeek to control WHEN it fires:
// Fire only on the last day of the last week in a 6-week block
if (completedReps >= reps && dayInWeek == 3 && week == 6) {
rm1 += state.increment
}
CRITICAL: weights += applies to ALL weeks and ALL days by default (wildcard target [*:*:*:*]). In multi-day programs, you MUST guard with dayInWeek to prevent the increment from firing multiple times per week:
// WRONG — fires 4 times per week, adding 4x the increment
if (week == 1) { weights += 10lb }
// CORRECT — fires only on the last day of each week
if (week == 1 && dayInWeek == 4) { weights += 10lb }
This applies to any weights += in a progress block where the exercise appears on multiple days per week.
Warmups
- Default warmups are auto-added. Use
warmup: nonefor bodyweight exercises (Pull Up, Chin Up, Dip, etc.) or exercises where warmups don't make sense. - Custom warmups:
warmup: 1x5 45lb, 1x3 135lb, 1x1 80%(percentages are of first working set, not 1RM).
Set variations (stage-based progression)
Separate set schemes with / and switch between them via setVariationIndex:
t1 / used: none / 4x3, 1x3+ / 5x2, 1x2+ / 9x1, 1x1+ / 75% / progress: custom() {~
if (completedReps >= reps) {
weights = completedWeights[ns] + 5lb
} else {
setVariationIndex += 1
}
~}
Starting weights
- If the program uses 1RM percentages, use
%notation:3x5 / 75% - If the program uses RPE, use
@notation:3x5 @8 - Otherwise, ALWAYS set a default starting weight:
3x8 / 135lb. Look up sensible defaults fromstartingWeightLbinsrc/models/exercise.ts. - Non-default equipment:
startingWeightLbis calibrated for the default equipment (e.g., Shrug defaults to dumbbell at 45lb, Bicep Curl defaults to dumbbell at 20lb). When using a non-default variant likeShrug, BarbellorBicep Curl, Barbell, always specify an explicit starting weight since the default will be wrong (e.g.,Shrug, Barbell / 1x6 / 135lb).
Labels for same exercise with different roles
If the same exercise appears in different contexts (e.g., heavy and light), use labels:
power: Bench Press / 3x3 / 85% / progress: dp(5lb, 3, 5)
hyper: Bench Press / 3x10 / 60% / progress: dp(5lb, 10, 15)
AMRAP sets
Add + after the rep count: 4x3, 1x3+. The last set becomes as-many-reps-as-possible.
Ask weight
Add + after a weight to prompt the user to confirm: 3x8 100lb+. To ask for weight when none is explicit (e.g. inferred from RPE), use ?+:
Squat / 3x8 @8 ?+
Rest times
Specify per-exercise or per-set: 3x5 / 120s or 1x5 60s, 3x5 120s.
Validation
After writing, ALWAYS validate:
TS_NODE_TRANSPILE_ONLY=1 npx ts-node scripts/validate_liftoscript.ts programs/builtin/<id>.md
Use validation output to sanity-check:
- Workout duration — does it match claims in the description?
- Weekly volume per muscle group — any glaring gaps to mention in pros/cons?
Fix errors and re-validate until it passes.
Common Mistakes
- Repeating
used: noneon template redefinitions in later weeks - Missing empty
## Dayheaders in later weeks (exercises won't appear) - Using
weights +=in percentage-based programs (userm1 +=instead) - Using rep ranges with
dp():3x8-12 / dp(...)is WRONG - Using
[1,3,5]thinking it selects weeks 1, 3, and 5 — it's actually forced ordering, not week selection. For non-contiguous weeks, define the exercise explicitly in each week header. - Adding forced order numbers when not needed:
[1,1-6]vs[1-6] - Forgetting
warmup: noneon bodyweight exercises - Defining exercises in every week instead of using
[1-N]and template redefinitions - Using
weights +=withoutdayInWeekguard in multi-day programs — it fires once per day, soweights += 10lbon a 4-day program adds 40lb per week instead of 10lb - Omitting rest timers from template overrides in later weeks — rest timers are NOT inherited across weeks. If the initial template defines
/ 120s, every week override must also include/ 120sor the timer will be lost for that week.