@drakulavich/oura-cli 0.4.5 → 0.5.0

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/CHANGELOG.md CHANGED
@@ -6,6 +6,71 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.0] - 2026-09-05
10
+
11
+ ### Breaking
12
+ - Per-collection API commands replaced by `fetch <collection>`; `db reset` and `db import` removed. See "Changed" and "Removed" below.
13
+
14
+ ### Changed
15
+ - `sync` fetches heartrate over the same window as every other collection (from the last synced day, or 30 days back on first sync) instead of today only, in pieces of at most 30 × 24 hours because the heartrate endpoint rejects longer ranges; consecutive pieces do not share a boundary instant, so no sample is fetched twice. Days the ring synced while `oura-cli sync` did not run are no longer skipped; the unique `(timestamp, source)` index keeps re-fetched samples from duplicating.
16
+ - README "First five minutes" now leads with a concrete report sample,
17
+ documents `login` → `doctor` → `sync` → `report` as the onboarding path,
18
+ states plainly that `login` hides the token as you type, and adds a
19
+ symptom → command recovery table.
20
+ - Text formatters moved from the repo root into `src/render/` (internal).
21
+ - Every data command runs through one runner that resolves the output format, opens and always closes the database, creates the API client and maps errors to exit codes. `sync` progress lines are printed together with the summary instead of streaming. `--no-color` now takes effect before any output (internal).
22
+ - Token resolution lives in `src/api/token.ts` and is shared by the API client and `doctor`; the two SQLite wrappers merged into `src/db/open.ts` (internal). A whitespace-only token is now rejected as missing instead of being sent as an empty bearer token.
23
+ - Every Oura collection is described once in `src/collections/`; table DDL, inserts and the sync loop derive from it. A test proves the derived DDL matches the shipped migrations (internal).
24
+ - **Breaking:** the seven per-collection commands (`sleep`, `readiness`, `activity`, `hr`, `spo2`, `stress`, `workout` × `today|date|week`) are replaced by `oura-cli fetch <collection> [--day D | --from A --to B | --days N]`. `sleep-periods` and `cv-age` are now fetchable too.
25
+ - `describe` and `manifest` are generated from the registered command tree, so they can no longer drift from the CLI; `manifest` examples that referenced a non-existent `--start` flag are gone.
26
+ - `docs/schemas/<collection>.json` are generated (`bun run schemas`) and checked by a test; added `sleep-periods.json` and `cv-age.json`.
27
+
28
+ ### Removed
29
+ - `db reset` and the CSV importer it relied on (it read a hard-coded personal directory), and the `db import` alias of `sync`.
30
+ - `vo2max` no longer appears in `db stats`; the table was never populated. `db stats` now lists tables in registry order (`heartrate` fourth, `cardiovascular_age` last); key by `table`, not position.
31
+
32
+ ### Added
33
+ - `oura-cli doctor` diagnoses token resolution (honoring `--token` like every
34
+ other command), live token validity (skippable with `--offline`), local
35
+ database health, and data freshness across sleep/readiness/activity in one
36
+ pass. `ok` is true only when every check is clean — a warning is enough to
37
+ clear "everything looks healthy". `nextStep` is the first non-ok check's
38
+ fix, so it never recommends a command (like `sync`) that would just hit the
39
+ same root cause (like an unreachable API) a check upstream already found.
40
+ Both `table` and `json` output are supported; the JSON shape is published
41
+ as `docs/schemas/doctor.json`.
42
+
43
+ ### Fixed
44
+ - `fetch --day/--from/--to` and `db date` reject calendar-invalid dates such as `2026-02-30` with `BAD_ARGS` instead of sending them to the API or querying nothing. `db trends <days>` rejects anything but a positive integer instead of failing with `UNKNOWN` (`abc`) or silently returning nothing (`0`).
45
+ - An unknown `--format` is rejected for `fetch` too (it used to be silently accepted); `fetch` errors now render as text on a TTY and as JSON when piped.
46
+ - An unknown `--tz` / `OURA_TZ` is reported as `BAD_ARGS` with a hint instead of an `UNKNOWN` error from the date formatter.
47
+ - `fetch hr` and the heartrate step of `sync` now send `start_datetime`/`end_datetime` (UTC instants covering the requested local days in `OURA_TZ`), which is what the Oura heartrate endpoint takes; every other collection keeps `start_date`/`end_date`. Previously heartrate was queried with date parameters the endpoint does not define.
48
+ - The API client follows `next_token` and returns every page, so ranges longer than one page (heartrate over several days) are no longer silently truncated.
49
+ - Local-day boundaries are computed at local midnight instead of noon, so `fetch hr --day` on a DST transition day covers the whole 23- or 25-hour day rather than shifting by an hour (internal `localDateToUtcRange`, previously unused).
50
+ - Interactive `oura-cli login` now hides the Personal Access Token while it is
51
+ typed and explains how to use `--token` safely in non-interactive contexts.
52
+ - `oura-cli report` now explains when no data is available and directs new
53
+ users to run `oura-cli sync`, instead of showing an all-empty report table.
54
+ - Reports containing only sleep-detail data are no longer mistaken for empty
55
+ reports and continue to show the available sleep metrics.
56
+ - `oura-cli db today` and `oura-cli db week` now explain when the local cache
57
+ has no data for the requested range and point at `oura-cli sync`, instead of
58
+ showing an all-dash table. JSON output is unchanged.
59
+ - `oura-cli sync` now says explicitly when it is a first sync backfilling the
60
+ default 30 days, names the resolved date range for both first and
61
+ incremental syncs, and prints a per-collection import count summary in
62
+ table mode. JSON output gains an additive `import.isFirstSync` boolean.
63
+ - `OuraStressDay.day_summary`, `OuraWorkout.label` and `OuraSleepModel.type` are
64
+ now typed `?: string | null`, matching the Oura OpenAPI spec (checked against
65
+ 1.37), which marks all three nullable and omits them from `required`. The
66
+ declared types claimed a value is always present, so code reading them could
67
+ assume one that never arrives. `importDaily` now maps an absent value to
68
+ `NULL` explicitly instead of relying on `bun:sqlite` silently coercing
69
+ `undefined`.
70
+ - `db week`, `db trends`, `db stats`, `report` and `sync` now compute "today"
71
+ and every window boundary in the configured timezone (`--tz` / `OURA_TZ`);
72
+ previously they used UTC and disagreed with `db today` around midnight.
73
+
9
74
  ## [0.4.5] - 2026-07-18
10
75
 
11
76
  ### Removed
package/README.md CHANGED
@@ -26,17 +26,45 @@ curl -fsSL https://bun.sh/install | bash # if you don't have Bun yet
26
26
  bun add -g @drakulavich/oura-cli
27
27
  ```
28
28
 
29
- You'll also need a [Personal Access Token from Oura](https://cloud.ouraring.com/personal-access-tokens). Paste it into `oura-cli login` once — it lands at `~/.oura-token` with `0600` perms.
29
+ You'll also need a [Personal Access Token from Oura](https://cloud.ouraring.com/personal-access-tokens). Run `oura-cli login` once — it hides the token as you type (nothing is echoed to the terminal) and saves it to `~/.oura-token` with `0600` perms.
30
30
 
31
31
  ## First five minutes
32
32
 
33
+ Five minutes from now you'll have your week of sleep, readiness and activity in a terminal digest like this:
34
+
35
+ ```
36
+ Oura Weekly Report
37
+ 2026-08-24 — 2026-08-30
38
+
39
+ Last 7 Days:
40
+ ────────────────────────────────────────────────────
41
+ Day Sleep Ready Active Steps
42
+ ────────────────────────────────────────────────────
43
+ Mon 24/08 87 74 68 9,668
44
+ Tue 25/08 82 79 74 11,204
45
+ ...
46
+ ```
47
+
48
+ Four commands get you there:
49
+
33
50
  ```bash
34
- oura-cli login # paste your PAT, one time
35
- oura-cli sync # backfill recent days into ~/.oura-cli/oura.db
36
- oura-cli report # weekly digest in the terminal
51
+ oura-cli login # paste your PAT — input is hidden, nothing echoes to the terminal
52
+ oura-cli doctor # confirm the token works and the local database is ready
53
+ oura-cli sync # first sync backfills the last 30 days; later syncs are incremental
54
+ oura-cli report # weekly digest in the terminal
37
55
  ```
38
56
 
39
- That's it. Subsequent `oura-cli sync` pulls only new days.
57
+ Subsequent `oura-cli sync` only pulls new days, and `oura-cli db today` / `oura-cli db week` read the local cache instantly, no API call.
58
+
59
+ ### If something looks wrong
60
+
61
+ | What you see | What to run |
62
+ |---|---|
63
+ | `No Oura data is available for this report yet.` | `oura-cli sync` |
64
+ | `No Oura access token at ~/.oura-token` | `oura-cli login` |
65
+ | `Oura API 401` | `oura-cli login` with a fresh PAT |
66
+ | `db today` empty right after a sync | Normal — Oura publishes a day's summary after that night's sleep syncs from the ring. |
67
+ | Anything else | `oura-cli doctor` |
40
68
 
41
69
  ## Daily use
42
70
 
@@ -58,7 +86,7 @@ oura-cli db date 2026-05-10
58
86
 
59
87
  ```bash
60
88
  oura-cli db week # local cache summary, no API hit
61
- oura-cli sleep week # fresh sleep details direct from Oura
89
+ oura-cli fetch sleep --days 7 # fresh sleep details direct from Oura
62
90
  ```
63
91
 
64
92
  ### Reports
@@ -77,26 +105,25 @@ oura-cli db trends 30 # score trends across the last 30 days
77
105
  oura-cli db stats # row counts, date range, personal bests
78
106
  ```
79
107
 
80
- ### Per-endpoint detail
108
+ ### Raw API records
81
109
 
82
- When you want raw Oura V2 data, every endpoint shares the same shape `today | date <day> | week`:
110
+ `fetch` returns one collection straight from the Oura API as JSON, without touching the local cache.
83
111
 
84
112
  ```bash
85
- oura-cli sleep today
86
- oura-cli readiness date 2026-05-10
87
- oura-cli activity week
88
- oura-cli hr week
89
- oura-cli spo2 week
90
- oura-cli stress week
91
- oura-cli workout week
113
+ oura-cli fetch sleep # today
114
+ oura-cli fetch hr --days 7 # last 7 days
115
+ oura-cli fetch workout --from 2026-05-01 --to 2026-05-31
116
+ oura-cli fetch sleep-periods --day 2026-06-01 | jq '.[] | {day, type, average_hrv}'
92
117
  ```
93
118
 
119
+ Collections: `sleep readiness activity hr spo2 stress workout sleep-periods cv-age`.
120
+
94
121
  ### Piping to other tools
95
122
 
96
123
  Output auto-switches to JSON the moment you pipe it:
97
124
 
98
125
  ```bash
99
- oura-cli sleep week | jq '.[] | {day, score, hrv: .contributors.hrv_balance}'
126
+ oura-cli fetch sleep --days 7 | jq '.[] | {day, score, hrv: .contributors.hrv_balance}'
100
127
  oura-cli db trends 90 > trends.json
101
128
  ```
102
129
 
@@ -146,7 +173,7 @@ If you're driving the CLI from a script or LLM harness:
146
173
  - `oura-cli healthcheck` — `{ok, version, latencyMs}` JSON for liveness probes.
147
174
  - Errors emit a stable JSON envelope on stderr: `{"error":{"code":"…","message":"…","hint":"…"}}`.
148
175
  - Documented exit codes: `0` success, `1` user error, `2` auth, `3` API, `4` storage.
149
- - JSON Schemas under [`docs/schemas/`](docs/schemas/) describe every output shape, semver-stable.
176
+ - JSON Schemas under [`docs/schemas/`](docs/schemas/) describe every output shape, semver-stable. Per-collection schemas pin the identity fields (`id`, `day`, `timestamp`) and allow the rest of the Oura record through unchanged, so new upstream fields never break validation.
150
177
 
151
178
  Plays cleanly with [OpenClaw](https://github.com/openclaw/openclaw) — `oura-cli manifest` returns the tool-registry shape. A first-party `oura-mcp` companion is on the roadmap.
152
179