whoop-up 1.0.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.
Files changed (78) hide show
  1. package/.env.example +3 -0
  2. package/CLAUDE.md +277 -0
  3. package/README.md +278 -0
  4. package/SKILL.md +235 -0
  5. package/dist/api/client.d.ts +24 -0
  6. package/dist/api/client.d.ts.map +1 -0
  7. package/dist/api/client.js +149 -0
  8. package/dist/api/client.js.map +1 -0
  9. package/dist/api/endpoints.d.ts +18 -0
  10. package/dist/api/endpoints.d.ts.map +1 -0
  11. package/dist/api/endpoints.js +19 -0
  12. package/dist/api/endpoints.js.map +1 -0
  13. package/dist/auth/oauth.d.ts +5 -0
  14. package/dist/auth/oauth.d.ts.map +1 -0
  15. package/dist/auth/oauth.js +140 -0
  16. package/dist/auth/oauth.js.map +1 -0
  17. package/dist/auth/tokens.d.ts +12 -0
  18. package/dist/auth/tokens.d.ts.map +1 -0
  19. package/dist/auth/tokens.js +102 -0
  20. package/dist/auth/tokens.js.map +1 -0
  21. package/dist/charts/generator.d.ts +8 -0
  22. package/dist/charts/generator.d.ts.map +1 -0
  23. package/dist/charts/generator.js +445 -0
  24. package/dist/charts/generator.js.map +1 -0
  25. package/dist/cli.d.ts +3 -0
  26. package/dist/cli.d.ts.map +1 -0
  27. package/dist/cli.js +370 -0
  28. package/dist/cli.js.map +1 -0
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +6 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/types/whoop.d.ts +156 -0
  34. package/dist/types/whoop.d.ts.map +1 -0
  35. package/dist/types/whoop.js +3 -0
  36. package/dist/types/whoop.js.map +1 -0
  37. package/dist/utils/analysis.d.ts +30 -0
  38. package/dist/utils/analysis.d.ts.map +1 -0
  39. package/dist/utils/analysis.js +246 -0
  40. package/dist/utils/analysis.js.map +1 -0
  41. package/dist/utils/constants.d.ts +16 -0
  42. package/dist/utils/constants.d.ts.map +1 -0
  43. package/dist/utils/constants.js +25 -0
  44. package/dist/utils/constants.js.map +1 -0
  45. package/dist/utils/date.d.ts +14 -0
  46. package/dist/utils/date.d.ts.map +1 -0
  47. package/dist/utils/date.js +48 -0
  48. package/dist/utils/date.js.map +1 -0
  49. package/dist/utils/errors.d.ts +14 -0
  50. package/dist/utils/errors.d.ts.map +1 -0
  51. package/dist/utils/errors.js +36 -0
  52. package/dist/utils/errors.js.map +1 -0
  53. package/dist/utils/format.d.ts +25 -0
  54. package/dist/utils/format.d.ts.map +1 -0
  55. package/dist/utils/format.js +262 -0
  56. package/dist/utils/format.js.map +1 -0
  57. package/docs/COMMANDS.md +435 -0
  58. package/package.json +54 -0
  59. package/references/health_analysis.md +212 -0
  60. package/src/api/client.ts +207 -0
  61. package/src/api/endpoints.ts +20 -0
  62. package/src/auth/oauth.ts +171 -0
  63. package/src/auth/tokens.ts +120 -0
  64. package/src/charts/generator.ts +493 -0
  65. package/src/cli.ts +433 -0
  66. package/src/index.ts +8 -0
  67. package/src/types/whoop.ts +192 -0
  68. package/src/utils/analysis.ts +321 -0
  69. package/src/utils/constants.ts +32 -0
  70. package/src/utils/date.ts +58 -0
  71. package/src/utils/errors.ts +38 -0
  72. package/src/utils/format.ts +323 -0
  73. package/tests/cli/cli.test.ts +49 -0
  74. package/tests/utils/analysis.test.ts +152 -0
  75. package/tests/utils/date.test.ts +69 -0
  76. package/tests/utils/errors.test.ts +33 -0
  77. package/tests/utils/format.test.ts +229 -0
  78. package/tsconfig.json +19 -0
package/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ WHOOP_CLIENT_ID=your_client_id_here
2
+ WHOOP_CLIENT_SECRET=your_client_secret_here
3
+ WHOOP_REDIRECT_URI=http://localhost:9876/callback
package/CLAUDE.md ADDED
@@ -0,0 +1,277 @@
1
+ # whoop-up — Claude Code Guide
2
+
3
+ ## Project Overview
4
+
5
+ `whoop-up` is a TypeScript CLI tool that fetches, analyzes, and visualizes health data from the WHOOP API v2. It is published to npm and installed globally: `npm install -g whoop-up`.
6
+
7
+ **Origin:** Built on top of [whoop-cli](https://github.com/xonika9/whoop-cli) by xonika9. Extended with:
8
+ - Trend analysis, health insights, full dashboard, interactive HTML charts
9
+ - Retry logic, `--days` shorthand, summary averages, per-minute rate limit awareness
10
+
11
+ ---
12
+
13
+ ## Architecture
14
+
15
+ ```
16
+ src/
17
+ ├── index.ts Entry point — loads .env, runs Commander program
18
+ ├── cli.ts All CLI commands (Commander.js)
19
+ ├── api/
20
+ │ ├── endpoints.ts Base URL, named endpoint paths, byId helpers
21
+ │ └── client.ts All API functions + retry logic + pagination
22
+ ├── auth/
23
+ │ ├── oauth.ts OAuth 2.0 login/logout/status/refresh
24
+ │ └── tokens.ts Token storage at ~/.whoop-up/tokens.json
25
+ ├── charts/
26
+ │ └── generator.ts HTML chart generation (ApexCharts, opens in browser)
27
+ ├── types/
28
+ │ └── whoop.ts All TypeScript interfaces matching the WHOOP API schema
29
+ └── utils/
30
+ ├── date.ts Date helpers, WHOOP day boundary (4am cutoff)
31
+ ├── format.ts CLI output formatting, summary stats computation
32
+ ├── analysis.ts Trend analysis, health insights generation
33
+ └── errors.ts WhoopError class, ExitCode enum, handleError()
34
+ ```
35
+
36
+ **Build output:** `dist/` (compiled JS + `.d.ts` + sourcemaps)
37
+ **Binary:** `dist/index.js` — wired to `whoop` via `package.json` `bin` field
38
+
39
+ ---
40
+
41
+ ## WHOOP API v2
42
+
43
+ **Base URL:** `https://api.prod.whoop.com/developer/v2`
44
+ **Auth:** OAuth 2.0 Authorization Code flow
45
+ **Token URL:** `https://api.prod.whoop.com/oauth/oauth2/token`
46
+
47
+ ### Endpoints we use
48
+
49
+ | Endpoint | Function |
50
+ |---|---|
51
+ | `GET /user/profile/basic` | `getProfile()` |
52
+ | `GET /user/measurement/body` | `getBody()` |
53
+ | `GET /activity/sleep` | `getSleep()` |
54
+ | `GET /activity/sleep/{id}` | `getSleepById(id)` |
55
+ | `GET /recovery` | `getRecovery()` |
56
+ | `GET /cycle` | `getCycle()` |
57
+ | `GET /cycle/{id}` | `getCycleById(id)` |
58
+ | `GET /cycle/{id}/sleep` | `getSleepForCycle(cycleId)` |
59
+ | `GET /cycle/{id}/recovery` | `getRecoveryForCycle(cycleId)` |
60
+ | `GET /activity/workout` | `getWorkout()` |
61
+ | `GET /activity/workout/{id}` | `getWorkoutById(id)` |
62
+ | `DELETE /user/access` | Called by `logout()` to revoke on WHOOP server |
63
+
64
+ ### Endpoints NOT used (in spec but unimplemented)
65
+ - `GET /v1/activity-mapping/{activityV1Id}` — maps legacy v1 activity IDs to v2 UUIDs (migration utility, low priority)
66
+
67
+ ### Pagination
68
+ All collection endpoints support: `limit` (max 25), `start`, `end`, `nextToken`.
69
+ `fetchAll()` in `client.ts` handles multi-page fetching with a 50-page safety cap.
70
+
71
+ ### Rate limiting
72
+ The client retries on HTTP `{429, 500, 502, 503, 504}` with exponential backoff starting at 1s, max 3 retries.
73
+
74
+ **API rate limits (enforced by WHOOP):**
75
+ - **100 requests per minute** ← the binding constraint
76
+ - 10,000 requests per day
77
+
78
+ **During tests and development — always respect the per-minute limit:**
79
+ - Tests must use pure in-memory mocks/fixtures — never call the live API
80
+ - Avoid running multiple `--all` fetches in quick succession in scripts
81
+ - If writing a script that loops over multiple commands, add a delay between calls
82
+ - The `fetchAll()` 50-page safety cap helps stay within daily limits, but the minute limit is the one that bites in practice
83
+
84
+ ---
85
+
86
+ ## Critical Type System Rules
87
+
88
+ ### `score_state` is always checked before accessing `score`
89
+
90
+ Every scored record (`WhoopSleep`, `WhoopRecovery`, `WhoopWorkout`, `WhoopCycle`) has:
91
+ ```typescript
92
+ score_state: 'SCORED' | 'PENDING_SCORE' | 'UNSCORABLE'
93
+ score?: <ScoreType> // ONLY present when score_state === 'SCORED'
94
+ ```
95
+
96
+ **Always filter before accessing `score`:**
97
+ ```typescript
98
+ // Correct
99
+ const scored = records.filter(r => r.score_state === 'SCORED' && r.score != null);
100
+ const value = scored[0].score!.hrv_rmssd_milli;
101
+
102
+ // Wrong — will crash on PENDING_SCORE records
103
+ const value = records[0].score.hrv_rmssd_milli;
104
+ ```
105
+
106
+ This pattern is applied consistently throughout `format.ts` and `analysis.ts`.
107
+
108
+ ### `WhoopCycle` does NOT have a `recovery` field
109
+
110
+ The WHOOP API `/v2/cycle` response does **not** embed recovery data. Recovery is a separate resource linked by `cycle_id`. Use `getRecoveryForCycle(cycleId)` or the `/v2/recovery` collection endpoint.
111
+
112
+ ### `WhoopSleep.id` is a UUID string, not a number
113
+
114
+ The API returns sleep IDs as UUID strings (e.g. `"ecfc6a15-4661-442f-a9a4-f160dd7afae8"`). Cycle IDs are integers.
115
+
116
+ ### Optional score subfields
117
+
118
+ Some score fields are only present on WHOOP 4.0+ hardware:
119
+ - `RecoveryScore.spo2_percentage` — optional
120
+ - `RecoveryScore.skin_temp_celsius` — optional
121
+
122
+ Some sleep score fields may be absent if WHOOP has insufficient data:
123
+ - `SleepScore.sleep_performance_percentage` — optional
124
+ - `SleepScore.sleep_consistency_percentage` — optional
125
+ - `SleepScore.sleep_efficiency_percentage` — optional
126
+
127
+ Always use `?.` or null checks for these.
128
+
129
+ ---
130
+
131
+ ## Key Design Decisions
132
+
133
+ ### Default date range is last 7 days (not today)
134
+ Unlike the original `whoopskill_v2`, all data commands default to `--days 7`. This matches the Python skill's behavior and produces more useful results by default.
135
+
136
+ ### WHOOP day boundary = 4am
137
+ WHOOP considers a new "day" to start at 4:00 AM. `getWhoopDay()` in `date.ts` handles this — calls before 4am still reference the previous calendar day.
138
+
139
+ ### Token storage at `~/.whoop-up/tokens.json`
140
+ Permissions: directory `0700`, file `0600`. Tokens auto-refresh 15 minutes before expiry (`REFRESH_BUFFER_SECONDS = 900`).
141
+
142
+ ### Logout revokes server-side
143
+ `logout()` calls `DELETE /v2/user/access` before clearing the local token file. If the server call fails (expired token, network error), it still clears locally and logs a note — never leaves user in a broken state.
144
+
145
+ ### No circular imports: auth ↔ client
146
+ `client.ts` imports from `auth/tokens.ts`. `auth/oauth.ts` must NOT import from `client.ts`. The revoke call in `oauth.ts` is implemented as a raw `fetch()` to avoid the cycle.
147
+
148
+ ### Charts use CDN ApexCharts
149
+ `charts/generator.ts` produces self-contained HTML files referencing `https://cdn.jsdelivr.net/npm/apexcharts@3`. No local chart dependencies. Files are written to `os.tmpdir()` and opened with the `open` package, or saved with `--output`.
150
+
151
+ ---
152
+
153
+ ## CLI Commands Reference
154
+
155
+ ```bash
156
+ # Auth
157
+ whoop auth login
158
+ whoop auth logout # revokes on WHOOP server + clears local tokens
159
+ whoop auth status
160
+ whoop auth refresh
161
+
162
+ # Data collection (all support: -n/--days, -s/--start, -e/--end, -d/--date, -l/--limit, -a/--all, -p/--pretty)
163
+ whoop sleep [options]
164
+ whoop recovery [options]
165
+ whoop workout [options]
166
+ whoop cycle [options]
167
+ whoop profile
168
+ whoop body
169
+
170
+ # Multi-day summary with averages (HRV, RHR, sleep%, sleep hours, strain)
171
+ whoop summary [-n days] [-s start] [-e end] [-c/--color] [--json]
172
+
173
+ # Trend analysis — days must be 7, 14, or 30
174
+ whoop trends [-n 7|14|30] [--json]
175
+
176
+ # AI-style insights
177
+ whoop insights [-n days] [--json]
178
+
179
+ # Full terminal dashboard with today's data and 7-day trends
180
+ whoop dashboard [--json]
181
+
182
+ # HTML charts — opens in browser
183
+ whoop chart <sleep|recovery|strain|hrv|dashboard> [-n days] [-o output.html]
184
+
185
+ # By-ID lookups
186
+ whoop get <sleep|workout|cycle> <id>
187
+ whoop cycle-sleep <cycleId> # sleep linked to a cycle
188
+ whoop cycle-recovery <cycleId> # recovery linked to a cycle
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Development Workflow
194
+
195
+ ```bash
196
+ # Install and build
197
+ npm install # also runs 'prepare' → 'npm run build'
198
+
199
+ # Development (run without building)
200
+ npm run dev -- sleep --days 7
201
+
202
+ # Build TypeScript → dist/
203
+ npm run build
204
+
205
+ # Test locally as installed CLI
206
+ npm install -g .
207
+ whoop --help
208
+
209
+ # One-shot run (compiled)
210
+ node dist/index.js summary --color
211
+ ```
212
+
213
+ ### Environment variables (copy from `.env.example`)
214
+ ```
215
+ WHOOP_CLIENT_ID=...
216
+ WHOOP_CLIENT_SECRET=...
217
+ WHOOP_REDIRECT_URI=http://localhost:9876/callback
218
+ ```
219
+
220
+ Place in `.env` at project root — loaded by `dotenv` at startup.
221
+
222
+ ---
223
+
224
+ ## Publishing to npm
225
+
226
+ ```bash
227
+ # First-time login
228
+ npm login
229
+
230
+ # Publish (prepare hook auto-builds)
231
+ npm publish --access public
232
+
233
+ # Users install with:
234
+ npm install -g whoop-up
235
+ ```
236
+
237
+ Version is set in `package.json`. Bump it before each publish.
238
+
239
+ ---
240
+
241
+ ## Adding a New Feature — Checklist
242
+
243
+ 1. **New endpoint?** Add to `src/api/endpoints.ts` (`ENDPOINTS` or `byId`), implement in `src/api/client.ts`, wire up in `src/cli.ts`
244
+ 2. **New type?** Add to `src/types/whoop.ts`. If it has a score, use `ScoreState` + optional `score?`
245
+ 3. **New CLI command?** Add in `src/cli.ts` before the root flags block
246
+ 4. **New chart?** Add to `src/charts/generator.ts`, add `case` to the `chart` command in `cli.ts`
247
+ 5. **Always run:** `npm run build` — zero TypeScript errors required before committing
248
+
249
+ ---
250
+
251
+ ## File Ownership Map
252
+
253
+ | What you want to change | File to edit |
254
+ |---|---|
255
+ | API endpoint URLs | `src/api/endpoints.ts` |
256
+ | Retry logic, pagination, API auth | `src/api/client.ts` |
257
+ | OAuth login/logout/token refresh | `src/auth/oauth.ts` |
258
+ | Token storage location/format | `src/auth/tokens.ts` |
259
+ | TypeScript types matching API | `src/types/whoop.ts` |
260
+ | CLI commands and options | `src/cli.ts` |
261
+ | Terminal output formatting | `src/utils/format.ts` |
262
+ | Trend/insight calculations | `src/utils/analysis.ts` |
263
+ | Date helpers, WHOOP day boundary | `src/utils/date.ts` |
264
+ | Error codes and handling | `src/utils/errors.ts` |
265
+ | HTML chart generation | `src/charts/generator.ts` |
266
+ | Science-backed health reference | `references/health_analysis.md` |
267
+
268
+ ---
269
+
270
+ ## Known Constraints
271
+
272
+ - `limit` max is **25** per page (enforced by WHOOP API — sending higher values is silently capped)
273
+ - `trends` command only accepts 7, 14, or 30 days (enforced in CLI; `analyzeTrends()` itself accepts any number)
274
+ - Token expiry buffer is 15 minutes — cron jobs should call `auth refresh` before running to ensure a fresh token
275
+ - `sport_id` on workouts is deprecated and will stop appearing after 09/01/2025 — use `sport_name`
276
+ - `v1_id` on sleep/workout records is deprecated and will stop appearing after 09/01/2025
277
+ - SpO2 and skin temperature only appear for WHOOP 4.0+ hardware users — always check for `null`
package/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # whoop-up
2
+
3
+ [![npm version](https://img.shields.io/npm/v/whoop-up.svg)](https://www.npmjs.com/package/whoop-up)
4
+
5
+ CLI for WHOOP health data — fetch, analyze, and visualize via the WHOOP API v2.
6
+
7
+ ```bash
8
+ npm install -g whoop-up
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ whoop auth login # authenticate via browser
15
+ whoop summary --color # color-coded daily snapshot
16
+ whoop dashboard # full dashboard + 7-day trends
17
+ whoop chart dashboard -n 30 -o /tmp/whoop.html # interactive HTML chart
18
+ ```
19
+
20
+ ## Setup
21
+
22
+ 1. Register a WHOOP application at [developer.whoop.com](https://developer.whoop.com)
23
+ - Apps with fewer than 10 users don't need WHOOP review (immediate use)
24
+
25
+ 2. Set environment variables — create a `.env` file in your working directory:
26
+
27
+ ```env
28
+ WHOOP_CLIENT_ID=your_client_id
29
+ WHOOP_CLIENT_SECRET=your_client_secret
30
+ WHOOP_REDIRECT_URI=https://your-redirect-uri.com/callback
31
+ ```
32
+
33
+ 3. Authenticate:
34
+
35
+ ```bash
36
+ whoop auth login
37
+ ```
38
+
39
+ Tokens are stored at `~/.whoop-up/tokens.json` and auto-refresh 15 minutes before expiry.
40
+
41
+ ## Commands
42
+
43
+ ### Auth
44
+
45
+ | Command | Description |
46
+ | --- | --- |
47
+ | `whoop auth login` | OAuth flow — opens browser, paste callback URL |
48
+ | `whoop auth logout` | Revoke access on WHOOP server + clear local tokens |
49
+ | `whoop auth status` | Show token expiry (does not refresh) |
50
+ | `whoop auth refresh` | Force refresh access token using refresh token |
51
+
52
+ ### Data
53
+
54
+ Output is JSON by default. Add `--pretty` for human-readable formatted output.
55
+
56
+ | Command | Description |
57
+ | --- | --- |
58
+ | `whoop sleep` | Sleep stages, efficiency, respiratory rate |
59
+ | `whoop recovery` | Recovery score, HRV, RHR, SpO2, skin temperature |
60
+ | `whoop workout` | Workouts with strain, HR zones, calories |
61
+ | `whoop cycle` | Daily physiological cycle (strain, calories) |
62
+ | `whoop profile` | User info (name, email) |
63
+ | `whoop body` | Body measurements (height, weight, max HR) |
64
+
65
+ ### Analysis
66
+
67
+ Output is pretty-printed by default. Add `--json` for raw JSON.
68
+
69
+ | Command | Description |
70
+ | --- | --- |
71
+ | `whoop summary` | Multi-day averages: recovery, HRV, RHR, sleep, strain |
72
+ | `whoop summary --color` | Same with 🔴🟡🟢 color-coded status indicators |
73
+ | `whoop dashboard` | Full terminal dashboard with 7-day trends |
74
+ | `whoop trends` | Multi-day trend analysis with direction arrows ↑↓→ |
75
+ | `whoop insights` | Health recommendations based on your recent data |
76
+
77
+ ### Charts
78
+
79
+ Interactive HTML charts, opened in browser automatically. Use `-o file.html` to save instead.
80
+
81
+ | Command | Description |
82
+ | --- | --- |
83
+ | `whoop chart sleep` | Sleep performance and stage breakdown |
84
+ | `whoop chart recovery` | Recovery score and HRV over time |
85
+ | `whoop chart strain` | Daily strain and calorie burn |
86
+ | `whoop chart hrv` | HRV trend |
87
+ | `whoop chart dashboard` | Combined overview (recommended) |
88
+
89
+ ### Lookups
90
+
91
+ Fetch a single record by ID. IDs come from collection endpoint responses or `dashboard --json`.
92
+
93
+ | Command | Description |
94
+ | --- | --- |
95
+ | `whoop get sleep <uuid>` | Full detail for one sleep record (UUID string) |
96
+ | `whoop get workout <uuid>` | Full detail for one workout record (UUID string) |
97
+ | `whoop get cycle <id>` | Full detail for one cycle (integer ID) |
98
+ | `whoop cycle-sleep <cycleId>` | Sleep record linked to a given cycle |
99
+ | `whoop cycle-recovery <cycleId>` | Recovery record linked to a given cycle |
100
+
101
+ ## Flags
102
+
103
+ ### Data command flags
104
+
105
+ | Flag | Description |
106
+ | --- | --- |
107
+ | `-n, --days <number>` | Days of history (default: 7) |
108
+ | `-d, --date <date>` | Specific date (YYYY-MM-DD) |
109
+ | `-s, --start <date>` | Range start date |
110
+ | `-e, --end <date>` | Range end date |
111
+ | `-l, --limit <n>` | Max results per page (max: 25) |
112
+ | `-a, --all` | Fetch all pages |
113
+ | `-p, --pretty` | Human-readable output |
114
+
115
+ ### Analysis command flags
116
+
117
+ | Flag | Applies to | Description |
118
+ | --- | --- | --- |
119
+ | `-n, --days <number>` | all analysis | Days of history (default: 7) |
120
+ | `-c, --color` | summary | Color-coded status indicators |
121
+ | `--json` | dashboard, trends, insights | Raw JSON output |
122
+
123
+ ### Chart flags
124
+
125
+ | Flag | Description |
126
+ | --- | --- |
127
+ | `-n, --days <number>` | Days of history to chart (default: 7) |
128
+ | `-o, --output <file>` | Save HTML to file instead of opening browser |
129
+
130
+ ### Global flags (combine data types)
131
+
132
+ | Flag | Description |
133
+ | --- | --- |
134
+ | `--sleep` | Include sleep data |
135
+ | `--recovery` | Include recovery data |
136
+ | `--workout` | Include workout data |
137
+ | `--cycle` | Include cycle data |
138
+ | `--profile` | Include profile |
139
+ | `--body` | Include body measurements |
140
+
141
+ Running `whoop` with no arguments fetches all data types.
142
+
143
+ ## Example output
144
+
145
+ `whoop summary --color`:
146
+ ```
147
+ 📊 7-Day Summary
148
+
149
+ 🔴 Avg Recovery: 31.5%
150
+ 💓 Avg HRV: 124.4ms
151
+ ❤️ Avg RHR: 56.1bpm
152
+ 🔴 Avg Sleep: 44.8% | 4.0h
153
+ 🔥 Avg Strain: 6.8
154
+ ```
155
+
156
+ `whoop dashboard`:
157
+ ```
158
+ 📅 2026-02-17 | Ruben Khachaturov
159
+
160
+ ── Recovery ──────────────────────────
161
+ 🔴 13% | HRV: 129ms (↑ vs 124 avg) | RHR: 60bpm (↑ vs 56 avg)
162
+ SpO2: 96% | Skin: 33.1°C | Resp: 15.6/min
163
+
164
+ ── Sleep ─────────────────────────────
165
+ 😴 27% | 2.0h total | Efficiency: 100%
166
+ Deep: 1.0h (49%) | REM: 0.6h (30%) | Light: 0.4h
167
+ Disturbances: 1 | Consistency: 66%
168
+ 💤 Sleep debt: 2.1h | Need tonight: 9.7h
169
+
170
+ ── Strain ────────────────────────────
171
+ 🔥 4.1 / 6 optimal | 767 cal
172
+
173
+ ── 7-Day Trends ──────────────────────
174
+ HRV: 148 → 129ms ↓ (range 66-179)
175
+ RHR: 49 → 60bpm ↑ (range 49-64)
176
+ Recovery: 44 → 13% ↓
177
+ Sleep: 4.6 → 2.0h ↓
178
+ Strain: 6.8 avg (range 4.1-16.4)
179
+ ```
180
+
181
+ `whoop trends --days 7`:
182
+ ```
183
+ 📊 7-Day Trends
184
+
185
+ 💚 Recovery: 31.5% avg (5-85) ↓
186
+ 💓 HRV: 124.4ms avg (66-179) ↓
187
+ ❤️ RHR: 56.1bpm avg (49-64) ↑
188
+ 😴 Sleep: 44.8% avg (25-78) ↓
189
+ 🛏️ Hours: 4.3h avg (1.3-7.2) ↓
190
+ 🔥 Strain: 6.8 avg (4.1-16.4) ↓
191
+ ```
192
+
193
+ `whoop insights`:
194
+ ```
195
+ 💡 Insights & Recommendations
196
+
197
+ 🔴 Red Recovery
198
+ Recovery at 13% — body needs rest.
199
+ → Prioritize rest, hydration, and sleep tonight.
200
+
201
+ 🔴 Significant Sleep Debt
202
+ You have 2.1 hours of accumulated sleep debt.
203
+ → Try to get to bed 30-60 min earlier for the next few days.
204
+ ```
205
+
206
+ `whoop recovery` (default JSON):
207
+ ```json
208
+ {
209
+ "cycle_id": 1317587415,
210
+ "sleep_id": "5d225f07-8722-4dcf-be52-528e2500bad3",
211
+ "user_id": 30398164,
212
+ "created_at": "2026-02-17T20:02:32.740Z",
213
+ "updated_at": "2026-02-17T20:02:32.740Z",
214
+ "score_state": "SCORED",
215
+ "score": {
216
+ "user_calibrating": false,
217
+ "recovery_score": 13,
218
+ "resting_heart_rate": 60,
219
+ "hrv_rmssd_milli": 128.51297,
220
+ "spo2_percentage": 96,
221
+ "skin_temp_celsius": 33.145668
222
+ }
223
+ }
224
+ ```
225
+
226
+ ## Token management
227
+
228
+ `whoop auth status` **does not refresh tokens** — it only reports expiry.
229
+
230
+ For cron jobs or automation:
231
+
232
+ ```bash
233
+ # Recommended pattern
234
+ whoop auth refresh # ensure fresh token before fetching
235
+ whoop dashboard --json
236
+ ```
237
+
238
+ If refresh fails with an expired refresh token, re-authenticate interactively:
239
+
240
+ ```bash
241
+ whoop auth login
242
+ ```
243
+
244
+ ## Exit codes
245
+
246
+ | Code | Meaning |
247
+ | --- | --- |
248
+ | 0 | Success |
249
+ | 1 | General error |
250
+ | 2 | Authentication error |
251
+ | 3 | Rate limit exceeded |
252
+ | 4 | Network error |
253
+
254
+ ## Development
255
+
256
+ ```bash
257
+ git clone https://github.com/mrkhachaturov/whoop-up.git
258
+ cd whoop-up
259
+ npm install
260
+
261
+ npm run dev -- dashboard # run without building
262
+ npm run build # compile TypeScript → dist/
263
+ npm test # run test suite
264
+ ```
265
+
266
+ Node.js 22+ required.
267
+
268
+ ## Full command reference
269
+
270
+ → [docs/COMMANDS.md](docs/COMMANDS.md)
271
+
272
+ ## Attribution
273
+
274
+ Built on top of [whoop-cli](https://github.com/xonika9/whoop-cli) by [xonika9](https://github.com/xonika9).
275
+
276
+ ## License
277
+
278
+ MIT