@wattsonclub/wattson-cli 0.1.0 → 0.1.1

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.
@@ -1,375 +1,280 @@
1
- > Command examples inherited from the compatibility CLI. Current offline app behavior is described in ../SKILL.md; legacy scheduling/session calls do not remotely control the app.
1
+ # Wattson CLI command reference
2
2
 
3
- # Planner CLI examples
3
+ Current offline workout/ride workflow. [Human guide](https://wattson.club/cli) · [Agent discovery](https://wattson.club/llms.txt).
4
4
 
5
- This file exists to make the intended agent/operator flow concrete.
5
+ ## Install and authenticate
6
6
 
7
- Design rule:
8
- - natural language belongs in the agent layer
9
- - structured inputs belong in planner-api / planner-cli
10
- - prefer deterministic request bodies over special `...from-prompt` endpoints
11
-
12
- Operator defaults:
13
- - use `wattson --help` to inspect the live command surface
14
- - add `--json` when the output is meant for automation or another agent step
15
- - add `--base-url <url>` when you are pointing at a non-default planner-api instance
16
-
17
- ## 0. Sanity-check the surface
7
+ Requires Node.js 22+. Shell examples use Bash; JSON transformations require `jq`.
18
8
 
19
9
  ```bash
10
+ npm install -g @wattsonclub/wattson-cli
11
+ wattson --help
20
12
  wattson health
21
- wattson --json health
22
- wattson --base-url http://192.168.1.50:3000 health
13
+ wattson auth login
14
+ # Alternatively, on SSH/server/container/agent runtime:
15
+ wattson auth login --remote
16
+ wattson --json auth status
17
+ wattson --json athlete profile
23
18
  ```
24
19
 
25
- ## 1. Recommend a workout for today
20
+ Choose one login flow. Local login uses OAuth authorization code + PKCE and browser callback `http://127.0.0.1:8976/oauth/callback`. Remote login uses device authorization: verification URL and short-lived code appear on stderr; the CLI waits for approval on the user's phone/computer. No inbound port or SSH tunnel is required. Both request `planner:read planner:write`; there is no scope-selection flag. `--timeout SECONDS` overrides the five-minute wait (remote authorization may expire sooner). Denial/timeout is not authentication. Deliver URLs/codes privately to the requester; never approve on their behalf, request passwords, or copy cookies/tokens.
26
21
 
27
- ```bash
28
- wattson recommend daily athlete-1 \
29
- --available-time-min 20 \
30
- --prefer-objective vo2max \
31
- --hard-constraint "indoor only"
32
- ```
22
+ API requests refresh on HTTP 401 and retry once, persisting replacement credentials. A failed refresh-token request removes saved state and requires login again. `auth status` checks authenticated bootstrap; signed-out status exits 1, including JSON mode. JSON auth output excludes tokens but includes identity metadata.
33
23
 
34
- ## 2. Draft a workout from simple flags
24
+ Credentials are unencrypted JSON, written atomically with file mode `0600`; newly created parent directories use `0700`. Unix default: `$XDG_STATE_HOME/trainer-planner/auth.json` or `~/.local/state/trainer-planner/auth.json`. Windows: `%LOCALAPPDATA%/trainer-planner/auth.json`, falling back under the user's AppData/Local. `WATTSON_AUTH_FILE`, then legacy `PLANNER_CLI_AUTH_FILE`, override the path. Keep it private and persistent; do not print/commit credentials or bake them into images. Stored credentials are matched to the API base URL.
35
25
 
36
- ```bash
37
- wattson workout draft athlete-1 \
38
- --objective vo2max \
39
- --duration-min 20 \
40
- --execution-mode erg \
41
- --style-hint "3 high intensity intervals"
42
- ```
26
+ `wattson auth logout` attempts `/auth/logout` with saved refresh token and bearer credentials, then deletes the local file even if the server call fails. This is **not** a guarantee of OAuth-token revocation or logout of other sessions.
43
27
 
44
- This is the intended translation pattern for agents:
28
+ ## Global behavior and supported families
45
29
 
46
- - user says: `Create me a 20 min session with 3 high intensity intervals`
47
- - agent infers:
48
- - objective: `vo2max`
49
- - durationMin: `20`
50
- - allowedExecutionModes: `["erg"]`
51
- - styleHints: `["3 high intensity intervals"]`
52
- - agent calls `workout draft`
30
+ Use `wattson [--base-url URL] [--json] COMMAND`. API precedence: `--base-url`, `WATTSON_API_BASE_URL`, legacy `PLANNER_API_BASE_URL`, default `https://api.wattson.club`. Override only for an intentionally selected environment. Omit optional athlete IDs for the signed-in athlete; explicit IDs are not permission to access others.
53
31
 
54
- ## 3. Draft a workout from a full JSON body
32
+ `--json` returns the API response, not a universal wrapper. Errors go to stderr; check exit codes. Successful transport for `workout validate` can exit 0 with `validation.status: "fail"`.
55
33
 
56
- ```bash
57
- wattson workout draft --input-file /tmp/draft-workout.json
58
- ```
34
+ | Family | Supported commands |
35
+ | --- | --- |
36
+ | Auth | `auth login [--remote] [--timeout SECONDS]`, `auth status`, `auth logout` |
37
+ | Service | `health` |
38
+ | Athlete | `athlete profile [athleteId]`, `athlete readiness latest [athleteId] [--as-of ISO]`, `athlete sessions [athleteId] [--from DATE] [--to DATE] [--limit N]` |
39
+ | Recommendation | `recommend daily [athleteId] --available-time-min N [--date DATE] [--prefer-objective OBJECTIVE] [--hard-constraint TEXT ...]` |
40
+ | Authoring | `workout draft`, `workout revise`, `workout validate`, `workout save` (below) |
41
+ | Workout reads | `workout get ID [--version N]`, `workout revisions ID`, `workout runtime-plan ID --version N` |
42
+ | Ride reads/exports | `session summary ID`, `session telemetry ID`, `session artifacts ID`, `session fit ID` |
43
+ | External publication | `session strava-sync ID` (authorized, asynchronous) |
44
+ | Library/calendar | `workout list [athleteId] [--limit N]`, `workout schedule ID --date YYYY-MM-DD`, `workout unschedule ID`, `workout retire ID` |
45
+ | Courses/discovery/sharing/research | See the full MCP mapping below; structured operations accept `--input-file PATH` or `--input-json JSON`. |
59
46
 
60
- Example `/tmp/draft-workout.json`:
61
-
62
- ```json
63
- {
64
- "athleteId": "athlete-1",
65
- "objective": "vo2max",
66
- "durationMin": 20,
67
- "constraints": {
68
- "allowedExecutionModes": ["erg"],
69
- "maxPctFtp": 1.2
70
- },
71
- "context": {
72
- "styleHints": ["3 high intensity intervals"]
73
- }
74
- }
75
- ```
47
+ The CLI covers the current MCP action and research tools. Strava account connection remains in the app, not in either tool surface. Strava availability depends on server access mode. Course imports return a course profile; they do not save or schedule a workout.
76
48
 
77
- Note:
78
- - FTP-relative values are fractional in structured payloads
79
- - `maxPctFtp: 1.2` means `120% FTP`
80
- - `target: { "mode": "pct_ftp", "value": 0.55 }` means `55% FTP`
81
- - never pass whole percentages like `120` or `55`
49
+ ## Inspect, draft, validate, save, verify
82
50
 
83
- ## 4. Revise a workout from a structured patch
51
+ Read profile/readiness before choosing targets. Recommendations are advice, not saved workouts.
84
52
 
85
53
  ```bash
86
- wattson workout revise --input-file /tmp/revise-workout.json
87
- ```
88
-
89
- Example `/tmp/revise-workout.json`:
90
-
91
- ```json
92
- {
93
- "patch": {
94
- "workoutId": "workout-1",
95
- "baseVersion": 2,
96
- "preserve": ["objective", "interval_structure"],
97
- "changes": {
98
- "durationDeltaSec": -120,
99
- "intensityMultiplier": 0.95,
100
- "addCadenceFocus": false,
101
- "removeLastInterval": false,
102
- "switchToRecovery": false
103
- },
104
- "naturalLanguage": "Keep the same shape, just make it slightly easier and a bit shorter.",
105
- "reason": "Rider requested a lighter version."
106
- }
107
- }
54
+ wattson --json athlete profile
55
+ wattson --json athlete readiness latest
56
+ wattson --json recommend daily --available-time-min 20 --prefer-objective vo2max
108
57
  ```
109
58
 
110
- This is the intended translation pattern for agents:
111
- - user says: `make it slightly easier`
112
- - agent chooses a structured patch like:
113
- - `intensityMultiplier: 0.95`
114
- - maybe preserve objective / interval structure
115
- - include the original natural-language request as trace context when helpful
116
-
117
- ## 5. Validate a workout before saving
59
+ Run in a private working directory, signed in, for a request authorizing library creation. This sequence stops on errors and requires validation `pass` before constructing the save request. Review the draft and validation issues before the save step.
118
60
 
119
61
  ```bash
120
- wattson workout validate --input-file /tmp/validate-workout.json
62
+ set -euo pipefail
63
+ umask 077
64
+ wattson --json workout draft --objective vo2max --duration-min 20 --execution-mode erg --style-hint "3 hard intervals" > draft.json
65
+ # Draft output: {workout, validationPreview?}. Validate input: {workout}.
66
+ jq -e '{workout: .workout} | select(.workout != null)' draft.json > validate.json
67
+ wattson --json workout validate --input-file validate.json > validation.json
68
+ jq -e '.validation.status == "pass"' validation.json >/dev/null
69
+ # Validation output has no workout. Preserve the original definition.
70
+ jq -e '{workout: .workout, expectedValidationStatus: "pass"}' validate.json > save.json
71
+ wattson --json workout save --input-file save.json > saved.json
72
+ workout_id=$(jq -er '.workoutId' saved.json)
73
+ workout_version=$(jq -er '.version' saved.json)
74
+ wattson --json workout get "$workout_id" --version "$workout_version"
75
+ wattson --json workout runtime-plan "$workout_id" --version "$workout_version"
121
76
  ```
122
77
 
123
- Example `/tmp/validate-workout.json`:
124
-
125
- ```json
126
- {
127
- "workout": {
128
- "id": "workout-1",
129
- "version": 2,
130
- "createdAt": "2026-04-09T00:00:00.000Z",
131
- "updatedAt": "2026-04-09T00:00:00.000Z",
132
- "createdBy": { "type": "system", "system": "planner_api" },
133
- "updatedBy": { "type": "system", "system": "planner_api" },
134
- "kind": "workout_definition",
135
- "athleteId": "athlete-1",
136
- "title": "VO2 20",
137
- "objective": "vo2max",
138
- "sport": "cycling_indoor",
139
- "status": "draft",
140
- "athleteContext": {
141
- "ftpWatts": 250,
142
- "constraints": []
143
- },
144
- "blocks": [
145
- {
146
- "id": "block-1",
147
- "type": "steady",
148
- "durationSec": 300,
149
- "target": { "mode": "pct_ftp", "value": 0.55 },
150
- "instructions": ["Warm up"]
151
- }
152
- ],
153
- "constraints": {
154
- "allowedExecutionModes": ["erg"]
155
- },
156
- "rationale": {
157
- "summary": "Short VO2-focused session",
158
- "whyToday": ["Fits available time"]
159
- },
160
- "tags": ["vo2", "short"]
161
- }
162
- }
163
- ```
78
+ For `warn`, review the issues and deliberately use the observed expected status only if acceptable; for `fail`, fix and revalidate. Never merely relabel a workout as validated. Save recomputes validation: `expectedValidationStatus` is a conflict guard. Passing saves persist as `validated` even if input status was `draft`. Save returns `{workoutId, version, status}`, not `{workout}`.
164
79
 
165
- ## 6. Save a validated workout
80
+ Draft flags also support repeatable `--execution-mode` and `--style-hint`, `--max-pct-ftp`, and `--readiness-snapshot-id`. Structured draft/revise/validate/save input uses **one** of `--input-file` or `--input-json`:
166
81
 
167
82
  ```bash
168
- wattson workout save --input-file /tmp/save-workout.json
83
+ wattson --json workout draft --input-json '{"objective":"vo2max","durationMin":20,"constraints":{"allowedExecutionModes":["erg"],"maxPctFtp":1.2},"context":{"styleHints":["3 hard intervals"]}}'
169
84
  ```
170
85
 
171
- Example `/tmp/save-workout.json`:
172
-
173
- ```json
174
- {
175
- "workout": {
176
- "id": "workout-1",
177
- "version": 2,
178
- "createdAt": "2026-04-09T00:00:00.000Z",
179
- "updatedAt": "2026-04-09T00:00:00.000Z",
180
- "createdBy": { "type": "system", "system": "planner_api" },
181
- "updatedBy": { "type": "system", "system": "planner_api" },
182
- "kind": "workout_definition",
183
- "athleteId": "athlete-1",
184
- "title": "VO2 20",
185
- "objective": "vo2max",
186
- "sport": "cycling_indoor",
187
- "status": "validated",
188
- "athleteContext": {
189
- "ftpWatts": 250,
190
- "constraints": []
191
- },
192
- "blocks": [
193
- {
194
- "id": "block-1",
195
- "type": "steady",
196
- "durationSec": 300,
197
- "target": { "mode": "pct_ftp", "value": 0.55 },
198
- "instructions": ["Warm up"]
199
- }
200
- ],
201
- "constraints": {
202
- "allowedExecutionModes": ["erg"]
203
- },
204
- "rationale": {
205
- "summary": "Short VO2-focused session",
206
- "whyToday": ["Fits available time"]
207
- },
208
- "tags": ["vo2", "short"]
209
- },
210
- "expectedValidationStatus": "pass"
211
- }
212
- ```
86
+ Signed-in structured drafts may omit `athleteId`. FTP values are fractions: `1.2` = 120%, `0.55` = 55%. See [building blocks](workout-building-blocks.md) and [workout text](workout-text-format.md). Preserve returned IDs, version, attribution, athlete context and blocks; don't invent a partial workout definition.
213
87
 
214
- The same `%FTP` rule applies here:
215
- - use fractional values like `0.55` and `1.2`
216
- - do not send whole percentages like `55` or `120`
88
+ ### Revise a saved version
217
89
 
218
- ## 7. Schedule a saved workout
90
+ Using `saved.json` above, only for an authorized edit (retain `set -euo pipefail`):
219
91
 
220
92
  ```bash
221
- wattson plan schedule athlete-1 \
222
- --workout-id workout-1 \
223
- --version 2 \
224
- --date 2026-04-10
93
+ jq '{patch: {workoutId: .workoutId, baseVersion: .version, preserve: ["objective", "interval_structure"], changes: {intensityMultiplier: 0.95}, reason: "Rider requested a slightly easier version."}}' saved.json > revise.json
94
+ wattson --json workout revise --input-file revise.json > revised.json
95
+ jq -e '{workout: .workout} | select(.workout != null)' revised.json > validate.json
96
+ wattson --json workout validate --input-file validate.json > validation.json
97
+ jq -e '.validation.status == "pass"' validation.json >/dev/null
98
+ jq --slurpfile prior saved.json '{workout: .workout, expectedValidationStatus: "pass", expectedLatestVersion: $prior[0].version}' validate.json > save.json
99
+ wattson --json workout save --input-file save.json > revised-saved.json
100
+ wattson --json workout get "$(jq -er '.workoutId' revised-saved.json)" --version "$(jq -er '.version' revised-saved.json)"
101
+ wattson --json workout revisions "$(jq -er '.workoutId' revised-saved.json)"
225
102
  ```
226
103
 
227
- Note:
228
- - the CLI takes `athleteId` here and hides the current HTTP `planId` convention (`<athleteId>:current-plan`)
104
+ `revise` returns `{workout, diff}`; it does not save. `expectedLatestVersion` guards concurrent edits; on conflict, fetch latest and reconsider. Other patch changes: `newTitle`, `newObjective`, `durationDeltaSec`, `addCadenceFocus`, `removeLastInterval`, `switchToRecovery`. Translate natural language into structured intent.
105
+
106
+ ## Offline app and uploaded rides
229
107
 
230
- ## 8. Inspect the compiled runtime plan
108
+ Report **saved to your library, ready for the app to download**. The app downloads workouts; the rider selects and executes them locally. A suspended/offline app syncs when online again. CLI success is not device acknowledgement, ride start or completion. Never fabricate a session to make a workflow look finished.
231
109
 
232
110
  ```bash
233
- wattson workout runtime-plan workout-1 --version 2
111
+ wattson --json athlete sessions --limit 10
112
+ # Replace SESSION_ID with a real uploaded session ID.
113
+ wattson --json session summary SESSION_ID
114
+ wattson --json session telemetry SESSION_ID
115
+ wattson --json session artifacts SESSION_ID
234
116
  ```
235
117
 
236
- ## 9. Queue the saved workout into an active trainer session
118
+ Telemetry can be `{ "telemetry": null }` when not persisted. Artifacts returns metadata without inline FIT bytes. Treat ride/health data as private; retrieve/export within the user's request.
237
119
 
238
- ```bash
239
- wattson session load-workout \
240
- --athlete-id athlete-1 \
241
- --workout-id workout-1 \
242
- --version 2
243
- ```
120
+ ### FIT export
244
121
 
245
- ## 10. Complete a session from simple flags
122
+ `session fit` returns `fitFile` with inline `contentBase64`, filename, MIME type, byte count and SHA-256. It does not write a FIT file. If export is requested:
246
123
 
247
124
  ```bash
248
- wattson session complete session-123 \
249
- --status completed \
250
- --completion-kind completed \
251
- --elapsed-sec 1200 \
252
- --completed-duration-sec 1200 \
253
- --compliance-pct 98 \
254
- --note "Strong finish"
125
+ wattson --json session fit SESSION_ID > session-fit.json
126
+ node --input-type=module <<'NODE'
127
+ import { readFileSync, writeFileSync } from 'node:fs';
128
+ import { createHash } from 'node:crypto';
129
+ const { fitFile } = JSON.parse(readFileSync('session-fit.json', 'utf8'));
130
+ const bytes = Buffer.from(fitFile.contentBase64, 'base64');
131
+ if (bytes.length !== fitFile.sizeBytes || createHash('sha256').update(bytes).digest('hex') !== fitFile.checksumSha256.toLowerCase()) {
132
+ throw new Error('FIT size/checksum mismatch');
133
+ }
134
+ writeFileSync('ride.fit', bytes, { mode: 0o600, flag: 'wx' });
135
+ NODE
255
136
  ```
256
137
 
257
- ## 11. Complete a session from a structured JSON body
138
+ ### Strava publication/retry — only when authorized
258
139
 
259
140
  ```bash
260
- wattson session complete session-123 --input-file /tmp/complete-session.json
141
+ wattson --json session strava-sync SESSION_ID
142
+ wattson --json session summary SESSION_ID
143
+ wattson --json session artifacts SESSION_ID
261
144
  ```
262
145
 
263
- Example `/tmp/complete-session.json`:
264
-
265
- ```json
266
- {
267
- "status": "completed",
268
- "completionKind": "completed",
269
- "startedAt": "2026-04-09T10:00:00.000Z",
270
- "endedAt": "2026-04-09T10:20:00.000Z",
271
- "executionMode": "erg",
272
- "liveSummary": {
273
- "elapsedSec": 1200
274
- },
275
- "actualMetrics": {
276
- "avgPower": 245,
277
- "avgHrBpm": 158,
278
- "maxHrBpm": 173,
279
- "compliancePct": 98,
280
- "completedDurationSec": 1200,
281
- "completedTss": 42
282
- },
283
- "notes": ["Strong finish"]
284
- }
285
- ```
146
+ This enqueues/retries an asynchronous backend operation (HTTP 202). It requires the relevant Strava connection; the CLI has no account-connect command. Inspect `session.externalSync.strava` on later session summaries for status, activity URL or errors. Report pending until the backend confirms delivery; FIT export or HTTP 202 is not proof of Strava publication. Avoid tight polling.
286
147
 
287
- ## 12. Apply a bounded live adaptation
148
+ ## Library calendar and courses
288
149
 
289
150
  ```bash
290
- wattson session adapt session-123 \
291
- --op skip_interval \
292
- --reason "Rider requested it" \
293
- --payload-json '{"stepIndex":2}'
151
+ wattson --json workout list --limit 20
152
+ wattson workout schedule WORKOUT_ID --date 2026-11-13
153
+ wattson workout unschedule WORKOUT_ID
154
+ wattson workout retire WORKOUT_ID
155
+ wattson strava routes --limit 20
156
+ wattson strava starred-segments --limit 20
157
+ wattson course import-strava-route --input-json '{"route":"123456"}'
158
+ wattson course import-strava-segment --input-json '{"segment":"629046"}'
294
159
  ```
295
160
 
296
- Allowed live ops today:
297
- - `reduce_intensity`
298
- - `increase_intensity`
299
- - `extend_recovery`
300
- - `skip_interval`
301
- - `shorten_workout`
302
- - `switch_execution_mode`
161
+ Scheduling sets the library workout's calendar date, not a legacy plan slot or remote start. Unscheduling clears that date; retirement removes the workout from the downloadable library. Verify with `workout get` and `workout list`. Course imports also accept `effortPctFtp` as a fraction.
303
162
 
304
- That means:
305
- - `skip the next interval` -> yes, good fit
306
- - `make it a bit easier` -> yes, good fit
307
- - `add another interval` -> not yet a first-class live op; that would need a richer structural adaptation model later
163
+ ## Removed commands
308
164
 
309
- ## 13. Inspect the summary after completion
165
+ `plan current`, `plan schedule`, `runtime-plan today`, `session load-workout`, `session adapt`, and `session complete` are no longer CLI commands. Their backend compatibility routes are not the current app workflow. Use library scheduling and uploaded-ride reads instead; do not synthesize a ride completion.
310
166
 
311
- ```bash
312
- wattson session summary session-123
313
- ```
167
+ Finish with an affected read endpoint; distinguish server persistence, app synchronization, recorded riding and external publication.
314
168
 
315
- ## 14. Inspect canonical telemetry after completion
316
169
 
317
- ```bash
318
- wattson session telemetry session-123
319
- wattson --json session telemetry session-123
320
- ```
170
+ ## MCP and CLI interchangeability
321
171
 
322
- If no telemetry has been persisted yet, JSON mode returns:
172
+ Use the same authenticated account and API environment for equivalent results. The CLI calls the same backend through the typed client; it does not require an MCP server process. Local/internal athlete-ID inputs do not grant access to other accounts. Server/provider access restrictions still apply.
323
173
 
324
- ```json
325
- {
326
- "telemetry": null
327
- }
174
+ ```bash
175
+ wattson --json tool list
176
+ wattson --json tool schema draft_workout
177
+ wattson --json tool schema validate_workout
178
+ wattson --json tool schema generate_terrain_course
179
+ # Same payload as the MCP tool, same result data:
180
+ wattson --json tool draft_workout --input-json '{"objective":"endurance","durationMin":30}'
328
181
  ```
329
182
 
330
- ## 15. Inspect persisted artifact metadata
183
+ `tool TOOL_NAME --input-json JSON` (or `--input-file PATH`) accepts the corresponding MCP input envelope. `tool schema TOOL_NAME` describes fields, types, defaults and constraints; invocation also enforces contract refinements. Inspect the installed version rather than guessing a flag. The normal CLI commands remain available for humans.
184
+
185
+ | MCP tool | CLI command |
186
+ | --- | --- |
187
+ | `list_saved_workouts` | `workout list` |
188
+ | `health` | `health` |
189
+ | `get_athlete_profile` | `athlete profile` |
190
+ | `get_readiness_snapshot` | `athlete readiness latest` |
191
+ | `get_recent_sessions` | `athlete sessions` |
192
+ | `get_saved_workout` | `workout get` |
193
+ | `get_workout_runtime_plan` | `workout runtime-plan` |
194
+ | `list_saved_workout_revisions` | `workout revisions` |
195
+ | `summarize_session` | `session summary` |
196
+ | `get_session_telemetry` | `session telemetry` |
197
+ | `get_session_artifacts` | `session artifacts` |
198
+ | `get_session_fit_export` | `session fit` |
199
+ | `lookup_climb` | `course lookup` |
200
+ | `find_my_strava_segments` | `strava find-segments` |
201
+ | `import_strava_segment` | `course import-strava-segment` |
202
+ | `import_gpx_course` | `course import-gpx` |
203
+ | `generate_terrain_course` | `course generate-terrain` |
204
+ | `import_strava_activity_course` | `course import-strava-activity` |
205
+ | `build_road_course` | `course build-road` |
206
+ | `import_climb_page` | `course import-climb-page` |
207
+ | `get_my_climb_history` | `course history` |
208
+ | `explore_strava_segments` | `strava explore` |
209
+ | `get_my_strava_segment_efforts` | `strava segment-efforts` |
210
+ | `list_my_strava_starred_segments` | `strava starred-segments` |
211
+ | `list_my_favourite_strava_climbs` | `strava favourite-climbs` |
212
+ | `recommend_first_workout` | `recommend first` |
213
+ | `list_my_strava_routes` | `strava routes` |
214
+ | `import_strava_route` | `course import-strava-route` |
215
+ | `draft_workout` | `workout draft` |
216
+ | `revise_workout` | `workout revise` |
217
+ | `validate_workout` | `workout validate` |
218
+ | `save_workout` | `workout save` |
219
+ | `schedule_saved_workout` | `workout schedule` |
220
+ | `retire_saved_workout` | `workout retire` |
221
+ | `trigger_session_strava_sync` | `session strava-sync` |
222
+ | `create_workout_share` | `share create` |
223
+ | `preview_workout_share` | `share preview` |
224
+ | `import_workout_share` | `share import` |
225
+ | `search` | `research search` |
226
+ | `fetch` | `research fetch` |
227
+
228
+ For new course/discovery/sharing/recommendation commands, schema field names map to kebab-case flags (`effortPctFtp` → `--effort-pct-ftp`). Nested fields use dots (`--answers.ideal-minutes 30`); objects/arrays can be JSON. Booleans take explicit `true`/`false`. Do not combine `--input-json`/`--input-file` with field flags. Existing convenience commands keep their documented flags (for example, `workout get --version`); use generic `tool` invocation for a literal MCP payload.
229
+
230
+ ### Sharing: create, preview, import
331
231
 
332
232
  ```bash
333
- wattson session artifacts session-123
334
- wattson --json session artifacts session-123
233
+ wattson --json share create --input-json '{"workoutId":"WORKOUT_ID","workoutVersion":1}' > share.json
234
+ share_id=$(jq -er '.shareId' share.json)
235
+ wattson --json share preview "$share_id" > preview.json
236
+ # Only when the user wants to add the shared workout to their own library:
237
+ wattson --json share import "$share_id" > imported.json
238
+ wattson --json workout list
335
239
  ```
336
240
 
337
- Use this when you want artifact state/metadata without pulling inline FIT base64 content.
241
+ Creation returns `{shareId,url}` for an owned, saved revision. Share IDs are UUIDs, not arbitrary URLs. Sharing exposes a sanitized template (title, objective, blocks, constraints), not the owner's profile, rationale, schedule or ride history. Block instructions are shared: don't put private notes there if sharing the workout. Anyone with the link can access the public preview. Authenticated preview returns `{item}` with recipient-adjusted artifacts and does **not** save. Import returns `{item,alreadyImported}`; repeated imports may reuse an existing copy. It does not start or schedule a ride.
338
242
 
339
- ## 16. Fetch the deterministic FIT export
243
+ ### All course sources and discovery
340
244
 
341
245
  ```bash
342
- wattson session fit session-123
343
- wattson --json session fit session-123 > /tmp/session-fit.json
246
+ wattson --json course lookup "Ventoux" --limit 3
247
+ wattson --json course generate-terrain --input-json '{"distanceM":5000,"averageGradePct":5,"shape":"hairpins","seed":7}'
248
+ wattson --json course import-gpx --gpx-file route.gpx --name "My climb"
249
+ wattson --json course build-road --input-json '{"start":{"place":"Bédoin, France"},"end":{"place":"Mont Ventoux, France"}}'
250
+ wattson --json course import-climb-page --input-json '{"url":"https://climbfinder.com/en/climbs/mont-ventoux-bedoin"}'
251
+ wattson --json course history --limit 20
252
+ wattson --json strava find-segments "Collserola" --limit 5
253
+ wattson --json strava explore --input-json '{"place":"Girona","radiusKm":10}'
254
+ wattson --json strava segment-efforts "629046" --limit 10
255
+ wattson --json strava favourite-climbs --limit 3 --recent-rides 6
256
+ wattson --json course import-strava-activity --input-json '{"activity":"123456","startDistanceM":1000,"endDistanceM":6000}'
344
257
  ```
345
258
 
346
- Important:
347
- - `session fit` returns inline `contentBase64`
348
- - `session artifacts` is usually the better inspection command when you only need status/metadata
259
+ Replace example Strava IDs with real accessible items. Course sources return profiles; compose a `climb` block from the returned `course`, allow grade execution, then validate/save the workout. Lookup returns a `climbs` array instead of one top-level `course`. GPX takes exactly one of text or a public GPX URL; `--gpx-file` reads local text. Strava activity imports are limited to the rider's own rides. Use `tool schema` for every optional field, bound and default; the [authoring reference](workout-building-blocks.md) explains course embedding, pacing estimates and runtime completion.
349
260
 
350
- ## 17. Enqueue or retry Strava sync
261
+ Limited Strava mode still permits segment import, name matching against starred segments, and ride upload/retry; it restricts other Strava reads. Changing interfaces does not bypass that policy. Lookup, GPX, terrain, road and supported climb pages are alternatives without those Strava reads.
262
+
263
+ ### First-workout recommendations
351
264
 
352
265
  ```bash
353
- wattson session strava-sync session-123
354
- wattson --json session strava-sync session-123
266
+ wattson --json recommend first --input-json '{"answers":{"idealMinutes":30,"maxMinutes":45,"goal":"fitness","freshness":"normal"},"useStrava":false}' > first.json
267
+ jq '{workout:.draft.workout}' first.json > validate.json
268
+ wattson --json workout validate --input-file validate.json
355
269
  ```
356
270
 
357
- Important:
358
- - this corresponds to an async backend operation
359
- - the underlying HTTP route returns `202 Accepted`
360
- - use `session artifacts`, `session fit`, or later session reads to inspect the resulting state
361
-
362
- ## A practical agent loop
271
+ This returns a draft and rationale, not a saved workout. Its definition is at `.draft.workout`, unlike `workout draft`'s `.workout`. Continue the same reviewed validate/save flow above. Use `tool schema recommend_first_workout` for questionnaire choices and all optional answers. Unknown FTP stays omitted; never invent one. `recommend daily` is a separate legacy advisory helper, not this onboarding operation or a saved plan.
363
272
 
364
- A practical agent should usually do this:
273
+ ### Research search and fetch
365
274
 
366
- 1. interpret the user request
367
- 2. choose the smallest structured planner action that matches it
368
- 3. keep natural language as context / rationale, not as the API contract itself
369
- 4. validate before saving when changing workout structure
370
- 5. ingest final session completion once the ride is done
371
- 6. inspect telemetry / artifacts / FIT / Strava sync state after completion when durability matters
372
- 7. use bounded live adaptation ops during an active session
275
+ ```bash
276
+ wattson --json research search "threshold"
277
+ wattson --json research fetch "DOCUMENT_ID_FROM_SEARCH"
278
+ ```
373
279
 
374
- The planner API should stay structured.
375
- The agent should do the interpretation.
280
+ These use the same profile/saved-workout/uploaded-ride corpus, ranking and document handles as MCP research. They are not web search. Fetch a returned handle rather than constructing IDs. The output may contain the signed-in athlete's private training data.