@drakulavich/oura-cli 0.1.1 → 0.1.2

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,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.2] - 2026-05-13
10
+
11
+ ### Fixed
12
+ - `oura-cli describe` now reports the real CLI surface. Data commands
13
+ (sleep, readiness, activity, hr, spo2, stress, workout) advertise their
14
+ `today | date <day> | week` subcommands instead of fictional `--start/--end`
15
+ flags. `db` and `report` likewise list their real subcommands.
16
+
17
+ ### Added
18
+ - `ManifestCommand.subcommands` field in the describe manifest, with a sibling
19
+ `$def` in `docs/schemas/describe.json`.
20
+
9
21
  ## [0.1.1] - 2026-05-13
10
22
 
11
23
  ### Added
@@ -30,5 +42,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
30
42
  - Local SQLite cache at `~/.oura-cli/oura.db`.
31
43
  - Auth via `oura-cli login`, `OURA_TOKEN`, `OURA_TOKEN_PATH`, or `~/.oura-token`.
32
44
 
45
+ [0.1.2]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.2
33
46
  [0.1.1]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.1
34
47
  [0.1.0]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,54 +1,110 @@
1
- # oura-cli
1
+ <h1 align="center">oura-cli</h1>
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@drakulavich/oura-cli.svg)](https://www.npmjs.com/package/@drakulavich/oura-cli)
4
- [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
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>
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
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
7
+ <a href="https://bun.sh"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun" alt="Bun"></a>
8
+ </p>
5
9
 
6
- A command-line tool for **Oura Ring** users to query and analyze their own health data locally. Designed for two audiences: people in a terminal, and AI agents driving the CLI programmatically.
10
+ <p align="center"><b>Own your Oura Ring data.</b> Local CLI that pulls your biometrics from the Oura Cloud API, caches them in SQLite, and serves both your terminal and your AI agent from the same binary.</p>
7
11
 
8
- - Fetches sleep, readiness, activity, heart rate, SpO₂, stress, and workouts from the Oura Cloud API.
9
- - Caches everything in a local SQLite database (`~/.oura-cli/oura.db`) so you can query and report offline.
10
- - Outputs human-friendly tables when run interactively; emits stable JSON when piped or invoked by a parent process.
11
- - Self-describes via `oura-cli describe` so agents can discover commands, arguments, and output schemas.
12
+ - **Two audiences, one binary** pretty tables when you're at a terminal, stable JSON when stdout is piped to a parent process
13
+ - **Self-describing** `oura-cli describe` emits a JSON manifest of every command, argument, and output schema. Agents discover capabilities without scraping `--help`
14
+ - **Local-first** everything lives in `~/.oura-cli/oura.db` after `oura-cli sync`. Query and report offline, no Oura mobile app required
15
+ - **Documented contract** — JSON Schemas under `docs/schemas/`, semver-versioned, plus machine-readable errors and exit codes (0–4) for clean error handling in scripts and agents
12
16
 
13
- ## Install
17
+ ## Quick Start
18
+
19
+ Runtime: **[Bun](https://bun.sh)** >= 1.0.
14
20
 
15
21
  ```bash
16
- npm install -g @drakulavich/oura-cli
22
+ curl -fsSL https://bun.sh/install | bash # skip if Bun is already installed
23
+
24
+ bun add -g @drakulavich/oura-cli
25
+ oura-cli login # paste your Personal Access Token (one-time)
26
+ oura-cli sync # backfill recent days into ~/.oura-cli/oura.db
27
+ oura-cli report weekly # weekly summary with trends and recommendations
17
28
  ```
18
29
 
19
- Requires [Bun](https://bun.sh/) at runtime (the binary uses `#!/usr/bin/env bun`).
30
+ Get a Personal Access Token at <https://cloud.ouraring.com/personal-access-tokens>.
20
31
 
21
32
  ## For humans
22
33
 
34
+ Output format auto-detects: tables in your terminal, JSON when piped.
35
+
23
36
  ```bash
24
- oura-cli login # paste your Personal Access Token (one-time)
25
- oura-cli sync # pull the last 90 days into ~/.oura-cli/oura.db
26
- oura-cli report --week # render a weekly summary in the terminal
37
+ oura-cli sync # pull the latest from Oura Cloud
38
+ oura-cli db today # today's summary
39
+ oura-cli db date 2026-05-10 # any specific day
40
+ oura-cli db week # last 7 days
41
+ oura-cli db trends 30 # score trends across last 30 days
42
+ oura-cli db stats # row counts, date range, personal bests
43
+ oura-cli report weekly # narrative weekly summary
27
44
  ```
28
45
 
29
- Get a Personal Access Token at <https://cloud.ouraring.com/personal-access-tokens>.
46
+ Pipe a result to your favourite JSON tool — `--format` is auto-detected, no flag needed:
30
47
 
31
- By default, output formatting auto-detects: pretty tables in your terminal, JSON when piped.
48
+ ```bash
49
+ oura-cli sleep week | jq '.[] | {day, score, hrv: .contributors.hrv_balance}'
50
+ ```
51
+
52
+ Per-endpoint fetches mirror Oura's V2 API one-to-one and share the same subcommand shape:
53
+
54
+ ```bash
55
+ oura-cli sleep today # daily_sleep, today
56
+ oura-cli readiness date 2026-05-10 # daily_readiness, specific day
57
+ oura-cli activity week # daily_activity, last 7 days
58
+ oura-cli hr week # heartrate samples
59
+ oura-cli spo2 week # daily_spo2
60
+ oura-cli stress week # daily_stress
61
+ oura-cli workout week # workouts
62
+ ```
32
63
 
33
64
  ## For agents
34
65
 
66
+ Designed for child-process invocation by LLM harnesses (Claude Code, Codex, generic MCP wrappers).
67
+
35
68
  ```bash
36
- export OURA_TOKEN="…" # no file or interactive flow needed
37
- oura-cli describe # JSON manifest of commands, args, output schemas
38
- oura-cli sleep --start 2026-05-01 # JSON (since stdout is non-TTY for child processes)
69
+ export OURA_TOKEN="…" # no file or interactive flow needed
70
+ oura-cli describe # JSON manifest of commands, args, schemas
71
+ oura-cli sleep today # JSON (stdout is non-TTY for child processes)
72
+ oura-cli healthcheck # JSON: {ok, version, latencyMs}
73
+ ```
74
+
75
+ **Stable JSON I/O contract.** Output shapes are versioned with the package; breaking changes are major semver bumps. Schemas live in `docs/schemas/`.
76
+
77
+ **Machine-readable errors.** When format resolves to `json`, every error emits a single line to stderr:
78
+
79
+ ```json
80
+ {"error":{"code":"TOKEN_MISSING","message":"…","hint":"Run `oura-cli login` or set OURA_TOKEN."}}
39
81
  ```
40
82
 
41
- - **Stable JSON I/O contract.** Output shapes are versioned with the package; breaking changes are major semver bumps. Schemas live in `docs/schemas/`.
42
- - **Machine-readable errors.** When `--format json` (or auto-detected), all errors emit a single `{"error": {"code": "...", "message": "...", "hint": "..."}}` line on stderr.
43
- - **Documented exit codes.**
83
+ **Exit codes:**
84
+
85
+ | Code | Meaning |
86
+ |------|---------|
87
+ | 0 | success |
88
+ | 1 | user error (bad arguments) |
89
+ | 2 | auth error (missing or invalid token) |
90
+ | 3 | API or network error |
91
+ | 4 | database or local storage error |
44
92
 
45
- | Code | Meaning |
46
- |------|------------------------------------------------|
47
- | 0 | success |
48
- | 1 | user error (bad arguments) |
49
- | 2 | auth error (missing or invalid token) |
50
- | 3 | API or network error |
51
- | 4 | database or local storage error |
93
+ ## What's Inside
94
+
95
+ | Endpoint | Source | Cached table |
96
+ |------------|-------------------------------------|-----------------------|
97
+ | Sleep | Oura V2 `daily_sleep` | `daily_sleep` |
98
+ | Readiness | Oura V2 `daily_readiness` | `daily_readiness` |
99
+ | Activity | Oura V2 `daily_activity` | `daily_activity` |
100
+ | Heart rate | Oura V2 `heartrate` | `heartrate` |
101
+ | SpO₂ | Oura V2 `daily_spo2` | `daily_spo2` |
102
+ | Stress | Oura V2 `daily_stress` | `daily_stress` |
103
+ | Workouts | Oura V2 `workout` | `workouts` |
104
+ | Sleep model | Oura V2 `sleep` | `sleep_model` |
105
+ | Cardiovascular age | Oura V2 `cardiovascular_age` | `cardiovascular_age` |
106
+
107
+ 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). Zero native dependencies, single 142 kB `dist/index.js`.
52
108
 
53
109
  ## Configuration
54
110
 
@@ -60,27 +116,21 @@ oura-cli sleep --start 2026-05-01 # JSON (since stdout is non-TTY for child pro
60
116
  | Timezone | `--tz` | `OURA_TZ` | system timezone, else `UTC` |
61
117
  | Output format | `--format` | | auto-detect (TTY → table) |
62
118
 
63
- ## Commands
119
+ ## Integrations
64
120
 
65
- Run `oura-cli --help` for the live list, or `oura-cli describe` for a machine-readable manifest.
121
+ - **OpenClaw** — drop into your LLM agent as an [OpenClaw skill](https://github.com/openclaw/openclaw). `oura-cli manifest` and `oura-cli healthcheck` report back in the tool-registry shape, so the agent can discover the binary and audit its DB health automatically.
122
+ - **MCP** — `oura-cli describe` returns enough metadata to autogenerate an MCP server wrapper. A first-party `oura-mcp` companion is on the roadmap.
66
123
 
67
- - `login` — interactively save a Personal Access Token to `~/.oura-token`
68
- - `describe` — emit JSON manifest (for agents)
69
- - `sleep | readiness | activity | hr | spo2 | stress | workout [--start YYYY-MM-DD] [--end YYYY-MM-DD]` — fetch from Oura API
70
- - `sync` — sync all collections into the local SQLite cache
71
- - `db <subcommand>` — query the local cache
72
- - `report --week | --month` — render a summary
124
+ ## Requirements
73
125
 
74
- ## Development
126
+ - [Bun](https://bun.sh) >= 1.0
127
+ - macOS, Linux, or Windows (WSL)
128
+ - An [Oura Personal Access Token](https://cloud.ouraring.com/personal-access-tokens)
75
129
 
76
- ```bash
77
- git clone https://github.com/drakulavich/oura-cli
78
- cd oura-cli
79
- bun install
80
- bun test
81
- bun run dev describe # run from source
82
- ```
130
+ ## Contributing
131
+
132
+ Bug reports and pull requests welcome at [drakulavich/oura-cli/issues](https://github.com/drakulavich/oura-cli/issues).
83
133
 
84
134
  ## License
85
135
 
86
- MIT © Anton Yakutovich
136
+ Made with 💍🤖 under MIT License.
package/dist/index.js CHANGED
@@ -3683,10 +3683,6 @@ function loginCommand() {
3683
3683
  }
3684
3684
 
3685
3685
  // src/commands/describe.ts
3686
- var DATE_ARGS = [
3687
- { name: "--start", type: "date", format: "YYYY-MM-DD", required: false, description: "Range start (inclusive)" },
3688
- { name: "--end", type: "date", format: "YYYY-MM-DD", required: false, description: "Range end (inclusive)" }
3689
- ];
3690
3686
  function buildManifest(version) {
3691
3687
  return {
3692
3688
  name: "oura-cli",
@@ -3716,19 +3712,106 @@ function buildManifest(version) {
3716
3712
  { name: "--path", type: "string", required: false, description: "Override token file path" }
3717
3713
  ] },
3718
3714
  { name: "describe", description: "Emit a machine-readable manifest of commands, args, and outputs.", args: [] },
3719
- { name: "sleep", description: "Fetch daily sleep scores from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/sleep.json" },
3720
- { name: "readiness", description: "Fetch daily readiness scores from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/readiness.json" },
3721
- { name: "activity", description: "Fetch daily activity scores from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/activity.json" },
3722
- { name: "hr", description: "Fetch heart rate samples from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/hr.json" },
3723
- { name: "spo2", description: "Fetch blood oxygen (SpO2) data from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/spo2.json" },
3724
- { name: "stress", description: "Fetch daily stress data from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/stress.json" },
3725
- { name: "workout", description: "Fetch workout data from Oura API.", args: DATE_ARGS, outputSchema: "docs/schemas/workout.json" },
3715
+ {
3716
+ name: "sleep",
3717
+ description: "Fetch daily sleep scores from Oura API. Pick a subcommand: today | date <day> | week.",
3718
+ args: [],
3719
+ outputSchema: "docs/schemas/sleep.json",
3720
+ subcommands: [
3721
+ { name: "today", description: "Today's sleep data.", args: [] },
3722
+ { name: "date", description: "Sleep data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3723
+ { name: "week", description: "Last 7 days of sleep data.", args: [] }
3724
+ ]
3725
+ },
3726
+ {
3727
+ name: "readiness",
3728
+ description: "Fetch daily readiness scores from Oura API. Pick a subcommand: today | date <day> | week.",
3729
+ args: [],
3730
+ outputSchema: "docs/schemas/readiness.json",
3731
+ subcommands: [
3732
+ { name: "today", description: "Today's readiness data.", args: [] },
3733
+ { name: "date", description: "Readiness data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3734
+ { name: "week", description: "Last 7 days of readiness data.", args: [] }
3735
+ ]
3736
+ },
3737
+ {
3738
+ name: "activity",
3739
+ description: "Fetch daily activity scores from Oura API. Pick a subcommand: today | date <day> | week.",
3740
+ args: [],
3741
+ outputSchema: "docs/schemas/activity.json",
3742
+ subcommands: [
3743
+ { name: "today", description: "Today's activity data.", args: [] },
3744
+ { name: "date", description: "Activity data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3745
+ { name: "week", description: "Last 7 days of activity data.", args: [] }
3746
+ ]
3747
+ },
3748
+ {
3749
+ name: "hr",
3750
+ description: "Fetch heart rate samples from Oura API. Pick a subcommand: today | date <day> | week.",
3751
+ args: [],
3752
+ outputSchema: "docs/schemas/hr.json",
3753
+ subcommands: [
3754
+ { name: "today", description: "Today's heart rate data.", args: [] },
3755
+ { name: "date", description: "Heart rate data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3756
+ { name: "week", description: "Last 7 days of heart rate data.", args: [] }
3757
+ ]
3758
+ },
3759
+ {
3760
+ name: "spo2",
3761
+ description: "Fetch blood oxygen (SpO2) data from Oura API. Pick a subcommand: today | date <day> | week.",
3762
+ args: [],
3763
+ outputSchema: "docs/schemas/spo2.json",
3764
+ subcommands: [
3765
+ { name: "today", description: "Today's SpO2 data.", args: [] },
3766
+ { name: "date", description: "SpO2 data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3767
+ { name: "week", description: "Last 7 days of SpO2 data.", args: [] }
3768
+ ]
3769
+ },
3770
+ {
3771
+ name: "stress",
3772
+ description: "Fetch daily stress data from Oura API. Pick a subcommand: today | date <day> | week.",
3773
+ args: [],
3774
+ outputSchema: "docs/schemas/stress.json",
3775
+ subcommands: [
3776
+ { name: "today", description: "Today's stress data.", args: [] },
3777
+ { name: "date", description: "Stress data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3778
+ { name: "week", description: "Last 7 days of stress data.", args: [] }
3779
+ ]
3780
+ },
3781
+ {
3782
+ name: "workout",
3783
+ description: "Fetch workout data from Oura API. Pick a subcommand: today | date <day> | week.",
3784
+ args: [],
3785
+ outputSchema: "docs/schemas/workout.json",
3786
+ subcommands: [
3787
+ { name: "today", description: "Today's workout data.", args: [] },
3788
+ { name: "date", description: "Workout data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3789
+ { name: "week", description: "Last 7 days of workout data.", args: [] }
3790
+ ]
3791
+ },
3726
3792
  { name: "sync", description: "Sync all Oura collections into the local database.", args: [] },
3727
- { name: "db", description: "Query the local SQLite cache.", args: [] },
3728
- { name: "report", description: "Render a weekly or monthly summary report.", args: [
3729
- { name: "--week", type: "boolean", description: "Render the last 7 days" },
3730
- { name: "--month", type: "boolean", description: "Render the last 30 days" }
3731
- ] }
3793
+ {
3794
+ name: "db",
3795
+ description: "Query and manage the local SQLite cache. Pick a subcommand.",
3796
+ args: [],
3797
+ subcommands: [
3798
+ { name: "today", description: "Today's summary from local DB.", args: [] },
3799
+ { name: "date", description: "Summary for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
3800
+ { name: "week", description: "Last 7 days from local DB.", args: [] },
3801
+ { name: "trends", description: "Score and metric trends over N days (default 30).", args: [{ name: "[days]", type: "number", required: false, description: "Window size in days." }] },
3802
+ { name: "stats", description: "Row counts, date range, record highs.", args: [] },
3803
+ { name: "import", description: "Sync new data from Oura API into the local DB.", args: [] },
3804
+ { name: "reset", description: "Destroy and rebuild the database from exported CSVs.", args: [] }
3805
+ ]
3806
+ },
3807
+ {
3808
+ name: "report",
3809
+ description: "Render a narrative health report. Pick a subcommand.",
3810
+ args: [],
3811
+ subcommands: [
3812
+ { name: "weekly", description: "Weekly health summary with trends and recommendations.", args: [] }
3813
+ ]
3814
+ }
3732
3815
  ]
3733
3816
  };
3734
3817
  }
@@ -3739,7 +3822,7 @@ function describeCommand(version) {
3739
3822
  }
3740
3823
 
3741
3824
  // src/index.ts
3742
- var VERSION = "0.1.1";
3825
+ var VERSION = "0.1.2";
3743
3826
  var program2 = new Command;
3744
3827
  program2.name("oura-cli").description("Oura Ring CLI \u2014 query and analyze Oura Ring health data. Designed for humans and agents.").version(VERSION).option("--format <format>", "Output format: table | json (default auto-detect by TTY)").option("--token <pat>", "Inline access token (prefer env vars or `oura-cli login`)").option("--db <path>", "Path to SQLite database file (env: OURA_DB_PATH)").option("--tz <timezone>", "Display timezone (env: OURA_TZ; default auto-detect)");
3745
3828
  program2.addCommand(loginCommand());
@@ -37,7 +37,8 @@
37
37
  "name": { "type": "string" },
38
38
  "description": { "type": "string" },
39
39
  "args": { "type": "array", "items": { "$ref": "#/$defs/arg" } },
40
- "outputSchema": { "type": "string" }
40
+ "outputSchema": { "type": "string" },
41
+ "subcommands": { "type": "array", "items": { "$ref": "#/$defs/subcommand" } }
41
42
  }
42
43
  }
43
44
  }
@@ -54,6 +55,15 @@
54
55
  "description": { "type": "string" },
55
56
  "values": { "type": "array", "items": { "type": "string" } }
56
57
  }
58
+ },
59
+ "subcommand": {
60
+ "type": "object",
61
+ "required": ["name", "description", "args"],
62
+ "properties": {
63
+ "name": { "type": "string" },
64
+ "description": { "type": "string" },
65
+ "args": { "type": "array", "items": { "$ref": "#/$defs/arg" } }
66
+ }
57
67
  }
58
68
  }
59
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drakulavich/oura-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Oura Ring CLI — query and analyze Oura Ring health data from the command line, designed for humans and AI agents.",
5
5
  "keywords": [
6
6
  "oura",