@betterness/cli 1.3.1 → 1.3.4
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/CLI_REFERENCE.md +648 -0
- package/README.md +10 -0
- package/dist/index.js +209 -31
- package/package.json +1 -1
package/CLI_REFERENCE.md
ADDED
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
# Betterness CLI Reference
|
|
2
|
+
|
|
3
|
+
> Auto-generated from command definitions — do not edit manually.
|
|
4
|
+
> Run `npm run docs` in `modules/betterness-cli` to regenerate.
|
|
5
|
+
|
|
6
|
+
Version: 0.0.0
|
|
7
|
+
|
|
8
|
+
## Global Options
|
|
9
|
+
|
|
10
|
+
These options apply to all commands:
|
|
11
|
+
|
|
12
|
+
| Option | Description |
|
|
13
|
+
|--------|-------------|
|
|
14
|
+
| `-V, --version` | output the version number |
|
|
15
|
+
| `--api-key <key>` | API key (overrides env and stored credentials) |
|
|
16
|
+
| `--json` | Output as JSON |
|
|
17
|
+
| `--markdown` | Output as Markdown |
|
|
18
|
+
| `--quiet` | Suppress output (exit code only) |
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
- **[auth](#auth)** — Manage authentication
|
|
23
|
+
- `auth login` — Authenticate with Betterness (OAuth by default, or --key for API key)
|
|
24
|
+
- `auth logout` — Remove stored credentials
|
|
25
|
+
- `auth whoami` — Show the currently authenticated user
|
|
26
|
+
- **[profile](#profile)** — User profile information
|
|
27
|
+
- `profile get` — Retrieve current user profile (name, email, phone, gender, DOB, address)
|
|
28
|
+
- `profile update` — Update profile information (only provided fields are changed)
|
|
29
|
+
- **[biomarkers](#biomarkers)** — Biomarker lab results and LOINC codes
|
|
30
|
+
- `biomarkers search` — Search and filter biomarker lab results
|
|
31
|
+
- `biomarkers loinc-codes` — List all available LOINC codes for biomarker identification
|
|
32
|
+
- **[biological-age](#biological-age)** — Biological age calculations and history
|
|
33
|
+
- `biological-age get` — Get biological age history with biomarker values
|
|
34
|
+
- **[activity](#activity)** — Activity and workout data from connected wearables
|
|
35
|
+
- `activity get` — Retrieve activity and workout data (steps, distance, calories, VO2 max, workouts)
|
|
36
|
+
- **[sleep](#sleep)** — Sleep data from connected wearables
|
|
37
|
+
- `sleep get` — Retrieve nightly sleep data (time in bed, total sleep, sleep stage breakdown)
|
|
38
|
+
- `sleep stages` — Retrieve minute-by-minute sleep stage transitions (Deep, Core, REM, Awake)
|
|
39
|
+
- **[vitals](#vitals)** — Vital signs from connected wearables
|
|
40
|
+
- `vitals get` — Retrieve vital signs (heart rate, HRV, blood pressure, SpO2, glucose, respiratory rate)
|
|
41
|
+
- **[body-composition](#body-composition)** — Body composition data from connected wearables
|
|
42
|
+
- `body-composition get` — Retrieve body composition (weight, body fat %, muscle mass, BMI, waist circumference)
|
|
43
|
+
- **[connected-devices](#connected-devices)** — Health device integrations and wearable connections
|
|
44
|
+
- `connected-devices list` — List all connected health devices and wearables
|
|
45
|
+
- `connected-devices available` — List health device integrations the user can connect (not currently active)
|
|
46
|
+
- `connected-devices link` — Generate connection link for a web-based health device integration
|
|
47
|
+
- `connected-devices apple-health-code` — Generate connection code for Apple HealthKit via Junction app
|
|
48
|
+
- `connected-devices disconnect` — Disconnect a health device integration
|
|
49
|
+
- **[lab-tests](#lab-tests)** — Available lab tests for ordering
|
|
50
|
+
- `lab-tests list` — List available lab tests with prices and included markers
|
|
51
|
+
- **[lab-records](#lab-records)** — Lab records — uploaded results and purchased test orders
|
|
52
|
+
- `lab-records list` — List lab records (both uploaded results and lab orders)
|
|
53
|
+
- `lab-records detail` — Get full detail of a lab record by external ID
|
|
54
|
+
- **[lab-orders](#lab-orders)** — Lab order management — scheduling, appointments, and service centers
|
|
55
|
+
- `lab-orders initialize` — Initialize a lab order for processing (order must be in Paid status)
|
|
56
|
+
- `lab-orders service-centers` — Search lab service centers near a ZIP code
|
|
57
|
+
- `lab-orders slots` — Get available appointment time slots at a service center
|
|
58
|
+
- `lab-orders book` — Book a blood draw appointment at a service center
|
|
59
|
+
- `lab-orders reschedule` — Reschedule an existing blood draw appointment
|
|
60
|
+
- `lab-orders cancel` — Cancel a blood draw appointment (run without --reason-id to see available reasons)
|
|
61
|
+
- **[lab-results](#lab-results)** — Lab result management — approve, update biomarkers, update metadata
|
|
62
|
+
- `lab-results update-status` — Update lab result status (APPROVE, ROLLBACK, or REPROCESS)
|
|
63
|
+
- `lab-results update-biomarker` — Update or delete a biomarker value within an uploaded lab result
|
|
64
|
+
- `lab-results update-metadata` — Update metadata of an uploaded lab result (patient info and test details)
|
|
65
|
+
- `lab-results upload` — Upload a lab result PDF for processing
|
|
66
|
+
- **[purchases](#purchases)** — Lab test purchases and payment methods
|
|
67
|
+
- `purchases payment-methods` — List saved payment methods (credit/debit cards)
|
|
68
|
+
- `purchases buy` — Purchase a lab test using a saved payment method
|
|
69
|
+
- `purchases checkout` — Generate a Stripe Checkout payment link for a lab test (for users without saved cards)
|
|
70
|
+
- **[smart-listings](#smart-listings)** — Search and browse wellness providers (SmartListings)
|
|
71
|
+
- `smart-listings search` — Search SmartListings by name, description, tags, or location
|
|
72
|
+
- `smart-listings detail` — Get full details of a SmartListing by ID
|
|
73
|
+
- **[workflow](#workflow)** — Composite commands that aggregate data from multiple endpoints
|
|
74
|
+
- `workflow daily-brief` — Daily health summary — profile, biological age, biomarkers, devices
|
|
75
|
+
- `workflow next-actions` — Recommended next actions based on biomarkers and health data
|
|
76
|
+
- **[schema](#schema)** — Discover available commands, options, and response formats
|
|
77
|
+
- **[debug](#debug)** — Internal debug utilities
|
|
78
|
+
- `debug config` — Show resolved configuration
|
|
79
|
+
- **[health-profile](#health-profile)** — Health profile questionnaire (read, update, schema)
|
|
80
|
+
- `health-profile schema` — List all sections and question IDs available in the health profile
|
|
81
|
+
- `health-profile get` — Retrieve the full health profile (all answered questions as a flat map)
|
|
82
|
+
- `health-profile get-section` — Retrieve health profile answers for a specific section
|
|
83
|
+
- `health-profile update` — Update health profile questions (only provided fields are changed)
|
|
84
|
+
- `health-profile reset-section` — Clear all answers in a section
|
|
85
|
+
- `health-profile summary` — Human-readable summary of answered health profile questions
|
|
86
|
+
- **[knowledge](#knowledge)** — Search the Betterness knowledge library
|
|
87
|
+
- `knowledge search` — Search Betterness-published knowledge (files and videos)
|
|
88
|
+
- **[mcp](#mcp)** — MCP server integration for AI agents (Claude, Cursor, Windsurf)
|
|
89
|
+
- `mcp install` — Install Betterness MCP integration into an AI client
|
|
90
|
+
- `mcp uninstall` — Remove Betterness MCP integration from an AI client
|
|
91
|
+
- `mcp status` — Show MCP integration status across all supported clients
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## auth
|
|
96
|
+
|
|
97
|
+
Manage authentication
|
|
98
|
+
|
|
99
|
+
### `betterness auth login`
|
|
100
|
+
|
|
101
|
+
Authenticate with Betterness (OAuth by default, or --key for API key)
|
|
102
|
+
|
|
103
|
+
| Option | Description |
|
|
104
|
+
|--------|-------------|
|
|
105
|
+
| `--key <apiKey>` | Use an API key instead of OAuth |
|
|
106
|
+
|
|
107
|
+
### `betterness auth logout`
|
|
108
|
+
|
|
109
|
+
Remove stored credentials
|
|
110
|
+
|
|
111
|
+
### `betterness auth whoami`
|
|
112
|
+
|
|
113
|
+
Show the currently authenticated user
|
|
114
|
+
|
|
115
|
+
## profile
|
|
116
|
+
|
|
117
|
+
User profile information
|
|
118
|
+
|
|
119
|
+
### `betterness profile get`
|
|
120
|
+
|
|
121
|
+
Retrieve current user profile (name, email, phone, gender, DOB, address)
|
|
122
|
+
|
|
123
|
+
### `betterness profile update`
|
|
124
|
+
|
|
125
|
+
Update profile information (only provided fields are changed)
|
|
126
|
+
|
|
127
|
+
| Option | Description |
|
|
128
|
+
|--------|-------------|
|
|
129
|
+
| `--first-name <name>` | First name |
|
|
130
|
+
| `--last-name <name>` | Last name |
|
|
131
|
+
| `--phone <number>` | Phone number without dial code |
|
|
132
|
+
| `--phone-dial-code <code>` | Phone dial code (e.g. +1, +44) |
|
|
133
|
+
| `--gender <value>` | Gender: MALE, FEMALE, OTHER, or PREF_NOT |
|
|
134
|
+
| `--birth-date <YYYY-MM-DD>` | Date of birth |
|
|
135
|
+
| `--address <street>` | Home street address |
|
|
136
|
+
| `--city <city>` | City |
|
|
137
|
+
| `--state <state>` | State or province |
|
|
138
|
+
| `--zip-code <zip>` | ZIP or postal code |
|
|
139
|
+
| `--country <code>` | Country (e.g. US, GB) |
|
|
140
|
+
| `--dry-run` | Preview changes without applying |
|
|
141
|
+
|
|
142
|
+
## biomarkers
|
|
143
|
+
|
|
144
|
+
Biomarker lab results and LOINC codes
|
|
145
|
+
|
|
146
|
+
### `betterness biomarkers search`
|
|
147
|
+
|
|
148
|
+
Search and filter biomarker lab results
|
|
149
|
+
|
|
150
|
+
| Option | Description |
|
|
151
|
+
|--------|-------------|
|
|
152
|
+
| `--name <text>` | Filter by biomarker name |
|
|
153
|
+
| `--loinc-code <code>` | Filter by LOINC code |
|
|
154
|
+
| `--start-date <YYYY-MM-DD>` | Start date (ISO-8601) |
|
|
155
|
+
| `--end-date <YYYY-MM-DD>` | End date (ISO-8601) |
|
|
156
|
+
| `--categories <list>` | Comma-separated category filter |
|
|
157
|
+
| `--range <type>` | Range filter: OPTIMAL, AVERAGE, OUT_OF_RANGE, UNKNOWN |
|
|
158
|
+
| `--limit <n>` | Maximum number of results (default: `20`) |
|
|
159
|
+
|
|
160
|
+
### `betterness biomarkers loinc-codes`
|
|
161
|
+
|
|
162
|
+
List all available LOINC codes for biomarker identification
|
|
163
|
+
|
|
164
|
+
## biological-age
|
|
165
|
+
|
|
166
|
+
Biological age calculations and history
|
|
167
|
+
|
|
168
|
+
### `betterness biological-age get`
|
|
169
|
+
|
|
170
|
+
Get biological age history with biomarker values
|
|
171
|
+
|
|
172
|
+
| Option | Description |
|
|
173
|
+
|--------|-------------|
|
|
174
|
+
| `--limit <n>` | Maximum number of results (default: `10`) |
|
|
175
|
+
|
|
176
|
+
## activity
|
|
177
|
+
|
|
178
|
+
Activity and workout data from connected wearables
|
|
179
|
+
|
|
180
|
+
### `betterness activity get`
|
|
181
|
+
|
|
182
|
+
Retrieve activity and workout data (steps, distance, calories, VO2 max, workouts)
|
|
183
|
+
|
|
184
|
+
| Option | Description |
|
|
185
|
+
|--------|-------------|
|
|
186
|
+
| `--from <YYYY-MM-DD>` | Start date (default: `2026-03-17`) |
|
|
187
|
+
| `--to <YYYY-MM-DD>` | End date |
|
|
188
|
+
| `--timezone <tz>` | IANA timezone (default: `America/Mendoza`) |
|
|
189
|
+
|
|
190
|
+
## sleep
|
|
191
|
+
|
|
192
|
+
Sleep data from connected wearables
|
|
193
|
+
|
|
194
|
+
### `betterness sleep get`
|
|
195
|
+
|
|
196
|
+
Retrieve nightly sleep data (time in bed, total sleep, sleep stage breakdown)
|
|
197
|
+
|
|
198
|
+
| Option | Description |
|
|
199
|
+
|--------|-------------|
|
|
200
|
+
| `--from <YYYY-MM-DD>` | Start date (default: `2026-03-17`) |
|
|
201
|
+
| `--to <YYYY-MM-DD>` | End date |
|
|
202
|
+
| `--timezone <tz>` | IANA timezone (default: `America/Mendoza`) |
|
|
203
|
+
|
|
204
|
+
### `betterness sleep stages`
|
|
205
|
+
|
|
206
|
+
Retrieve minute-by-minute sleep stage transitions (Deep, Core, REM, Awake)
|
|
207
|
+
|
|
208
|
+
| Option | Description |
|
|
209
|
+
|--------|-------------|
|
|
210
|
+
| `--from <YYYY-MM-DD>` | Start date (default: `2026-03-17`) |
|
|
211
|
+
| `--to <YYYY-MM-DD>` | End date |
|
|
212
|
+
| `--timezone <tz>` | IANA timezone (default: `America/Mendoza`) |
|
|
213
|
+
|
|
214
|
+
## vitals
|
|
215
|
+
|
|
216
|
+
Vital signs from connected wearables
|
|
217
|
+
|
|
218
|
+
### `betterness vitals get`
|
|
219
|
+
|
|
220
|
+
Retrieve vital signs (heart rate, HRV, blood pressure, SpO2, glucose, respiratory rate)
|
|
221
|
+
|
|
222
|
+
| Option | Description |
|
|
223
|
+
|--------|-------------|
|
|
224
|
+
| `--from <YYYY-MM-DD>` | Start date (default: `2026-03-17`) |
|
|
225
|
+
| `--to <YYYY-MM-DD>` | End date |
|
|
226
|
+
| `--timezone <tz>` | IANA timezone (default: `America/Mendoza`) |
|
|
227
|
+
|
|
228
|
+
## body-composition
|
|
229
|
+
|
|
230
|
+
Body composition data from connected wearables
|
|
231
|
+
|
|
232
|
+
### `betterness body-composition get`
|
|
233
|
+
|
|
234
|
+
Retrieve body composition (weight, body fat %, muscle mass, BMI, waist circumference)
|
|
235
|
+
|
|
236
|
+
| Option | Description |
|
|
237
|
+
|--------|-------------|
|
|
238
|
+
| `--from <YYYY-MM-DD>` | Start date (default: `2026-03-17`) |
|
|
239
|
+
| `--to <YYYY-MM-DD>` | End date |
|
|
240
|
+
| `--timezone <tz>` | IANA timezone (default: `America/Mendoza`) |
|
|
241
|
+
|
|
242
|
+
## connected-devices
|
|
243
|
+
|
|
244
|
+
Health device integrations and wearable connections
|
|
245
|
+
|
|
246
|
+
### `betterness connected-devices list`
|
|
247
|
+
|
|
248
|
+
List all connected health devices and wearables
|
|
249
|
+
|
|
250
|
+
### `betterness connected-devices available`
|
|
251
|
+
|
|
252
|
+
List health device integrations the user can connect (not currently active)
|
|
253
|
+
|
|
254
|
+
### `betterness connected-devices link`
|
|
255
|
+
|
|
256
|
+
Generate connection link for a web-based health device integration
|
|
257
|
+
|
|
258
|
+
| Option | Description |
|
|
259
|
+
|--------|-------------|
|
|
260
|
+
| `--integration-key <key>` | Integration provider (GARMIN, OURA, WITHINGS, PELOTON, WAHOO, EIGHT_SLEEP) **(required)** |
|
|
261
|
+
|
|
262
|
+
### `betterness connected-devices apple-health-code`
|
|
263
|
+
|
|
264
|
+
Generate connection code for Apple HealthKit via Junction app
|
|
265
|
+
|
|
266
|
+
### `betterness connected-devices disconnect`
|
|
267
|
+
|
|
268
|
+
Disconnect a health device integration
|
|
269
|
+
|
|
270
|
+
| Option | Description |
|
|
271
|
+
|--------|-------------|
|
|
272
|
+
| `--integration-key <key>` | Integration provider to disconnect **(required)** |
|
|
273
|
+
| `--dry-run` | Preview the disconnection without executing |
|
|
274
|
+
|
|
275
|
+
## lab-tests
|
|
276
|
+
|
|
277
|
+
Available lab tests for ordering
|
|
278
|
+
|
|
279
|
+
### `betterness lab-tests list`
|
|
280
|
+
|
|
281
|
+
List available lab tests with prices and included markers
|
|
282
|
+
|
|
283
|
+
| Option | Description |
|
|
284
|
+
|--------|-------------|
|
|
285
|
+
| `--query <text>` | Search by name or description |
|
|
286
|
+
| `--popular` | Only show popular tests |
|
|
287
|
+
| `--loinc-slug <slug>` | Filter by LOINC slug |
|
|
288
|
+
|
|
289
|
+
## lab-records
|
|
290
|
+
|
|
291
|
+
Lab records — uploaded results and purchased test orders
|
|
292
|
+
|
|
293
|
+
### `betterness lab-records list`
|
|
294
|
+
|
|
295
|
+
List lab records (both uploaded results and lab orders)
|
|
296
|
+
|
|
297
|
+
| Option | Description |
|
|
298
|
+
|--------|-------------|
|
|
299
|
+
| `--limit <n>` | Results per page (default: `20`) |
|
|
300
|
+
| `--page <n>` | Page number (zero-based) (default: `0`) |
|
|
301
|
+
|
|
302
|
+
### `betterness lab-records detail`
|
|
303
|
+
|
|
304
|
+
Get full detail of a lab record by external ID
|
|
305
|
+
|
|
306
|
+
| Option | Description |
|
|
307
|
+
|--------|-------------|
|
|
308
|
+
| `--record-id <id>` | Lab record external ID **(required)** |
|
|
309
|
+
|
|
310
|
+
## lab-orders
|
|
311
|
+
|
|
312
|
+
Lab order management — scheduling, appointments, and service centers
|
|
313
|
+
|
|
314
|
+
### `betterness lab-orders initialize`
|
|
315
|
+
|
|
316
|
+
Initialize a lab order for processing (order must be in Paid status)
|
|
317
|
+
|
|
318
|
+
| Option | Description |
|
|
319
|
+
|--------|-------------|
|
|
320
|
+
| `--order-id <id>` | Lab order external ID **(required)** |
|
|
321
|
+
| `--dry-run` | Preview the action without executing |
|
|
322
|
+
|
|
323
|
+
### `betterness lab-orders service-centers`
|
|
324
|
+
|
|
325
|
+
Search lab service centers near a ZIP code
|
|
326
|
+
|
|
327
|
+
| Option | Description |
|
|
328
|
+
|--------|-------------|
|
|
329
|
+
| `--zip-code <zip>` | ZIP code to search near **(required)** |
|
|
330
|
+
| `--order-id <id>` | Lab order external ID **(required)** |
|
|
331
|
+
| `--limit <n>` | Maximum results (default: `6`) |
|
|
332
|
+
| `--offset <n>` | Pagination offset (default: `0`) |
|
|
333
|
+
|
|
334
|
+
### `betterness lab-orders slots`
|
|
335
|
+
|
|
336
|
+
Get available appointment time slots at a service center
|
|
337
|
+
|
|
338
|
+
| Option | Description |
|
|
339
|
+
|--------|-------------|
|
|
340
|
+
| `--site-code <code>` | Service center site code **(required)** |
|
|
341
|
+
| `--order-id <id>` | Lab order external ID **(required)** |
|
|
342
|
+
| `--timezone <tz>` | IANA timezone **(required)** |
|
|
343
|
+
| `--start-date <YYYY-MM-DD>` | Start date for slot search |
|
|
344
|
+
| `--range-days <n>` | Number of days to search (default: `7`) |
|
|
345
|
+
|
|
346
|
+
### `betterness lab-orders book`
|
|
347
|
+
|
|
348
|
+
Book a blood draw appointment at a service center
|
|
349
|
+
|
|
350
|
+
| Option | Description |
|
|
351
|
+
|--------|-------------|
|
|
352
|
+
| `--order-id <id>` | Lab order external ID **(required)** |
|
|
353
|
+
| `--booking-key <key>` | Booking key from slots command **(required)** |
|
|
354
|
+
| `--timezone <tz>` | IANA timezone **(required)** |
|
|
355
|
+
| `--dry-run` | Preview the booking without executing |
|
|
356
|
+
|
|
357
|
+
### `betterness lab-orders reschedule`
|
|
358
|
+
|
|
359
|
+
Reschedule an existing blood draw appointment
|
|
360
|
+
|
|
361
|
+
| Option | Description |
|
|
362
|
+
|--------|-------------|
|
|
363
|
+
| `--order-id <id>` | Lab order external ID **(required)** |
|
|
364
|
+
| `--booking-key <key>` | New booking key from slots command **(required)** |
|
|
365
|
+
| `--timezone <tz>` | IANA timezone **(required)** |
|
|
366
|
+
| `--dry-run` | Preview the reschedule without executing |
|
|
367
|
+
|
|
368
|
+
### `betterness lab-orders cancel`
|
|
369
|
+
|
|
370
|
+
Cancel a blood draw appointment (run without --reason-id to see available reasons)
|
|
371
|
+
|
|
372
|
+
| Option | Description |
|
|
373
|
+
|--------|-------------|
|
|
374
|
+
| `--order-id <id>` | Lab order external ID **(required)** |
|
|
375
|
+
| `--reason-id <id>` | Cancellation reason ID |
|
|
376
|
+
| `--dry-run` | Preview the cancellation without executing |
|
|
377
|
+
|
|
378
|
+
## lab-results
|
|
379
|
+
|
|
380
|
+
Lab result management — approve, update biomarkers, update metadata
|
|
381
|
+
|
|
382
|
+
### `betterness lab-results update-status`
|
|
383
|
+
|
|
384
|
+
Update lab result status (APPROVE, ROLLBACK, or REPROCESS)
|
|
385
|
+
|
|
386
|
+
| Option | Description |
|
|
387
|
+
|--------|-------------|
|
|
388
|
+
| `--result-id <id>` | Lab result external ID **(required)** |
|
|
389
|
+
| `--action <action>` | Action: APPROVE, ROLLBACK, or REPROCESS **(required)** |
|
|
390
|
+
|
|
391
|
+
### `betterness lab-results update-biomarker`
|
|
392
|
+
|
|
393
|
+
Update or delete a biomarker value within an uploaded lab result
|
|
394
|
+
|
|
395
|
+
| Option | Description |
|
|
396
|
+
|--------|-------------|
|
|
397
|
+
| `--biomarker-id <id>` | Biomarker external ID **(required)** |
|
|
398
|
+
| `--action <action>` | Set to DELETE to remove the biomarker |
|
|
399
|
+
| `--name <name>` | Biomarker name |
|
|
400
|
+
| `--result <value>` | Biomarker value (e.g. '5.2', '120') |
|
|
401
|
+
| `--unit <unit>` | Unit (e.g. 'mg/dL', 'ng/mL') |
|
|
402
|
+
| `--min-range <n>` | Minimum range value |
|
|
403
|
+
| `--max-range <n>` | Maximum range value |
|
|
404
|
+
|
|
405
|
+
### `betterness lab-results update-metadata`
|
|
406
|
+
|
|
407
|
+
Update metadata of an uploaded lab result (patient info and test details)
|
|
408
|
+
|
|
409
|
+
| Option | Description |
|
|
410
|
+
|--------|-------------|
|
|
411
|
+
| `--result-id <id>` | Lab result external ID **(required)** |
|
|
412
|
+
| `--patient-name <name>` | Patient name |
|
|
413
|
+
| `--patient-sex <sex>` | Patient sex: MALE or FEMALE |
|
|
414
|
+
| `--dob <date>` | Date of birth |
|
|
415
|
+
| `--lab-name <name>` | Lab name |
|
|
416
|
+
| `--ordering-physician <name>` | Ordering physician |
|
|
417
|
+
| `--date-collected <date>` | Date collected (ISO-8601) |
|
|
418
|
+
| `--fasting` | Mark as fasting test |
|
|
419
|
+
|
|
420
|
+
### `betterness lab-results upload`
|
|
421
|
+
|
|
422
|
+
Upload a lab result PDF for processing
|
|
423
|
+
|
|
424
|
+
| Option | Description |
|
|
425
|
+
|--------|-------------|
|
|
426
|
+
| `--file <path>` | Path to the PDF file **(required)** |
|
|
427
|
+
|
|
428
|
+
## purchases
|
|
429
|
+
|
|
430
|
+
Lab test purchases and payment methods
|
|
431
|
+
|
|
432
|
+
### `betterness purchases payment-methods`
|
|
433
|
+
|
|
434
|
+
List saved payment methods (credit/debit cards)
|
|
435
|
+
|
|
436
|
+
### `betterness purchases buy`
|
|
437
|
+
|
|
438
|
+
Purchase a lab test using a saved payment method
|
|
439
|
+
|
|
440
|
+
| Option | Description |
|
|
441
|
+
|--------|-------------|
|
|
442
|
+
| `--test-key <key>` | Lab test object key **(required)** |
|
|
443
|
+
| `--payment-method-id <id>` | Payment method external ID **(required)** |
|
|
444
|
+
| `--promo-code <code>` | Promotion code |
|
|
445
|
+
| `--dry-run` | Preview what would be purchased without executing |
|
|
446
|
+
|
|
447
|
+
### `betterness purchases checkout`
|
|
448
|
+
|
|
449
|
+
Generate a Stripe Checkout payment link for a lab test (for users without saved cards)
|
|
450
|
+
|
|
451
|
+
| Option | Description |
|
|
452
|
+
|--------|-------------|
|
|
453
|
+
| `--test-key <key>` | Lab test object key **(required)** |
|
|
454
|
+
| `--success-url <url>` | URL to redirect after successful payment **(required)** |
|
|
455
|
+
| `--cancel-url <url>` | URL to redirect if checkout is cancelled **(required)** |
|
|
456
|
+
| `--promo-code <code>` | Promotion code |
|
|
457
|
+
| `--dry-run` | Preview the checkout request without executing |
|
|
458
|
+
|
|
459
|
+
## smart-listings
|
|
460
|
+
|
|
461
|
+
Search and browse wellness providers (SmartListings)
|
|
462
|
+
|
|
463
|
+
### `betterness smart-listings search`
|
|
464
|
+
|
|
465
|
+
Search SmartListings by name, description, tags, or location
|
|
466
|
+
|
|
467
|
+
| Option | Description |
|
|
468
|
+
|--------|-------------|
|
|
469
|
+
| `--query <text>` | Text search query |
|
|
470
|
+
| `--lat <n>` | Latitude for proximity search |
|
|
471
|
+
| `--lng <n>` | Longitude for proximity search |
|
|
472
|
+
| `--radius <km>` | Radius in km (1, 2, 5, 10, 20) |
|
|
473
|
+
| `--following` | Only show followed providers |
|
|
474
|
+
| `--limit <n>` | Results per page (default: `10`) |
|
|
475
|
+
| `--page <n>` | Page number (zero-based) (default: `0`) |
|
|
476
|
+
|
|
477
|
+
### `betterness smart-listings detail`
|
|
478
|
+
|
|
479
|
+
Get full details of a SmartListing by ID
|
|
480
|
+
|
|
481
|
+
| Option | Description |
|
|
482
|
+
|--------|-------------|
|
|
483
|
+
| `--id <externalId>` | SmartListing external ID **(required)** |
|
|
484
|
+
|
|
485
|
+
## workflow
|
|
486
|
+
|
|
487
|
+
Composite commands that aggregate data from multiple endpoints
|
|
488
|
+
|
|
489
|
+
### `betterness workflow daily-brief`
|
|
490
|
+
|
|
491
|
+
Daily health summary — profile, biological age, biomarkers, devices
|
|
492
|
+
|
|
493
|
+
| Option | Description |
|
|
494
|
+
|--------|-------------|
|
|
495
|
+
| `--biomarker-limit <n>` | Maximum biomarkers to fetch (default: `20`) |
|
|
496
|
+
|
|
497
|
+
### `betterness workflow next-actions`
|
|
498
|
+
|
|
499
|
+
Recommended next actions based on biomarkers and health data
|
|
500
|
+
|
|
501
|
+
| Option | Description |
|
|
502
|
+
|--------|-------------|
|
|
503
|
+
| `--biomarker-limit <n>` | Maximum biomarkers to analyze (default: `50`) |
|
|
504
|
+
|
|
505
|
+
### `betterness schema`
|
|
506
|
+
|
|
507
|
+
Discover available commands, options, and response formats
|
|
508
|
+
|
|
509
|
+
## debug
|
|
510
|
+
|
|
511
|
+
Internal debug utilities
|
|
512
|
+
|
|
513
|
+
### `betterness debug config`
|
|
514
|
+
|
|
515
|
+
Show resolved configuration
|
|
516
|
+
|
|
517
|
+
## health-profile
|
|
518
|
+
|
|
519
|
+
Health profile questionnaire (read, update, schema)
|
|
520
|
+
|
|
521
|
+
### `betterness health-profile schema`
|
|
522
|
+
|
|
523
|
+
List all sections and question IDs available in the health profile
|
|
524
|
+
|
|
525
|
+
| Option | Description |
|
|
526
|
+
|--------|-------------|
|
|
527
|
+
| `--section <acronym>` | Show questions for a specific section only |
|
|
528
|
+
|
|
529
|
+
### `betterness health-profile get`
|
|
530
|
+
|
|
531
|
+
Retrieve the full health profile (all answered questions as a flat map)
|
|
532
|
+
|
|
533
|
+
### `betterness health-profile get-section`
|
|
534
|
+
|
|
535
|
+
Retrieve health profile answers for a specific section
|
|
536
|
+
|
|
537
|
+
| Option | Description |
|
|
538
|
+
|--------|-------------|
|
|
539
|
+
| `--section <acronym>` | Section acronym (e.g. HWP, DMH, DA) **(required)** |
|
|
540
|
+
|
|
541
|
+
### `betterness health-profile update`
|
|
542
|
+
|
|
543
|
+
Update health profile questions (only provided fields are changed)
|
|
544
|
+
|
|
545
|
+
| Option | Description |
|
|
546
|
+
|--------|-------------|
|
|
547
|
+
| `--data <json>` | JSON object with question IDs as keys and answers as values **(required)** |
|
|
548
|
+
| `--dry-run` | Preview changes without applying |
|
|
549
|
+
|
|
550
|
+
### `betterness health-profile reset-section`
|
|
551
|
+
|
|
552
|
+
Clear all answers in a section
|
|
553
|
+
|
|
554
|
+
| Option | Description |
|
|
555
|
+
|--------|-------------|
|
|
556
|
+
| `--section <acronym>` | Section acronym (e.g. HWP, DMH, DA) **(required)** |
|
|
557
|
+
| `--dry-run` | Preview without applying |
|
|
558
|
+
|
|
559
|
+
### `betterness health-profile summary`
|
|
560
|
+
|
|
561
|
+
Human-readable summary of answered health profile questions
|
|
562
|
+
|
|
563
|
+
| Option | Description |
|
|
564
|
+
|--------|-------------|
|
|
565
|
+
| `--section <acronym>` | Summarize a specific section only |
|
|
566
|
+
|
|
567
|
+
## knowledge
|
|
568
|
+
|
|
569
|
+
Search the Betterness knowledge library
|
|
570
|
+
|
|
571
|
+
### `betterness knowledge search`
|
|
572
|
+
|
|
573
|
+
Search Betterness-published knowledge (files and videos). Uses OpenSearch full-text search on title and summary fields.
|
|
574
|
+
|
|
575
|
+
| Option | Description |
|
|
576
|
+
|--------|-------------|
|
|
577
|
+
| `-s, --search <text>` | Full-text search query |
|
|
578
|
+
| `--type <type>` | Filter by type: `FILE` or `VIDEO` (default: both) |
|
|
579
|
+
| `--sort <method>` | Sort: `ASC`, `DESC`, `LATEST`, `OLDEST` (default: `LATEST`) |
|
|
580
|
+
| `--page <n>` | Page number (0-indexed) (default: `0`) |
|
|
581
|
+
| `--page-size <n>` | Results per page (default: `10`) |
|
|
582
|
+
|
|
583
|
+
**Response fields:** `externalId`, `title`, `type`, `authors`, `score` (0-100), `date`, `url`, `coverImageUrl`, `reviewCount`
|
|
584
|
+
|
|
585
|
+
**Examples:**
|
|
586
|
+
```bash
|
|
587
|
+
betterness knowledge search -s "nutrition" --json
|
|
588
|
+
betterness knowledge search --type VIDEO --page-size 5
|
|
589
|
+
betterness knowledge search --sort OLDEST --page 2
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
## mcp
|
|
593
|
+
|
|
594
|
+
MCP server integration for AI agents (Claude, Cursor, Windsurf)
|
|
595
|
+
|
|
596
|
+
### `betterness mcp install`
|
|
597
|
+
|
|
598
|
+
Install Betterness MCP integration into an AI client
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
betterness mcp install <client> [--scope <scope>] [--dry-run]
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
| Argument / Option | Description |
|
|
605
|
+
|-------------------|-------------|
|
|
606
|
+
| `<client>` | Client to configure: `claude`, `claude-code`, `cursor`, `windsurf` **(required)** |
|
|
607
|
+
| `--scope <scope>` | Config scope: `global` or `project` (claude-code only, default: `global`) |
|
|
608
|
+
| `--dry-run` | Print config without writing |
|
|
609
|
+
|
|
610
|
+
**Behavior:**
|
|
611
|
+
1. Resolves stored credentials (or `--api-key` override)
|
|
612
|
+
2. Reads the client's config file (if it exists), merges `mcpServers.betterness` entry
|
|
613
|
+
3. Backs up original file as `.bak`
|
|
614
|
+
4. Writes updated config
|
|
615
|
+
|
|
616
|
+
**Client config file locations:**
|
|
617
|
+
|
|
618
|
+
| Client | macOS | Linux |
|
|
619
|
+
|--------|-------|-------|
|
|
620
|
+
| `claude` | `~/Library/Application Support/Claude/claude_desktop_config.json` | `~/.config/Claude/claude_desktop_config.json` |
|
|
621
|
+
| `claude-code` (global) | `~/.claude/settings.json` | `~/.claude/settings.json` |
|
|
622
|
+
| `claude-code` (project) | `.mcp.json` | `.mcp.json` |
|
|
623
|
+
| `cursor` | `.cursor/mcp.json` | `.cursor/mcp.json` |
|
|
624
|
+
| `windsurf` | `~/.codeium/windsurf/mcp_config.json` | `~/.codeium/windsurf/mcp_config.json` |
|
|
625
|
+
|
|
626
|
+
### `betterness mcp uninstall`
|
|
627
|
+
|
|
628
|
+
Remove Betterness MCP integration from an AI client
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
betterness mcp uninstall <client> [--scope <scope>]
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
| Argument / Option | Description |
|
|
635
|
+
|-------------------|-------------|
|
|
636
|
+
| `<client>` | Client to unconfigure: `claude`, `claude-code`, `cursor`, `windsurf` **(required)** |
|
|
637
|
+
| `--scope <scope>` | Config scope: `global` or `project` (claude-code only, default: `global`) |
|
|
638
|
+
|
|
639
|
+
### `betterness mcp status`
|
|
640
|
+
|
|
641
|
+
Show MCP integration status across all supported clients. Scans all known config file paths and reports which clients have Betterness MCP configured.
|
|
642
|
+
|
|
643
|
+
```bash
|
|
644
|
+
betterness mcp status --json
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
Returns: `client`, `status`, `apiKey` (truncated), `configFile`
|
|
648
|
+
|
package/README.md
CHANGED
|
@@ -158,6 +158,15 @@ betterness health-profile update --data '{"HWP_HAB_do_you_smoke_cigarettes_or_us
|
|
|
158
158
|
betterness health-profile reset-section --section DA
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
### Knowledge Library
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
betterness knowledge search -s "nutrition" # Full-text search
|
|
165
|
+
betterness knowledge search --type VIDEO # Only videos
|
|
166
|
+
betterness knowledge search --type FILE --sort ASC # Files A-Z
|
|
167
|
+
betterness knowledge search -s "sleep" --page-size 5 # Paginate
|
|
168
|
+
```
|
|
169
|
+
|
|
161
170
|
### Smart Listings (Wellness Providers)
|
|
162
171
|
|
|
163
172
|
```bash
|
|
@@ -237,6 +246,7 @@ Agent skills teach your AI coding agent the Betterness API surface — endpoints
|
|
|
237
246
|
| [betterness-smart-listings](https://github.com/Betterness/betterness-cli/tree/main/skills/betterness-smart-listings) | Wellness provider search |
|
|
238
247
|
| [betterness-workflow](https://github.com/Betterness/betterness-cli/tree/main/skills/betterness-workflow) | Composite commands (daily brief, next actions) |
|
|
239
248
|
| [betterness-health-profile](https://github.com/Betterness/betterness-cli/tree/main/skills/betterness-health-profile) | Health profile questionnaire management |
|
|
249
|
+
| [betterness-knowledge](https://github.com/Betterness/betterness-cli/tree/main/skills/betterness-knowledge) | Knowledge library search (files and videos) |
|
|
240
250
|
| [betterness-mcp](https://github.com/Betterness/betterness-cli/tree/main/skills/betterness-mcp) | MCP server integration for AI clients |
|
|
241
251
|
|
|
242
252
|
**Persona skills:**
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,25 @@
|
|
|
3
3
|
// src/program.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
// src/commands/auth.ts
|
|
7
|
+
import { join as join4 } from "path";
|
|
8
|
+
|
|
6
9
|
// src/auth/credentialStore.ts
|
|
7
10
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
|
|
11
|
+
import { join as join2 } from "path";
|
|
12
|
+
|
|
13
|
+
// src/paths.ts
|
|
8
14
|
import { join } from "path";
|
|
9
15
|
import { homedir } from "os";
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
function resolveConfigDir() {
|
|
17
|
+
const apiUrl = "https://api.betterness.ai";
|
|
18
|
+
const isDev = apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1");
|
|
19
|
+
return join(homedir(), isDev ? ".betterness-dev" : ".betterness");
|
|
20
|
+
}
|
|
21
|
+
var CONFIG_DIR = resolveConfigDir();
|
|
22
|
+
|
|
23
|
+
// src/auth/credentialStore.ts
|
|
24
|
+
var CREDENTIALS_FILE = join2(CONFIG_DIR, "credentials.json");
|
|
12
25
|
function ensureConfigDir() {
|
|
13
26
|
if (!existsSync(CONFIG_DIR)) {
|
|
14
27
|
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
@@ -43,13 +56,11 @@ function deleteCredentials() {
|
|
|
43
56
|
|
|
44
57
|
// src/auth/tokenStore.ts
|
|
45
58
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
46
|
-
import { join as
|
|
47
|
-
|
|
48
|
-
var CONFIG_DIR2 = join2(homedir2(), ".betterness");
|
|
49
|
-
var TOKENS_FILE = join2(CONFIG_DIR2, "tokens.json");
|
|
59
|
+
import { join as join3 } from "path";
|
|
60
|
+
var TOKENS_FILE = join3(CONFIG_DIR, "tokens.json");
|
|
50
61
|
function ensureConfigDir2() {
|
|
51
|
-
if (!existsSync2(
|
|
52
|
-
mkdirSync2(
|
|
62
|
+
if (!existsSync2(CONFIG_DIR)) {
|
|
63
|
+
mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
53
64
|
}
|
|
54
65
|
}
|
|
55
66
|
function loadTokens() {
|
|
@@ -408,7 +419,7 @@ var ApiClient = class {
|
|
|
408
419
|
headers: {
|
|
409
420
|
"Authorization": `Bearer ${this.apiKey}`,
|
|
410
421
|
"Content-Type": "application/json",
|
|
411
|
-
"User-Agent": `betterness-cli/${"1.3.
|
|
422
|
+
"User-Agent": `betterness-cli/${"1.3.4"}`,
|
|
412
423
|
"Accept": "application/json"
|
|
413
424
|
},
|
|
414
425
|
body: body ? JSON.stringify(body) : void 0,
|
|
@@ -456,9 +467,9 @@ var ApiClient = class {
|
|
|
456
467
|
return this.unwrap(raw, schema);
|
|
457
468
|
}
|
|
458
469
|
async upload(path, filePath, fieldName = "file", schema) {
|
|
459
|
-
const { readFileSync:
|
|
470
|
+
const { readFileSync: readFileSync5 } = await import("fs");
|
|
460
471
|
const { basename, extname } = await import("path");
|
|
461
|
-
const buffer =
|
|
472
|
+
const buffer = readFileSync5(filePath);
|
|
462
473
|
const fileName = basename(filePath);
|
|
463
474
|
const ext = extname(filePath).toLowerCase();
|
|
464
475
|
const mimeType = ext === ".pdf" ? "application/pdf" : "application/octet-stream";
|
|
@@ -472,7 +483,7 @@ var ApiClient = class {
|
|
|
472
483
|
method: "POST",
|
|
473
484
|
headers: {
|
|
474
485
|
"Authorization": `Bearer ${this.apiKey}`,
|
|
475
|
-
"User-Agent": `betterness-cli/${"1.3.
|
|
486
|
+
"User-Agent": `betterness-cli/${"1.3.4"}`,
|
|
476
487
|
"Accept": "application/json"
|
|
477
488
|
},
|
|
478
489
|
body: formData,
|
|
@@ -837,6 +848,30 @@ var healthProfileResponseSchema = z.object({
|
|
|
837
848
|
userId: z.string(),
|
|
838
849
|
questionsData: z.record(z.unknown()).nullable().optional()
|
|
839
850
|
}).passthrough();
|
|
851
|
+
var expertSmallSchema = z.object({
|
|
852
|
+
externalId: z.string(),
|
|
853
|
+
name: z.string().nullable().optional()
|
|
854
|
+
});
|
|
855
|
+
var tagSmallSchema = z.object({
|
|
856
|
+
externalId: z.string(),
|
|
857
|
+
name: z.string().nullable().optional()
|
|
858
|
+
});
|
|
859
|
+
var knowledgeSearchResultSchema = z.object({
|
|
860
|
+
externalId: z.string(),
|
|
861
|
+
title: z.string().nullable().optional(),
|
|
862
|
+
coverImageUrl: z.string().nullable().optional(),
|
|
863
|
+
url: z.string().nullable().optional(),
|
|
864
|
+
type: z.string().nullable().optional(),
|
|
865
|
+
authors: z.array(expertSmallSchema).nullable().optional(),
|
|
866
|
+
tags: z.array(tagSmallSchema).nullable().optional(),
|
|
867
|
+
releaseDate: z.string().nullable().optional(),
|
|
868
|
+
betternessKnowledge: z.boolean().nullable().optional(),
|
|
869
|
+
averageScore: z.number().nullable().optional(),
|
|
870
|
+
reviewCount: z.number().nullable().optional(),
|
|
871
|
+
avatarUrl: z.string().nullable().optional(),
|
|
872
|
+
permission: z.string().nullable().optional(),
|
|
873
|
+
showOnProfile: z.boolean().nullable().optional()
|
|
874
|
+
});
|
|
840
875
|
|
|
841
876
|
// src/commands/auth.ts
|
|
842
877
|
function registerAuthCommands(program2) {
|
|
@@ -892,7 +927,7 @@ async function loginWithApiKey(apiKey) {
|
|
|
892
927
|
name: user.firstName ?? void 0
|
|
893
928
|
});
|
|
894
929
|
console.log(`Logged in as: ${user.firstName ?? "Unknown"} (${user.email ?? "no email"})`);
|
|
895
|
-
console.log(
|
|
930
|
+
console.log(`Credentials saved to ${join4(CONFIG_DIR, "credentials.json")}`);
|
|
896
931
|
}
|
|
897
932
|
async function loginWithOAuth() {
|
|
898
933
|
const result = await startOAuthLogin();
|
|
@@ -922,7 +957,7 @@ async function loginWithOAuth() {
|
|
|
922
957
|
name: user.firstName ?? result.name
|
|
923
958
|
});
|
|
924
959
|
console.log(`Logged in as: ${user.firstName ?? "Unknown"} (${user.email ?? "no email"})`);
|
|
925
|
-
console.log(
|
|
960
|
+
console.log(`Credentials saved to ${join4(CONFIG_DIR, "tokens.json")}`);
|
|
926
961
|
}
|
|
927
962
|
|
|
928
963
|
// src/commands/profile.ts
|
|
@@ -1661,10 +1696,10 @@ function registerLabResultsCommands(program2) {
|
|
|
1661
1696
|
});
|
|
1662
1697
|
labResults.command("upload").description("Upload a lab result PDF for processing").requiredOption("--file <path>", "Path to the PDF file").action(async (opts, cmd) => {
|
|
1663
1698
|
try {
|
|
1664
|
-
const { existsSync:
|
|
1699
|
+
const { existsSync: existsSync6 } = await import("fs");
|
|
1665
1700
|
const { resolve } = await import("path");
|
|
1666
1701
|
const filePath = resolve(opts.file);
|
|
1667
|
-
if (!
|
|
1702
|
+
if (!existsSync6(filePath)) {
|
|
1668
1703
|
console.error(`File not found: ${filePath}`);
|
|
1669
1704
|
process.exit(1);
|
|
1670
1705
|
}
|
|
@@ -2127,21 +2162,20 @@ function registerSchemaCommand(program2) {
|
|
|
2127
2162
|
}
|
|
2128
2163
|
|
|
2129
2164
|
// src/commands/debug.ts
|
|
2130
|
-
import {
|
|
2131
|
-
import { join as join3 } from "path";
|
|
2165
|
+
import { join as join5 } from "path";
|
|
2132
2166
|
import { existsSync as existsSync3 } from "fs";
|
|
2133
2167
|
function registerDebugCommands(program2) {
|
|
2134
2168
|
const debug = program2.command("debug", { hidden: true }).description("Internal debug utilities");
|
|
2135
2169
|
debug.command("config").description("Show resolved configuration").action((_, cmd) => {
|
|
2136
|
-
const credentialsPath =
|
|
2137
|
-
const tokensPath =
|
|
2170
|
+
const credentialsPath = join5(CONFIG_DIR, "credentials.json");
|
|
2171
|
+
const tokensPath = join5(CONFIG_DIR, "tokens.json");
|
|
2138
2172
|
const stored = loadCredentials();
|
|
2139
2173
|
const tokens = loadTokens();
|
|
2140
2174
|
const envKey = process.env.BETTERNESS_API_KEY;
|
|
2141
2175
|
const parentOpts = cmd.optsWithGlobals();
|
|
2142
|
-
const authSource = parentOpts.apiKey ? "--api-key flag" : envKey ? "BETTERNESS_API_KEY env" : tokens && !isTokenExpired(tokens) ?
|
|
2176
|
+
const authSource = parentOpts.apiKey ? "--api-key flag" : envKey ? "BETTERNESS_API_KEY env" : tokens && !isTokenExpired(tokens) ? `OAuth tokens (${tokensPath})` : stored ? `API key (${credentialsPath})` : "none";
|
|
2143
2177
|
const config = {
|
|
2144
|
-
version: "1.3.
|
|
2178
|
+
version: "1.3.4",
|
|
2145
2179
|
apiUrl: "https://api.betterness.ai",
|
|
2146
2180
|
auth0Domain: "betterness.us.auth0.com",
|
|
2147
2181
|
auth0ClientId: "g4lqYHRQb2QMgdRKIlKwoTJl6eu41pWn",
|
|
@@ -2449,17 +2483,17 @@ function formatAnswerValue(value) {
|
|
|
2449
2483
|
|
|
2450
2484
|
// src/commands/mcp.ts
|
|
2451
2485
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3, copyFileSync } from "fs";
|
|
2452
|
-
import { join as
|
|
2453
|
-
import { homedir as
|
|
2486
|
+
import { join as join6, dirname } from "path";
|
|
2487
|
+
import { homedir as homedir2, platform } from "os";
|
|
2454
2488
|
var CLIENTS = {
|
|
2455
2489
|
claude: {
|
|
2456
2490
|
name: "Claude Desktop",
|
|
2457
2491
|
supportsScope: false,
|
|
2458
2492
|
configPath: () => {
|
|
2459
2493
|
if (platform() === "darwin") {
|
|
2460
|
-
return
|
|
2494
|
+
return join6(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
2461
2495
|
}
|
|
2462
|
-
return
|
|
2496
|
+
return join6(homedir2(), ".config", "Claude", "claude_desktop_config.json");
|
|
2463
2497
|
}
|
|
2464
2498
|
},
|
|
2465
2499
|
"claude-code": {
|
|
@@ -2467,20 +2501,20 @@ var CLIENTS = {
|
|
|
2467
2501
|
supportsScope: true,
|
|
2468
2502
|
configPath: (scope) => {
|
|
2469
2503
|
if (scope === "project") {
|
|
2470
|
-
return
|
|
2504
|
+
return join6(process.cwd(), ".mcp.json");
|
|
2471
2505
|
}
|
|
2472
|
-
return
|
|
2506
|
+
return join6(homedir2(), ".claude", "settings.json");
|
|
2473
2507
|
}
|
|
2474
2508
|
},
|
|
2475
2509
|
cursor: {
|
|
2476
2510
|
name: "Cursor",
|
|
2477
2511
|
supportsScope: false,
|
|
2478
|
-
configPath: () =>
|
|
2512
|
+
configPath: () => join6(process.cwd(), ".cursor", "mcp.json")
|
|
2479
2513
|
},
|
|
2480
2514
|
windsurf: {
|
|
2481
2515
|
name: "Windsurf",
|
|
2482
2516
|
supportsScope: false,
|
|
2483
|
-
configPath: () =>
|
|
2517
|
+
configPath: () => join6(homedir2(), ".codeium", "windsurf", "mcp_config.json")
|
|
2484
2518
|
}
|
|
2485
2519
|
};
|
|
2486
2520
|
var VALID_CLIENTS = Object.keys(CLIENTS).join(", ");
|
|
@@ -2667,10 +2701,75 @@ Restart ${client.name} to activate. Then try asking:
|
|
|
2667
2701
|
});
|
|
2668
2702
|
}
|
|
2669
2703
|
|
|
2704
|
+
// src/commands/knowledge.ts
|
|
2705
|
+
var knowledgeColumns = [
|
|
2706
|
+
{ key: "title", label: "Title", width: 35 },
|
|
2707
|
+
{ key: "type", label: "Type", width: 10 },
|
|
2708
|
+
{ key: "authors", label: "Authors", width: 20 },
|
|
2709
|
+
{ key: "score", label: "Score", width: 6, align: "right" },
|
|
2710
|
+
{ key: "date", label: "Date", width: 12 }
|
|
2711
|
+
];
|
|
2712
|
+
var responseSchema = simplePageSchema(knowledgeSearchResultSchema);
|
|
2713
|
+
function mapRows(results) {
|
|
2714
|
+
return results.map((r) => ({
|
|
2715
|
+
externalId: r.externalId,
|
|
2716
|
+
title: r.title ?? "",
|
|
2717
|
+
type: r.type ?? "",
|
|
2718
|
+
authors: r.authors?.map((a) => a.name).join(", ") ?? "",
|
|
2719
|
+
score: r.averageScore ?? "-",
|
|
2720
|
+
date: r.releaseDate ?? "",
|
|
2721
|
+
url: r.url ?? "",
|
|
2722
|
+
coverImageUrl: r.coverImageUrl ?? "",
|
|
2723
|
+
reviewCount: r.reviewCount ?? 0
|
|
2724
|
+
}));
|
|
2725
|
+
}
|
|
2726
|
+
function registerKnowledgeCommands(program2) {
|
|
2727
|
+
const knowledge = program2.command("knowledge").description("Search the Betterness knowledge library");
|
|
2728
|
+
knowledge.command("search").description("Search Betterness-published knowledge (files and videos)").option("-s, --search <text>", "Full-text search query").option("--type <type>", "Filter by type: FILE or VIDEO (default: both)").option("--sort <method>", "Sort: ASC, DESC, LATEST, OLDEST", "LATEST").option("--page <n>", "Page number (0-indexed)", "0").option("--page-size <n>", "Results per page", "10").action(async (opts, cmd) => {
|
|
2729
|
+
try {
|
|
2730
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2731
|
+
const client = await createApiClient({ apiKey: globalOpts.apiKey });
|
|
2732
|
+
const params = {
|
|
2733
|
+
page: opts.page,
|
|
2734
|
+
pageSize: opts.pageSize
|
|
2735
|
+
};
|
|
2736
|
+
const body = {
|
|
2737
|
+
sortMethod: opts.sort.toUpperCase()
|
|
2738
|
+
};
|
|
2739
|
+
if (opts.search) body.search = opts.search;
|
|
2740
|
+
if (opts.type) {
|
|
2741
|
+
const t = opts.type.toUpperCase();
|
|
2742
|
+
if (t !== "FILE" && t !== "VIDEO") {
|
|
2743
|
+
process.stderr.write("Error: --type must be FILE or VIDEO\n");
|
|
2744
|
+
process.exitCode = 1;
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
body.knowledgeType = t;
|
|
2748
|
+
}
|
|
2749
|
+
const response = await client.post(
|
|
2750
|
+
"/api/library-knowledge/betterness",
|
|
2751
|
+
body,
|
|
2752
|
+
params,
|
|
2753
|
+
responseSchema
|
|
2754
|
+
);
|
|
2755
|
+
const rows = mapRows(response.content);
|
|
2756
|
+
if (!globalOpts.json && !globalOpts.quiet) {
|
|
2757
|
+
const showing = response.content.length;
|
|
2758
|
+
const total = response.totalResults;
|
|
2759
|
+
process.stderr.write(`Page ${response.number + 1} \u2014 ${showing} of ${total} results${response.last ? " (last page)" : ""}
|
|
2760
|
+
`);
|
|
2761
|
+
}
|
|
2762
|
+
outputList(cmd, rows, knowledgeColumns);
|
|
2763
|
+
} catch (error) {
|
|
2764
|
+
outputError(error);
|
|
2765
|
+
}
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2670
2769
|
// src/program.ts
|
|
2671
2770
|
function createProgram() {
|
|
2672
2771
|
const program2 = new Command();
|
|
2673
|
-
program2.name("betterness").description("Betterness CLI - Agent-first terminal interface for the Betterness platform").version("1.3.
|
|
2772
|
+
program2.name("betterness").description("Betterness CLI - Agent-first terminal interface for the Betterness platform").version("1.3.4").option("--api-key <key>", "API key (overrides env and stored credentials)").option("--json", "Output as JSON").option("--markdown", "Output as Markdown").option("--quiet", "Suppress output (exit code only)");
|
|
2674
2773
|
registerAuthCommands(program2);
|
|
2675
2774
|
registerProfileCommands(program2);
|
|
2676
2775
|
registerBiomarkersCommands(program2);
|
|
@@ -2691,9 +2790,88 @@ function createProgram() {
|
|
|
2691
2790
|
registerDebugCommands(program2);
|
|
2692
2791
|
registerHealthProfileCommands(program2);
|
|
2693
2792
|
registerMcpCommands(program2);
|
|
2793
|
+
registerKnowledgeCommands(program2);
|
|
2694
2794
|
return program2;
|
|
2695
2795
|
}
|
|
2696
2796
|
|
|
2797
|
+
// src/updateCheck.ts
|
|
2798
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
|
|
2799
|
+
import { join as join7 } from "path";
|
|
2800
|
+
var CACHE_FILE = join7(CONFIG_DIR, "update-check.json");
|
|
2801
|
+
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
2802
|
+
var REGISTRY_TIMEOUT_MS = 3e3;
|
|
2803
|
+
function loadCache() {
|
|
2804
|
+
if (!existsSync5(CACHE_FILE)) return null;
|
|
2805
|
+
try {
|
|
2806
|
+
return JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
|
|
2807
|
+
} catch {
|
|
2808
|
+
return null;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
function saveCache(cache) {
|
|
2812
|
+
if (!existsSync5(CONFIG_DIR)) {
|
|
2813
|
+
mkdirSync4(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
2814
|
+
}
|
|
2815
|
+
writeFileSync4(CACHE_FILE, JSON.stringify(cache), { mode: 384 });
|
|
2816
|
+
}
|
|
2817
|
+
function isNewer(latest, current) {
|
|
2818
|
+
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
2819
|
+
const [lMaj, lMin, lPat] = parse(latest);
|
|
2820
|
+
const [cMaj, cMin, cPat] = parse(current);
|
|
2821
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
2822
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
2823
|
+
return lPat > cPat;
|
|
2824
|
+
}
|
|
2825
|
+
async function fetchLatestVersion() {
|
|
2826
|
+
const controller = new AbortController();
|
|
2827
|
+
const timeoutId = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);
|
|
2828
|
+
try {
|
|
2829
|
+
const res = await fetch("https://registry.npmjs.org/@betterness/cli/latest", {
|
|
2830
|
+
headers: { Accept: "application/json" },
|
|
2831
|
+
signal: controller.signal
|
|
2832
|
+
});
|
|
2833
|
+
clearTimeout(timeoutId);
|
|
2834
|
+
if (!res.ok) return null;
|
|
2835
|
+
const data = await res.json();
|
|
2836
|
+
return data.version ?? null;
|
|
2837
|
+
} catch {
|
|
2838
|
+
clearTimeout(timeoutId);
|
|
2839
|
+
return null;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
function scheduleUpdateCheck() {
|
|
2843
|
+
const currentVersion = "1.3.4";
|
|
2844
|
+
if (currentVersion === "0.0.0") return;
|
|
2845
|
+
const cache = loadCache();
|
|
2846
|
+
if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
|
|
2847
|
+
if (isNewer(cache.latestVersion, currentVersion)) {
|
|
2848
|
+
printNotice(currentVersion, cache.latestVersion);
|
|
2849
|
+
}
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
fetchLatestVersion().then((latest) => {
|
|
2853
|
+
if (!latest) return;
|
|
2854
|
+
saveCache({ lastCheck: Date.now(), latestVersion: latest });
|
|
2855
|
+
if (isNewer(latest, currentVersion)) {
|
|
2856
|
+
printNotice(currentVersion, latest);
|
|
2857
|
+
}
|
|
2858
|
+
}).catch(() => {
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
function printNotice(current, latest) {
|
|
2862
|
+
const yellow = "\x1B[33m";
|
|
2863
|
+
const bold = "\x1B[1m";
|
|
2864
|
+
const reset = "\x1B[0m";
|
|
2865
|
+
process.stderr.write(
|
|
2866
|
+
`
|
|
2867
|
+
${yellow}Update available: ${bold}${current}${reset}${yellow} \u2192 ${bold}${latest}${reset}
|
|
2868
|
+
${yellow}Run \`npm i -g @betterness/cli\` to update.${reset}
|
|
2869
|
+
|
|
2870
|
+
`
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2697
2874
|
// src/index.ts
|
|
2875
|
+
scheduleUpdateCheck();
|
|
2698
2876
|
var program = createProgram();
|
|
2699
2877
|
program.parse();
|