paicer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. paicer-0.1.0/.claude/commands/paicer/create-plan.md +53 -0
  2. paicer-0.1.0/.claude/commands/paicer/review-progress.md +58 -0
  3. paicer-0.1.0/.claude/skills/plan-authoring.md +177 -0
  4. paicer-0.1.0/.env.example +18 -0
  5. paicer-0.1.0/.github/workflows/publish.yml +37 -0
  6. paicer-0.1.0/.gitignore +38 -0
  7. paicer-0.1.0/.python-version +1 -0
  8. paicer-0.1.0/CHANGELOG.md +58 -0
  9. paicer-0.1.0/CLAUDE.md +42 -0
  10. paicer-0.1.0/LICENSE +21 -0
  11. paicer-0.1.0/Makefile +52 -0
  12. paicer-0.1.0/PKG-INFO +56 -0
  13. paicer-0.1.0/README.md +43 -0
  14. paicer-0.1.0/docs/garmin-api.md +150 -0
  15. paicer-0.1.0/docs/rgactive_16_week_triathlon_training plan.pdf +0 -0
  16. paicer-0.1.0/docs/superpowers/specs/2026-05-24-pypi-packaging-design.md +136 -0
  17. paicer-0.1.0/examples/hm-tri-combo.yaml +3306 -0
  18. paicer-0.1.0/examples/reference-imperial.yaml +291 -0
  19. paicer-0.1.0/examples/reference-metric.yaml +288 -0
  20. paicer-0.1.0/pyproject.toml +29 -0
  21. paicer-0.1.0/src/paicer/__init__.py +6 -0
  22. paicer-0.1.0/src/paicer/cli.py +70 -0
  23. paicer-0.1.0/src/paicer/config.py +52 -0
  24. paicer-0.1.0/src/paicer/formatters/__init__.py +1 -0
  25. paicer-0.1.0/src/paicer/formatters/base.py +16 -0
  26. paicer-0.1.0/src/paicer/formatters/html.py +298 -0
  27. paicer-0.1.0/src/paicer/formatters/markdown.py +162 -0
  28. paicer-0.1.0/src/paicer/integrations/__init__.py +1 -0
  29. paicer-0.1.0/src/paicer/integrations/base.py +41 -0
  30. paicer-0.1.0/src/paicer/integrations/garmin.py +277 -0
  31. paicer-0.1.0/src/paicer/plan_utils.py +198 -0
  32. paicer-0.1.0/src/paicer/render.py +18 -0
  33. paicer-0.1.0/src/paicer/review_data.py +299 -0
  34. paicer-0.1.0/src/paicer/sync.py +133 -0
  35. paicer-0.1.0/tests/__init__.py +0 -0
  36. paicer-0.1.0/tests/test_cli.py +90 -0
  37. paicer-0.1.0/tests/test_config.py +50 -0
  38. paicer-0.1.0/tests/test_package.py +3 -0
  39. paicer-0.1.0/uv.lock +652 -0
@@ -0,0 +1,53 @@
1
+ # Create or Edit Training Plan
2
+
3
+ Help the user create a new training plan or modify an existing one.
4
+
5
+ **Invoke the `plan-authoring` skill** for YAML structure, unit conventions, Garmin patterns, and periodization principles.
6
+
7
+ ## First-Time Setup
8
+
9
+ No setup file needed — paicer saves config to `~/.paicer/config` automatically on first run.
10
+
11
+ ## Creating a New Plan
12
+
13
+ Interview the user **one question at a time**. Wait for each answer before asking the next.
14
+
15
+ 1. What are you training for? (race distance, goal date, first time?)
16
+ 2. Current fitness? (recent volume, longest recent run/ride, recent races?)
17
+ 3. How many days per week, and which days?
18
+ 4. What sports? (running only, triathlon, cycling?)
19
+ 5. Equipment? (Garmin watch model, power meter, pool access?)
20
+ 6. Current easy pace? (Adapt to their sport. Calculate race paces from any race results — don't ask them to do the math.)
21
+
22
+ If they have a Garmin watch: tell them to run `paicer sync w1` after the plan is ready — it will prompt for credentials on first use. Do NOT ask for credentials directly.
23
+
24
+ ## Plan Length and Start Date
25
+
26
+ Calculate weeks until race day, then recommend:
27
+
28
+ - Standard lengths: 8, 10, 12, 16, 20 weeks (pick longest that fits)
29
+ - Minimum: 8 weeks for 5K/10K, 12 for half marathon, 16 for marathon/triathlon
30
+ - `start_date` should be a Monday, counting back from race day
31
+ - More time than needed? Start later, don't pad.
32
+
33
+ Present: "You have N weeks until race day. I'd recommend an X-week plan starting [date]." Always confirm with the user.
34
+
35
+ ## Building the Plan
36
+
37
+ 1. Read the appropriate reference plan: `examples/reference-metric.yaml` or `examples/reference-imperial.yaml`
38
+ 2. Create plan file in `plans/`
39
+ 3. Design phase structure (Base -> Build -> Peak -> Taper)
40
+ 4. Build week-by-week with progressive volume
41
+ 5. Add Garmin structures with YAML comments in user's unit system
42
+ 6. Create YAML anchors for reusable sessions (swim, track)
43
+ 7. Preview: `paicer render --plan plans/new-plan.yaml` (saves plan path to config, fails loudly if YAML is invalid)
44
+ 8. Offer first week sync: `paicer sync w1`
45
+ 11. If Garmin set up: suggest `/paicer:review-progress` after first week of training
46
+
47
+ ## Modifying an Existing Plan
48
+
49
+ 1. Read plan path from `~/.paicer/config` (key: `plan`)
50
+ 2. Back up: `cp plans/my-plan.yaml plans/my-plan.backup.yaml`
51
+ 3. Make edits, preserving sequential numbering and naming conventions
52
+ 4. Preview: `paicer render` to confirm the YAML is valid
53
+ 5. If Garmin workouts changed, remind user to re-sync affected weeks
@@ -0,0 +1,58 @@
1
+ # Training Plan Weekly Review
2
+
3
+ Compare last week's Garmin activities against the plan and discuss adjustments.
4
+
5
+ **Invoke the `plan-authoring` skill** for unit conventions and pace conversion reference.
6
+
7
+ ## Steps
8
+
9
+ Read `units` from `~/.paicer/config` (default: `metric`). Present all values in the user's preferred system.
10
+
11
+ 1. Read `plan` path from `~/.paicer/config`, then read the plan YAML using Read tool (do NOT write scripts to parse YAML).
12
+ 2. Pull review data:
13
+ ```
14
+ uv run python -m paicer.review_data <plan_path> # most recently completed week
15
+ uv run python -m paicer.review_data <plan_path> 3 # specific week number
16
+ ```
17
+ 3. Match activities by `activityName` against `W{week_num}: {name}` (prefixed format from Garmin upload). Do NOT match by date — users often shift days.
18
+ 4. Analyze using the `intervals` array — NOT overall activity averages. Each interval has `type` (INTERVAL_WARMUP, INTERVAL_ACTIVE, INTERVAL_RECOVERY, INTERVAL_COOLDOWN), `paceSecPerKm`, `averageHR`, `averagePower`, `distance`, `duration`.
19
+ - **Structured workouts (tempo, intervals):** Compare INTERVAL_ACTIVE entries against targets. Overall average is misleading (includes warmup/cooldown).
20
+ - **Easy runs:** May only have INTERVAL_ACTIVE or no intervals. Use overall average.
21
+ - **Elevation context:** Check `elevationGain`/`elevationLoss` on every activity. Hilly runs raise HR at the same effort — don't flag elevated HR without accounting for elevation. Mention gain when it's notable (>5 m/km).
22
+ - **Running with pace targets:** compare each INTERVAL_ACTIVE pace vs planned, note HR
23
+ - **Running with HR targets:** compare INTERVAL_ACTIVE HR vs target zone
24
+ - **Cycling with power targets:** compare INTERVAL_ACTIVE power vs target zone
25
+ - **Swimming:** completion check (did the session happen?)
26
+ - **Distance:** actual vs planned
27
+ - **HR time in zones** (`hrTimeInZones`): Use to verify easy runs stayed in Zone 1–2. If zone 3+ time exceeds ~10% of duration on an easy run, flag it (accounting for elevation). For tempo/interval workouts, zone distribution confirms effort matched intent.
28
+ - **Aerobic training effect** (`aerobicTrainingEffect`): 1–5 scale. Easy runs should be 2.0–3.0 ("maintaining"). Tempo/intervals 3.0–4.0 ("improving"). Long runs 3.0–4.5. Flag if an easy run scores >3.5 (too hard) or a key session scores <2.5 (too easy). Useful as a weekly load summary — sum or average across sessions to gauge overall training stress.
29
+ - **Training status** (`trainingStatus`): Included at top level of review data. Report:
30
+ - **Acute-to-chronic ratio**: optimal 0.8–1.5. Below 0.8 = detraining. Above 1.5 = overreaching risk. If above 1.3, note it as something to watch.
31
+ - **Load balance feedback**: Garmin's own assessment (e.g., ABOVE_TARGETS, WITHIN_TARGETS). If above targets, discuss whether next week should be lighter.
32
+ - **VO2max trend**: note if it changed from previous review.
33
+ - **Training status phrase**: e.g., PRODUCTIVE, MAINTAINING, OVERREACHING. Flag non-productive states.
34
+ 5. For unmatched workouts: check for other activities that might be the same workout done under a different name. Ask user to confirm before using.
35
+ 6. Flag mismatches: HR too high/low at target pace, missed workouts, distance deviations.
36
+ 7. Present findings conversationally. Discuss adjustments.
37
+ 8. If pace adjustments agreed, update `targetValueOne`/`targetValueTwo` and descriptions in YAML.
38
+ 9. Add review entry:
39
+ ```yaml
40
+ reviews:
41
+ - week: N
42
+ date: "YYYY-MM-DD"
43
+ notes: "Summary"
44
+ adjustments:
45
+ - "Description of each change"
46
+ ```
47
+
48
+ **Notes content:** stick to training-relevant facts — pace, HR, power, distance, elevation, completion vs plan, perceived effort, weather if it affected execution, illness/injury that affected sessions. Allergies are fine to mention if they affected training. **Skip lifestyle context** like alcohol consumption, social events, work stress, or other personal details that are not training data — even when the user mentions them in chat. The plan log is a training record, not a journal.
49
+
50
+ 10. Run `paicer render` to confirm the YAML is valid after edits.
51
+
52
+ ## Race Week (special handling)
53
+
54
+ When the upcoming week contains a race, the standard review flow still applies — but **race-week conversations must lead with the HR ceiling rule, not with fueling/kit/course details.** See the "Race Strategy and Execution Priorities" section of the `plan-authoring` skill for the priority order.
55
+
56
+ The single highest-leverage piece of race-day advice is: *"Stay below the HR ceiling until the release km. Then push."* If that gets buried under gel timing, breakfast composition, and kit selection conversations, the user loses the race in execution even when training is fit. Make it the headline of every race-week message.
57
+
58
+ If the race workout has a `race_strategy:` field, surface its `one_liner` prominently in every race-week conversation.
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: plan-authoring
3
+ description: Use when creating, editing, or reviewing training plan YAML - covers structure rules, unit conventions, Garmin patterns, workout types, and periodization principles
4
+ ---
5
+
6
+ # Plan Authoring
7
+
8
+ Reference for writing correct training plan YAML. Used by `/paicer:create-plan`, `/paicer:review-progress`, and ad-hoc plan edits.
9
+
10
+ ## Unit System
11
+
12
+ Read `UNITS` from `.env` (default: `metric`). Use for all text fields (`name`, `description`) and YAML comments on Garmin values.
13
+
14
+ Garmin steps always use metric (meters, sec/km). Add comments in the user's system: `endConditionValue: 6437 # 4 mi`.
15
+
16
+ **Imperial conventions** — use clean round numbers, not conversions:
17
+ - Runs: 3, 4, 5, 6, 8, 10, 13, 16, 20 miles
18
+ - Bikes: 10, 15, 20, 25, 40 miles
19
+ - Paces: round to nearest :15 (7:30, 7:45, 8:00/mi)
20
+ - Swim: yards for US pools (200, 500, 1000 yds)
21
+ - Track: stays in meters (400m tracks are universal)
22
+
23
+ ## YAML Structure Rules
24
+
25
+ - Single `start_date` — all dates calculated from it
26
+ - Day numbers = 1-based positions in `training_days`, not weekday names
27
+ - Days must be sequential within a week (1, 2, 3... no gaps except same-day pairs)
28
+ - Non-optional workouts must not exceed `training_days` slots
29
+ - Week/day numbers sequential within their parent
30
+ - `name`: descriptive with distance/workout info (e.g., "Easy 8km", "Tempo 3x8min"). Week prefix added at Garmin sync time.
31
+
32
+ ## Workout Types
33
+
34
+ | Type | Garmin Sport | Notes |
35
+ |------|-------------|-------|
36
+ | `run` | running (1) | HR zone for easy, pace targets for tempo/intervals |
37
+ | `track` | running (1) | Reusable via YAML anchors |
38
+ | `bike` | cycling (2) | See cycling target rules below |
39
+ | `swim` | swimming (4) | Lap-button cue cards with `description` per step |
40
+ | `multisport` | multi_sport (10) | `garmin.legs`, each with `sport` + `steps` |
41
+ | `race` | — | Race day entry, typically `skip_garmin: true` |
42
+
43
+ ## Flags
44
+
45
+ - **`optional: true`** — shows as "Optional:" in docs, doesn't count against training_days. If it has a `garmin:` section, uploads to library without scheduling. Doesn't override `skip_garmin`.
46
+ - **`skip_garmin: true`** — no Garmin upload at all.
47
+
48
+ ## Cycling Target Rules
49
+
50
+ Garmin evaluates power zone targets against 3-second average power. On undulating outdoor terrain, this causes constant out-of-zone alerts regardless of actual effort — and the visual deviation banners obscure the data screens you actually want to see. The Garmin Connect API does not support configuring a longer averaging window.
51
+
52
+ Power is still the right measurement for hard cycling intervals when a power meter is present. The fix is to keep the structured workout shape but stop letting Garmin enforce target zones on the bike. The user paces by feel + lap-average power on the data screen, and gets clean post-ride power data for analysis.
53
+
54
+ **Use `heart.rate.zone` for:**
55
+ - All steady-state / endurance cycling (single-interval rides at zone 2–3)
56
+ - Warmup and cooldown steps in steady rides
57
+ - Brick workout bike legs that are steady-state at Z2/Z3
58
+
59
+ **Use `no.target` with `description` for:**
60
+ - Any step that would otherwise have a `power.zone` target (threshold, VO2, surge intervals, hard brick blocks)
61
+ - Warmup/cooldown of structured power workouts (so the entire workout is banner-free, not just the work intervals)
62
+
63
+ **Description format (Fenix/Edge display fits ~30–40 chars cleanly):**
64
+ ```
65
+ "<duration> @ Z<n> (RPE <n>)"
66
+ ```
67
+ Examples: `"5 km @ Z2 (RPE 5)"`, `"6 min @ Z3 (RPE 6)"`, `"1 min @ Z5 (RPE 9)"`.
68
+
69
+ Don't include explicit watt numbers — FTP changes over time and the zone label remains correct as long as zones are kept current. The user reads the description, glances at lap power, paces accordingly. Recorded power data still supports post-ride analysis; what's lost is real-time zone enforcement (which was never reliable outdoors anyway).
70
+
71
+ **Why HR works for steady cycling:** HR naturally smooths terrain-induced fluctuations. On a hilly loop, power spikes uphill and drops downhill, but HR stays in zone if effort is consistent. Same logic as running easy runs with HR targets. HR-targeted bike steps don't trigger banner spam because heart rate physiologically lags terrain.
72
+
73
+ **Roadmap:** for users without a power meter, the right pattern is to use `heart.rate.zone` targets across all bike steps including hard intervals. Plan generation should detect power-meter availability and pick the appropriate target type. Not yet implemented — current plans assume a power meter for the bike.
74
+
75
+ ## Garmin Step Patterns
76
+
77
+ Read `examples/reference-metric.yaml` (or `reference-imperial.yaml`) for working examples of every pattern. Read `docs/garmin-api.md` for complete API reference.
78
+
79
+ **Common patterns:**
80
+ - Easy run: single `interval` step + `heart.rate.zone` zone 2
81
+ - Steady bike: single `interval` step + `heart.rate.zone` zone 2–3
82
+ - Bike intervals: `warmup` (HR zone) + `repeat` group (HR recovery + power work) + `cooldown` (HR zone)
83
+ - Tempo: `warmup` + `repeat` group (work + recovery) + `cooldown`
84
+ - Swim: `lap.button` + `description` per step, `rest` steps between sections
85
+ - Multisport: `garmin.legs` array, each leg has `sport` + `steps`
86
+ - Reusable sessions: YAML anchors in `swim_sessions:` / `track_sessions:` blocks
87
+
88
+ ## Periodization Principles
89
+
90
+ - **Progressive overload:** +5-10% volume/week, recovery week every 3-4 weeks
91
+ - **Polarized intensity:** ~80% easy (RPE 4-5), ~20% hard (RPE 7-9), minimal grey zone
92
+ - **Specificity:** Training mirrors race demands as plan progresses
93
+ - **Taper:** 2-3 weeks before race, -30-50% volume, maintain intensity
94
+ - **Rest sequencing:** Rest day after long/hard sessions. Easy before hard, rest after hard/long.
95
+ - **Phase structure:** Base (aerobic) -> Build (race-specific) -> Peak (sharpening) -> Taper
96
+
97
+ ## Race Strategy and Execution Priorities
98
+
99
+ **Race outcome is determined more by execution discipline than by training. Race-day decisions are not equal weight.** When advising on race week, lead with the single rule that decides 80% of the outcome: **HR ceiling discipline**.
100
+
101
+ ### Priority order (by impact on finishing time)
102
+
103
+ 1. **HR ceiling discipline** (saves or costs 5–15 min) — set a hard cap below LTHR, hold it religiously until the release km, push only in the final stretch
104
+ 2. **Pacing strategy first 5 km** (saves or costs 3–8 min — tied to #1) — let people pass you, ignore corral pressure, run by HR not by feel
105
+ 3. **Fueling** (saves or costs 2–5 min in late race) — gel timing aligned with aid stations, tested in training
106
+ 4. **Kit / chafe management** (saves or costs 1–3 min from comfort) — singlet, socks, shorts all tested
107
+ 5. **Hydration** (saves or costs 1–2 min) — sip at every aid station from km 8+, pour on head if warm
108
+ 6. **Carb load** (saves or costs 1–2 min) — single solid carb-load day before race for HM, longer for marathon
109
+ 7. **Sleep** (background factor) — bank sleep Wed/Thu; Friday night sleep matters less than people think
110
+ 8. **Course strategy details** (saves or costs <1 min in good conditions) — useful color, not the main lever
111
+
112
+ ### HR ceiling rule (the headline)
113
+
114
+ - **HM:** HR cap ≈ LTHR − 9 to −12 bpm. Hold until km 16 (last 5 km). Then lift cap, push by feel.
115
+ - **Marathon:** HR cap ≈ LTHR − 16 to −20 bpm. Hold until km 32 (last 10 km). Lift cap, but stay below LTHR until last 5 km.
116
+ - **10K:** HR cap ≈ LTHR − 3 to −5 bpm. Hold until km 7, push.
117
+ - **5K:** HR cap doesn't really apply — run by feel.
118
+
119
+ Race-day HR for the same pace typically runs 5–8 bpm higher than training HR. Plan for this. The training-day "5:00/km at HR 168" becomes race-day "5:00/km at HR 173–176."
120
+
121
+ **Time-to-failure at intensities near threshold:**
122
+ - At LTHR: ~60 minutes sustainable
123
+ - LTHR + 5 bpm: ~30 minutes
124
+ - LTHR + 10 bpm: ~10 minutes
125
+
126
+ This is why HM at threshold = bonk-by-km-15. Run 5+ bpm below threshold and time-to-failure extends to 3+ hours.
127
+
128
+ ### `race_strategy` YAML field
129
+
130
+ For any `race` type workout, include a `race_strategy:` block. Renderers display this prominently for race week:
131
+
132
+ ```yaml
133
+ - day: 6
134
+ type: "race"
135
+ name: "RACE: Brooklyn Half Marathon"
136
+ skip_garmin: true
137
+ description: "Race description, course notes, etc."
138
+ race_strategy:
139
+ hr_cap: 165
140
+ cap_release_km: 16
141
+ one_liner: "Stay below HR 165 until km 16. Then push."
142
+ notes: |
143
+ Hilly first 5 km (Prospect Park). Don't chase corral pace.
144
+ Race-day HR runs 5-8 bpm above training HR — recalibrate in moment.
145
+ Fueling: 2 SiS gels at aid stations after km 5 and km 14.
146
+ ```
147
+
148
+ **Required fields:**
149
+ - `hr_cap` (int): the HR ceiling for the body of the race
150
+ - `cap_release_km` (int): km marker where the ceiling lifts
151
+ - `one_liner` (str): the one rule to remember on race day
152
+
153
+ **Optional fields:**
154
+ - `notes` (str): course-specific context (hills, weather, fueling reminders)
155
+ - `target_time` (str): for context, not for pacing — e.g., `"1:45"`
156
+ - `target_pace` (str): goal pace if HR cap allows — e.g., `"5:05/km"` (HR governs, not pace)
157
+
158
+ ### What NOT to lead with in race-week conversations
159
+
160
+ These are real considerations but should never displace the HR ceiling rule:
161
+ - Gel brand/timing minutiae
162
+ - Carb load gram counts
163
+ - Breakfast composition
164
+ - Kit selection
165
+ - Course-guide course-by-course narrative
166
+
167
+ Cover them, but make the HR rule the headline of every race-week conversation.
168
+
169
+ ## Pace Conversion
170
+
171
+ Garmin pace values are sec/km. Convert: `5:25/km = 325 sec/km`.
172
+
173
+ To min/mi: multiply sec/km by 1.60934. Example: 325 sec/km = 523 sec/mi = 8:43/mi.
174
+
175
+ ## Validation
176
+
177
+ Always run `make test` after changes. It validates YAML syntax, Python, and plan structure (day ranges, training_days counts).
@@ -0,0 +1,18 @@
1
+ # Unit system (metric or imperial). Also sets paper format (a4 or letter).
2
+ UNITS=metric
3
+
4
+ # Plan file (set after running /paicer:create-plan)
5
+ # PLAN=plans/my-plan.yaml
6
+
7
+ # Override paper format if your printer doesn't match your unit system.
8
+ # FORMAT=letter
9
+
10
+ # Pool swim distance tracking:
11
+ # auto — watch measures distance via stroke detection (needs good form)
12
+ # drill — each segment is a drill; you enter distance after each lap button press
13
+ # SWIM_TRACKING=auto
14
+
15
+ # Workout integration (uncomment if using Garmin)
16
+ # WORKOUT_INTEGRATION=garmin
17
+ # GARMIN_EMAIL=your-email@example.com
18
+ # GARMIN_PASSWORD=your-password-here
@@ -0,0 +1,37 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Verify tag matches pyproject.toml version
18
+ run: |
19
+ TAG="${{ github.event.release.tag_name }}"
20
+ VERSION=$(grep '^version' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
21
+ if [ "$TAG" != "$VERSION" ]; then
22
+ echo "Release tag '$TAG' does not match pyproject.toml version '$VERSION'"
23
+ exit 1
24
+ fi
25
+
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.12"
29
+
30
+ - name: Install build tools
31
+ run: pip install hatchling build
32
+
33
+ - name: Build package
34
+ run: python -m build
35
+
36
+ - name: Publish to PyPI
37
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,38 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+
8
+ # Virtual environments
9
+ .venv/
10
+ venv/
11
+ ENV/
12
+
13
+ # Generated files
14
+ output/
15
+
16
+ # User plans (generated by /paicer:create-plan)
17
+ plans/
18
+
19
+ # Env files
20
+ .env
21
+
22
+ # Garmin tokens
23
+ .garmin_tokens
24
+
25
+ # yaks data directory
26
+ .yaks
27
+
28
+
29
+ # IDE
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,58 @@
1
+ # Changelog
2
+
3
+ ## 2026-03-15
4
+
5
+ - Add training status to review data (VO2max, load balance, acute-to-chronic ratio)
6
+ - Add HR time in zones and aerobic training effect to review data
7
+ - Add elevation gain/loss to review data and skill
8
+ - Update review skill to interpret all new metrics
9
+ - Fix inaccurate distance totals in tempo workout descriptions
10
+ - Smooth long run progression (12 → 14 → 15 → 17 → 16 → 18 → 19 → 18 → 20 → 19 → 16 → Race)
11
+ - Add strides to W4, W5, W8, W10, W11 (every non-race week now has strides)
12
+ - Add introductory comment to hm-tri-combo.yaml example
13
+
14
+ ## 2026-03-14
15
+
16
+ - Fix advanced example plan (hm-tri-combo): remove duplicate workouts, correct step ordering
17
+
18
+ ## 2026-03-12
19
+
20
+ - Fix plan formatting across all example plans (descriptions, date displays, en dashes)
21
+
22
+ ## 2026-03-11
23
+
24
+ - Extract plan-authoring knowledge into shared skill (used by both create-plan and review-progress)
25
+ - Rename commands to `paicer/create-plan` and `paicer/review-progress`
26
+ - Fix hm-tri phase 1 training day assignments
27
+
28
+ ## 2026-03-10
29
+
30
+ - Add imperial unit system support (pace in min/mile, paper format switches to letter)
31
+ - Add optional workout support (`skip_garmin: true` for strength/rest days)
32
+ - Add sport labels (emoji in HTML, text in Markdown) and plan validation
33
+ - Add imperial reference example plan
34
+ - Update README
35
+
36
+ ## 2026-03-08
37
+
38
+ - Add coaching commands: `/paicer:create-plan` and `/paicer:review-progress`
39
+ - Add `review_data.py` for pulling Garmin activity data by plan week
40
+ - Add Garmin API reference docs
41
+ - Add CLAUDE.md project instructions
42
+ - Move plans to `examples/` directory
43
+
44
+ ## 2026-03-04
45
+
46
+ - Add multisport (brick) workout support for Garmin
47
+ - Add track session support with reusable YAML anchors
48
+
49
+ ## 2026-03-03
50
+
51
+ - Add cycling and swimming workout types
52
+ - Expand hm-tri-combo plan with full Phase 2 (swim/bike integration)
53
+
54
+ ## 2026-02-22
55
+
56
+ - Initial release: YAML plan parser, Markdown/HTML renderers, Garmin workout sync
57
+ - Run-only support with tempo, interval, and easy run structures
58
+ - Scope filtering (phase, week, day)
paicer-0.1.0/CLAUDE.md ADDED
@@ -0,0 +1,42 @@
1
+ # Paicer
2
+
3
+ Training plan tool: YAML plan → Garmin workouts + Markdown/HTML documents.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ src/
9
+ render_plan.py — YAML → Markdown/HTML (entry point)
10
+ generate_workouts.py — YAML → Garmin workouts with filtering
11
+ plan_utils.py — Date calculation, YAML loading, plan validation, sport maps
12
+ formatters/ — Base class + MarkdownFormatter + HTMLFormatter
13
+ integrations/ — Base class + GarminIntegration
14
+ examples/ — Example training plans
15
+ plans/ — User plans (gitignored, created by /paicer:create-plan)
16
+ output/ — Generated documents (gitignored)
17
+ docs/ — Garmin API reference, other docs
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ ```bash
23
+ make markdown # Generate Markdown
24
+ make html # Generate HTML (A4 for metric, letter for imperial)
25
+ make workouts SCOPE=w7 # Sync week 7 to Garmin
26
+ make workouts SCOPE=w7d2 # Sync week 7 day 2
27
+ make workouts SCOPE=p2 # Sync phase 2
28
+ make workouts SCOPE=all # Sync everything
29
+ make test # Validate YAML, Python, and plan structure
30
+ ```
31
+
32
+ Plan path is set in `.env` as `PLAN=plans/your-plan.yaml`.
33
+
34
+ Set `UNITS=metric` or `UNITS=imperial` in `.env` to control the unit system used when creating or editing plans. Garmin steps always use metric internally. Text fields (`name`, `description`) and YAML comments use the preferred system.
35
+
36
+ ## Plan Authoring
37
+
38
+ Invoke the `plan-authoring` skill for YAML structure rules, workout types, Garmin patterns, unit conventions, and periodization principles.
39
+
40
+ Reference plans: `examples/reference-metric.yaml` and `examples/reference-imperial.yaml` demonstrate every pattern in a minimal 2-week plan.
41
+
42
+ Garmin API: `docs/garmin-api.md` for step types, end conditions, target types, pace conversions.
paicer-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tome Cvitan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
paicer-0.1.0/Makefile ADDED
@@ -0,0 +1,52 @@
1
+ .PHONY: install test workouts markdown html all clean
2
+
3
+ -include .env
4
+ export
5
+
6
+ UNITS ?= metric
7
+ FORMAT ?= $(if $(filter imperial,$(UNITS)),letter,a4)
8
+ SCHEDULE ?= 1
9
+
10
+ define check_plan
11
+ @if [ -z "$(PLAN)" ]; then \
12
+ echo "No plan set. Set PLAN in .env or run /paicer:create-plan"; \
13
+ exit 1; \
14
+ fi
15
+ @if [ ! -f "$(PLAN)" ]; then \
16
+ echo "Plan file not found: $(PLAN)"; \
17
+ exit 1; \
18
+ fi
19
+ endef
20
+
21
+ install:
22
+ uv sync
23
+
24
+ test:
25
+ $(check_plan)
26
+ @echo "Running tests..."
27
+ @uv run pytest -v
28
+ @echo "Validating plan..."
29
+ @uv run paicer render --plan $(PLAN) > /dev/null
30
+ @echo "✅ Plan valid"
31
+
32
+ workouts:
33
+ $(check_plan)
34
+ @uv run paicer sync $(SCOPE) --plan $(PLAN) $(if $(filter 0,$(SCHEDULE)),--no-schedule)
35
+
36
+ markdown:
37
+ $(check_plan)
38
+ @mkdir -p output
39
+ @uv run paicer render --plan $(PLAN) > output/training_plan.md
40
+ @echo "Created output/training_plan.md"
41
+
42
+ html:
43
+ $(check_plan)
44
+ @mkdir -p output
45
+ @uv run paicer render --html --format=$(FORMAT) --plan $(PLAN) > output/training_plan.html
46
+ @echo "Created output/training_plan.html ($(FORMAT))"
47
+
48
+ all: markdown html
49
+
50
+ clean:
51
+ trash output/
52
+ trash __pycache__
paicer-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: paicer
3
+ Version: 0.1.0
4
+ Summary: Training plan tool: YAML to Garmin workouts and readable documents
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: click>=8.0
8
+ Requires-Dist: garminconnect<0.3.0,>=0.2.40
9
+ Requires-Dist: keyring>=24.0
10
+ Requires-Dist: markdown>=3.10.2
11
+ Requires-Dist: pyyaml>=6.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # p**ai**cer
15
+
16
+ AI-powered training plan manager. Provide your race goals, schedule, and fitness level to create a plan, with structured workouts that can be synced to your Garmin watch. After each training week, run the progress review command to pull your Garmin activity and training status data, compare it against the plan, and adjust targets based on how your body is responding.
17
+
18
+ **Disclaimer:** This tool is not a substitute for professional coaching or medical advice. Always listen to your body and consult a qualified professional for health or injury concerns.
19
+
20
+ ## Get Started
21
+
22
+ ```bash
23
+ git clone https://github.com/cvitan/paicer && cd paicer
24
+ ```
25
+
26
+ Open the project in Claude Code and run `/paicer:create-plan` to create a YAML-based training plan through a guided conversation. It handles setup, configuration, and walks you through the process.
27
+
28
+ After each week of training, run `/paicer:review-progress` to review your plan progress and make any tweaks if needed. The review will also be appended to your plan for future reference.
29
+
30
+ ### Plan output options
31
+ - **Markdown**
32
+ - **HTML** — set up to print 1 wk/page
33
+ - **Garmin** — sync scheduled structured workouts
34
+
35
+ ## Commands
36
+
37
+ ```bash
38
+ make markdown # Generate Markdown
39
+ make html # Generate HTML (A4 for metric, letter for imperial)
40
+ make workouts SCOPE=w7 # Sync week 7 to Garmin
41
+ make workouts SCOPE=w7d2 # Sync specific workout
42
+ make workouts SCOPE=p2 # Sync entire phase
43
+ make test # Validate plan
44
+ ```
45
+
46
+ ## Supported Sports
47
+
48
+ Running, cycling, swimming (pool and open water), track sessions, and multisport/brick workouts (bike + run with transition tracking). Requires a Garmin watch — multisport needs a compatible model (Fenix, Forerunner 570/970, Enduro).
49
+
50
+ ## Roadmap
51
+
52
+ 1. **Strava activity enrichment**
53
+ 2. **Zwift Integrations**
54
+ 3. **Additional Formats** - PDF, iCal, JSON, CSV export
55
+ 4. **HR-zone fallback for cycling** - generate bike workouts with `heart.rate.zone` targets for users without a power meter (current plans assume one)
56
+ 5. **Ad-hoc activity data tool** - extend `src/review_data.py` or add `src/activity_data.py` with `--activity-id`, `--latest N`, `--from-date/--to-date` flags so single-activity / cross-week queries don't require throwaway scripts. Expose `get_activity_typed_splits` as the primary intervals fetch.
paicer-0.1.0/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # p**ai**cer
2
+
3
+ AI-powered training plan manager. Provide your race goals, schedule, and fitness level to create a plan, with structured workouts that can be synced to your Garmin watch. After each training week, run the progress review command to pull your Garmin activity and training status data, compare it against the plan, and adjust targets based on how your body is responding.
4
+
5
+ **Disclaimer:** This tool is not a substitute for professional coaching or medical advice. Always listen to your body and consult a qualified professional for health or injury concerns.
6
+
7
+ ## Get Started
8
+
9
+ ```bash
10
+ git clone https://github.com/cvitan/paicer && cd paicer
11
+ ```
12
+
13
+ Open the project in Claude Code and run `/paicer:create-plan` to create a YAML-based training plan through a guided conversation. It handles setup, configuration, and walks you through the process.
14
+
15
+ After each week of training, run `/paicer:review-progress` to review your plan progress and make any tweaks if needed. The review will also be appended to your plan for future reference.
16
+
17
+ ### Plan output options
18
+ - **Markdown**
19
+ - **HTML** — set up to print 1 wk/page
20
+ - **Garmin** — sync scheduled structured workouts
21
+
22
+ ## Commands
23
+
24
+ ```bash
25
+ make markdown # Generate Markdown
26
+ make html # Generate HTML (A4 for metric, letter for imperial)
27
+ make workouts SCOPE=w7 # Sync week 7 to Garmin
28
+ make workouts SCOPE=w7d2 # Sync specific workout
29
+ make workouts SCOPE=p2 # Sync entire phase
30
+ make test # Validate plan
31
+ ```
32
+
33
+ ## Supported Sports
34
+
35
+ Running, cycling, swimming (pool and open water), track sessions, and multisport/brick workouts (bike + run with transition tracking). Requires a Garmin watch — multisport needs a compatible model (Fenix, Forerunner 570/970, Enduro).
36
+
37
+ ## Roadmap
38
+
39
+ 1. **Strava activity enrichment**
40
+ 2. **Zwift Integrations**
41
+ 3. **Additional Formats** - PDF, iCal, JSON, CSV export
42
+ 4. **HR-zone fallback for cycling** - generate bike workouts with `heart.rate.zone` targets for users without a power meter (current plans assume one)
43
+ 5. **Ad-hoc activity data tool** - extend `src/review_data.py` or add `src/activity_data.py` with `--activity-id`, `--latest N`, `--from-date/--to-date` flags so single-activity / cross-week queries don't require throwaway scripts. Expose `get_activity_typed_splits` as the primary intervals fetch.