@wattsonclub/wattson-cli 0.0.0-stage → 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.
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@wattsonclub/wattson-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "private": false,
4
+ "version": "0.1.1",
5
+ "type": "module",
6
+ "bin": {
7
+ "wattson": "./dist/bin.js",
8
+ "trainer-planner": "./dist/bin.js"
9
+ },
10
+ "scripts": {
11
+ "typecheck": "tsc -p tsconfig.json --noEmit",
12
+ "test": "vitest run",
13
+ "build": "node build.mjs",
14
+ "prepack": "npm run build"
15
+ },
16
+ "description": "Wattson training planner CLI",
17
+ "license": "UNLICENSED",
18
+ "engines": {
19
+ "node": ">=22"
20
+ },
21
+ "files": [
22
+ "dist/bin.js",
23
+ "dist/bin.js.LEGAL.txt",
24
+ "dist/THIRD-PARTY-NOTICES.txt",
25
+ "skill/SKILL.md",
26
+ "skill/references/commands.md",
27
+ "skill/references/workout-building-blocks.md",
28
+ "skill/references/workout-text-format.md",
29
+ "README.md",
30
+ "CHANGELOG.md"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "devDependencies": {
36
+ "zod": "^3.25.76",
37
+ "esbuild": "^0.25.12"
38
+ }
39
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: wattson
3
+ description: Use the Wattson CLI to authenticate a local or remote agent, inspect athlete profiles and uploaded rides, and draft, validate and save structured cycling workouts. Use for terminal automation with Wattson, not remote control of an offline ride.
4
+ ---
5
+
6
+ # Wattson
7
+
8
+ ## 1. Install and inspect
9
+
10
+ Requires Node.js 22 or newer. Install `npm install -g @wattsonclub/wattson-cli`, then run `wattson --help`. Use the commands advertised by the installed version; do not invent MCP-equivalent commands.
11
+
12
+ Human guide: [wattson.club/cli](https://wattson.club/cli). Agent discovery: [llms.txt](https://wattson.club/llms.txt). Full supported command families and copyable payload transformations: [commands](references/commands.md).
13
+
14
+ The default API is `https://api.wattson.club`. Override with `--base-url`, `WATTSON_API_BASE_URL` or legacy `PLANNER_API_BASE_URL` only for an explicitly chosen environment. Confirm `wattson health` succeeds before continuing.
15
+
16
+ ## 2. Authenticate
17
+
18
+ Run `wattson auth status` (exit 1 means signed out, including with `--json`). If not signed in:
19
+
20
+ - On the user's own computer: `wattson auth login` opens browser OAuth with a localhost callback.
21
+ - On an SSH host, server, container, or agent runtime: `wattson auth login --remote` prints a verification URL and a short-lived user code and waits for approval. The user opens that URL on their own phone/laptop, signs in, checks the code and explicitly approves. No inbound port or SSH tunnel is needed.
22
+
23
+ Deliver login URLs and codes privately to the requesting user, never in a shared group. Never request their password, copy browser cookies, approve on their behalf, or expose access/refresh tokens. A timeout or denial means not authenticated; start a new login only at the user's request. For an unattended agent, complete the initial approval interactively; both flows request `planner:read planner:write`. Subsequent API requests automatically refresh and retry once on HTTP 401; a failed refresh-token request clears saved login. `--timeout <seconds>` overrides the five-minute login timeout.
24
+
25
+ Verify `wattson auth status` and `wattson --json athlete profile`. `wattson auth logout` attempts server logout, ignores server errors, then removes local credentials. It does not guarantee OAuth-token revocation or logout of other sessions. Tokens are unencrypted in a file with mode `0600` (new parent directory `0700`); JSON auth output omits tokens but includes identity metadata. See the reference for paths and overrides. Keep the credential directory persistent and private for containers; do not bake credentials into an image.
26
+
27
+ ## 3. Inspect before changing
28
+
29
+ Use `--json` for machine consumption and check exit status. Login instructions are interactive information, not a successful login result.
30
+
31
+ ```sh
32
+ wattson --json athlete profile
33
+ wattson --json athlete readiness latest
34
+ wattson --json athlete sessions --limit 10
35
+ wattson --json workout get WORKOUT_ID --version 1
36
+ wattson --json workout revisions WORKOUT_ID
37
+ wattson --json session summary SESSION_ID
38
+ ```
39
+
40
+ Omit athlete IDs to use the authenticated athlete. Do not infer permission to access someone else's athlete ID.
41
+
42
+ ## 4. Draft, validate, save
43
+
44
+ Translate natural language into structured inputs. Read [workout building blocks](references/workout-building-blocks.md) for the complete field/primitive reference, exact composition examples, constraints and revision options, and [workout text format](references/workout-text-format.md) for readable previews.
45
+
46
+ ```sh
47
+ wattson --json workout draft --objective vo2max --duration-min 20 --execution-mode erg --style-hint "3 hard intervals"
48
+ wattson --json workout validate --input-file validate.json
49
+ wattson --json workout save --input-file save.json
50
+ ```
51
+
52
+ Draft output is not a save request: construct the appropriate payload described in [command examples](references/commands.md). A draft alone is not published. Inspect `validation.status`: validation can exit 0 even for `fail`. Fix failures and review warnings before saving; the reference example requires `pass`. FTP-relative values are fractions: `0.55` means 55%, not `55`.
53
+
54
+ Only save when the user's task authorizes adding or updating the workout in their library. After saving, verify the returned workout ID/version with `workout get`. Report “saved to your library, ready for the app to download,” not confirmed device delivery. Revisions require validation and save too.
55
+
56
+ ## 5. Respect the offline ride model
57
+
58
+ The rider's app downloads saved workouts and runs the selected ride locally. A suspended/offline app syncs when it returns online. Uploaded rides can then be reviewed with `athlete sessions`, `session summary`, `session telemetry` and `session artifacts`. FIT export returns inline base64, not a downloaded file. Enqueue/retry `session strava-sync` only when the user authorizes Strava publication; acceptance is asynchronous, not delivery confirmation.
59
+
60
+ Use `workout list`, `workout schedule ID --date YYYY-MM-DD`, `workout unschedule ID`, and `workout retire ID` for library management. Strava routes and starred segments can be listed and imported as courses (see reference). The former Today-plan and session load/adapt/complete commands have been removed. Do not synthesize ride completion. The CLI also exposes MCP-equivalent sharing, course discovery, onboarding and research operations. Use the command reference to translate a tool call; preserve its input fields and inspect the same response. A server restriction (such as limited Strava access) applies regardless of interface.
61
+
62
+ Finish by verifying the affected read endpoint and summarizing the result. Distinguish a successful local command from device acknowledgement or Strava delivery.
@@ -0,0 +1,280 @@
1
+ # Wattson CLI command reference
2
+
3
+ Current offline workout/ride workflow. [Human guide](https://wattson.club/cli) · [Agent discovery](https://wattson.club/llms.txt).
4
+
5
+ ## Install and authenticate
6
+
7
+ Requires Node.js 22+. Shell examples use Bash; JSON transformations require `jq`.
8
+
9
+ ```bash
10
+ npm install -g @wattsonclub/wattson-cli
11
+ wattson --help
12
+ wattson 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
18
+ ```
19
+
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.
21
+
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.
23
+
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.
25
+
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.
27
+
28
+ ## Global behavior and supported families
29
+
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.
31
+
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"`.
33
+
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`. |
46
+
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.
48
+
49
+ ## Inspect, draft, validate, save, verify
50
+
51
+ Read profile/readiness before choosing targets. Recommendations are advice, not saved workouts.
52
+
53
+ ```bash
54
+ wattson --json athlete profile
55
+ wattson --json athlete readiness latest
56
+ wattson --json recommend daily --available-time-min 20 --prefer-objective vo2max
57
+ ```
58
+
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.
60
+
61
+ ```bash
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"
76
+ ```
77
+
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}`.
79
+
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`:
81
+
82
+ ```bash
83
+ wattson --json workout draft --input-json '{"objective":"vo2max","durationMin":20,"constraints":{"allowedExecutionModes":["erg"],"maxPctFtp":1.2},"context":{"styleHints":["3 hard intervals"]}}'
84
+ ```
85
+
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.
87
+
88
+ ### Revise a saved version
89
+
90
+ Using `saved.json` above, only for an authorized edit (retain `set -euo pipefail`):
91
+
92
+ ```bash
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)"
102
+ ```
103
+
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
107
+
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.
109
+
110
+ ```bash
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
116
+ ```
117
+
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.
119
+
120
+ ### FIT export
121
+
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:
123
+
124
+ ```bash
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
136
+ ```
137
+
138
+ ### Strava publication/retry — only when authorized
139
+
140
+ ```bash
141
+ wattson --json session strava-sync SESSION_ID
142
+ wattson --json session summary SESSION_ID
143
+ wattson --json session artifacts SESSION_ID
144
+ ```
145
+
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.
147
+
148
+ ## Library calendar and courses
149
+
150
+ ```bash
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"}'
159
+ ```
160
+
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.
162
+
163
+ ## Removed commands
164
+
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.
166
+
167
+ Finish with an affected read endpoint; distinguish server persistence, app synchronization, recorded riding and external publication.
168
+
169
+
170
+ ## MCP and CLI interchangeability
171
+
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.
173
+
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}'
181
+ ```
182
+
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
231
+
232
+ ```bash
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
239
+ ```
240
+
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.
242
+
243
+ ### All course sources and discovery
244
+
245
+ ```bash
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}'
257
+ ```
258
+
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.
260
+
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
264
+
265
+ ```bash
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
269
+ ```
270
+
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.
272
+
273
+ ### Research search and fetch
274
+
275
+ ```bash
276
+ wattson --json research search "threshold"
277
+ wattson --json research fetch "DOCUMENT_ID_FROM_SEARCH"
278
+ ```
279
+
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.