@drakulavich/oura-cli 0.4.4 → 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,87 @@ 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
+
74
+ ## [0.4.5] - 2026-07-18
75
+
76
+ ### Removed
77
+ - Dropped the unused `@typescript/native-preview` devDependency. The
78
+ type-check now runs on the stable `typescript` compiler again: CI uses
79
+ `bunx tsc --noEmit` in place of `bunx tsgo --noEmit`.
80
+
81
+ ### Added
82
+ - CI now ships test runs to [flakiness.io](https://flakiness.io). `bun test`
83
+ emits JUnit XML, the [official `flakiness` CLI](https://github.com/flakiness/flakiness-report)
84
+ converts it to a Flakiness JSON Report (`--category bun`) and uploads to the
85
+ `Laputa/oura-cli` project. Auth via GitHub OIDC — no
86
+ `FLAKINESS_ACCESS_TOKEN` secret needed; the workflow grants `id-token: write`
87
+ permission for the upload. Zero new runtime/dev deps; the CLI is installed
88
+ ad-hoc via `curl https://cli.flakiness.io/install.sh | sh`.
89
+
9
90
  ## [0.4.4] - 2026-05-13
10
91
 
11
92
  ### Added
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">oura-cli</h1>
2
2
 
3
3
  <p align="center">
4
- <a href="https://github.com/drakulavich/oura-cli/actions/workflows/ci.yml"><img src="https://github.com/drakulavich/oura-cli/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
4
+ <a href="https://flakiness.io/Laputa/oura-cli"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fflakiness.io%2Fapi%2Fbadge%3Finput%3D%257B%2522badgeToken%2522%253A%2522badge-2qTwJcrJSmhJmKfKklQkKG%2522%257D" alt="Tests"></a>
5
5
  <a href="https://www.npmjs.com/package/@drakulavich/oura-cli"><img src="https://img.shields.io/npm/v/@drakulavich/oura-cli" alt="npm version"></a>
6
6
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
7
7
  <a href="https://bun.sh"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun" alt="Bun"></a>
@@ -11,16 +11,13 @@
11
11
  <p align="center"><b>Own your Oura Ring data.</b> Pull your sleep, readiness, activity, heart rate, SpO₂, stress, and workouts from the Oura Cloud API straight to your terminal. No mobile app. No telemetry. Just SQLite and your data.</p>
12
12
 
13
13
  <p align="center">
14
- <a href="https://github.com/drakulavich/oura-cli/raw/main/assets/demo.webm">
15
- <video src="https://github.com/drakulavich/oura-cli/raw/main/assets/demo.webm" width="720" controls muted playsinline></video>
16
- <em>Watch the 25-second demo</em>
17
- </a>
14
+ <img src="https://github.com/drakulavich/oura-cli/raw/main/assets/demo.gif" alt="oura-cli demo: --version, db today, db week, report, describe" width="720">
18
15
  </p>
19
16
 
20
17
  - **Offline-first.** Everything caches into `~/.oura-cli/oura.db` after one `oura-cli sync`. Reports keep working when your internet doesn't.
21
18
  - **Real terminal reports.** `oura-cli report` writes a weekly or monthly digest with averages, trend deltas, and "you slept poorly Tuesday" callouts. No dashboards, no logging in.
22
19
  - **Pipe-friendly.** Output auto-switches to stable JSON when stdout isn't a terminal. Analyse with `jq`, plot with `gnuplot`, or feed it into your own scripts.
23
- - **Single 142 kB binary, MIT, no telemetry.** Built on Bun; zero native dependencies.
20
+ - **Single 100 kB binary, MIT, no telemetry.** Built on Bun; zero native dependencies.
24
21
 
25
22
  ## Install
26
23
 
@@ -29,17 +26,45 @@ curl -fsSL https://bun.sh/install | bash # if you don't have Bun yet
29
26
  bun add -g @drakulavich/oura-cli
30
27
  ```
31
28
 
32
- 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.
33
30
 
34
31
  ## First five minutes
35
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
+
36
50
  ```bash
37
- oura-cli login # paste your PAT, one time
38
- oura-cli sync # backfill recent days into ~/.oura-cli/oura.db
39
- 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
40
55
  ```
41
56
 
42
- 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` |
43
68
 
44
69
  ## Daily use
45
70
 
@@ -61,7 +86,7 @@ oura-cli db date 2026-05-10
61
86
 
62
87
  ```bash
63
88
  oura-cli db week # local cache summary, no API hit
64
- oura-cli sleep week # fresh sleep details direct from Oura
89
+ oura-cli fetch sleep --days 7 # fresh sleep details direct from Oura
65
90
  ```
66
91
 
67
92
  ### Reports
@@ -80,26 +105,25 @@ oura-cli db trends 30 # score trends across the last 30 days
80
105
  oura-cli db stats # row counts, date range, personal bests
81
106
  ```
82
107
 
83
- ### Per-endpoint detail
108
+ ### Raw API records
84
109
 
85
- 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.
86
111
 
87
112
  ```bash
88
- oura-cli sleep today
89
- oura-cli readiness date 2026-05-10
90
- oura-cli activity week
91
- oura-cli hr week
92
- oura-cli spo2 week
93
- oura-cli stress week
94
- 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}'
95
117
  ```
96
118
 
119
+ Collections: `sleep readiness activity hr spo2 stress workout sleep-periods cv-age`.
120
+
97
121
  ### Piping to other tools
98
122
 
99
123
  Output auto-switches to JSON the moment you pipe it:
100
124
 
101
125
  ```bash
102
- 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}'
103
127
  oura-cli db trends 90 > trends.json
104
128
  ```
105
129
 
@@ -139,7 +163,7 @@ This tool reads your personal health data — handle the token with care.
139
163
  | Sleep model | Oura V2 `sleep` | `sleep_model` |
140
164
  | Cardiovascular age | Oura V2 `cardiovascular_age` | `cardiovascular_age` |
141
165
 
142
- Runtime: [Bun](https://bun.sh). Storage: built-in `bun:sqlite`. CLI parsing: [Commander](https://github.com/tj/commander.js). Output styling: [chalk](https://github.com/chalk/chalk). One 142 kB `dist/index.js`, no native deps.
166
+ Runtime: [Bun](https://bun.sh). Storage: built-in `bun:sqlite`. CLI parsing: [citty](https://github.com/unjs/citty). Output styling: [chalk](https://github.com/chalk/chalk). One 100 kB `dist/index.js`, no native deps.
143
167
 
144
168
  ## Automation (LLM agents, scripts, MCP)
145
169
 
@@ -149,7 +173,7 @@ If you're driving the CLI from a script or LLM harness:
149
173
  - `oura-cli healthcheck` — `{ok, version, latencyMs}` JSON for liveness probes.
150
174
  - Errors emit a stable JSON envelope on stderr: `{"error":{"code":"…","message":"…","hint":"…"}}`.
151
175
  - Documented exit codes: `0` success, `1` user error, `2` auth, `3` API, `4` storage.
152
- - 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.
153
177
 
154
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.
155
179