@purveyors/cli 0.14.0 → 0.19.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.
Files changed (53) hide show
  1. package/README.md +444 -217
  2. package/dist/commands/auth.d.ts +17 -0
  3. package/dist/commands/auth.d.ts.map +1 -1
  4. package/dist/commands/auth.js +160 -62
  5. package/dist/commands/auth.js.map +1 -1
  6. package/dist/commands/catalog.d.ts +3 -1
  7. package/dist/commands/catalog.d.ts.map +1 -1
  8. package/dist/commands/catalog.js +239 -42
  9. package/dist/commands/catalog.js.map +1 -1
  10. package/dist/commands/context.d.ts.map +1 -1
  11. package/dist/commands/context.js +5 -3
  12. package/dist/commands/context.js.map +1 -1
  13. package/dist/commands/manifest.d.ts.map +1 -1
  14. package/dist/commands/manifest.js +5 -1
  15. package/dist/commands/manifest.js.map +1 -1
  16. package/dist/commands/roast.d.ts.map +1 -1
  17. package/dist/commands/roast.js +115 -10
  18. package/dist/commands/roast.js.map +1 -1
  19. package/dist/commands/sales.d.ts.map +1 -1
  20. package/dist/commands/sales.js +87 -40
  21. package/dist/commands/sales.js.map +1 -1
  22. package/dist/lib/catalog.d.ts +332 -1
  23. package/dist/lib/catalog.d.ts.map +1 -1
  24. package/dist/lib/catalog.js +654 -2
  25. package/dist/lib/catalog.js.map +1 -1
  26. package/dist/lib/interactive/forms.d.ts +10 -0
  27. package/dist/lib/interactive/forms.d.ts.map +1 -1
  28. package/dist/lib/interactive/forms.js +10 -8
  29. package/dist/lib/interactive/forms.js.map +1 -1
  30. package/dist/lib/interactive/watch.d.ts +38 -4
  31. package/dist/lib/interactive/watch.d.ts.map +1 -1
  32. package/dist/lib/interactive/watch.js +294 -86
  33. package/dist/lib/interactive/watch.js.map +1 -1
  34. package/dist/lib/manifest.d.ts +7 -0
  35. package/dist/lib/manifest.d.ts.map +1 -1
  36. package/dist/lib/manifest.js +231 -26
  37. package/dist/lib/manifest.js.map +1 -1
  38. package/dist/lib/path-input.d.ts +3 -0
  39. package/dist/lib/path-input.d.ts.map +1 -0
  40. package/dist/lib/path-input.js +13 -0
  41. package/dist/lib/path-input.js.map +1 -0
  42. package/dist/lib/roast.d.ts +2 -0
  43. package/dist/lib/roast.d.ts.map +1 -1
  44. package/dist/lib/roast.js +5 -2
  45. package/dist/lib/roast.js.map +1 -1
  46. package/dist/lib/sales.d.ts +15 -2
  47. package/dist/lib/sales.d.ts.map +1 -1
  48. package/dist/lib/sales.js +77 -15
  49. package/dist/lib/sales.js.map +1 -1
  50. package/dist/program.d.ts.map +1 -1
  51. package/dist/program.js +17 -9
  52. package/dist/program.js.map +1 -1
  53. package/package.json +6 -1
package/README.md CHANGED
@@ -2,9 +2,22 @@
2
2
 
3
3
  Coffee intelligence from your terminal.
4
4
 
5
- `purvey` is the official CLI for [purveyors.io](https://purveyors.io). It gives coffee professionals and AI agents direct access to the Purveyors platform from the terminal: catalog search, inventory tracking, roast logging, sales records, tasting notes, and Artisan `.alog` import.
5
+ `purvey` is the official CLI for [purveyors.io](https://purveyors.io). It gives coffee professionals, developers, and AI agents direct access to the Purveyors platform from the terminal: catalog search, inventory tracking, roast logging, sales records, tasting notes, and Artisan `.alog` import.
6
6
 
7
- Run `purvey manifest` for the machine-readable contract, `purvey context` for the dense human-readable reference, or import `@purveyors/cli/manifest` in-process.
7
+ Use `purvey --help` for quick command discovery, `purvey context` for the dense human-readable operator reference, `purvey manifest` for the preferred machine-readable contract, or `@purveyors/cli/manifest` in-process.
8
+
9
+ ## At a glance
10
+
11
+ - Official binary: `purvey`
12
+ - Package: `@purveyors/cli`
13
+ - Runtime: Node.js 20+
14
+ - No pre-existing session required: `auth`, `config`, `context`, `manifest`
15
+ - Viewer role required: `catalog` (excluding structured process filters on `catalog search`)
16
+ - Member role required: `inventory`, `roast`, `sales`, `tasting`
17
+ - Preferred machine-readable contract: `purvey manifest`
18
+ - Dense human-readable reference: `purvey context`
19
+ - Compatibility JSON alias: `purvey context --json`
20
+ - In-process machine contract: `@purveyors/cli/manifest`
8
21
 
9
22
  ## Installation
10
23
 
@@ -22,6 +35,30 @@ Verify the install:
22
35
  purvey --version
23
36
  ```
24
37
 
38
+ ## Documentation map
39
+
40
+ | Surface | Use it for |
41
+ | ------------------------------------------------ | --------------------------------------------------------- |
42
+ | <https://purveyors.io/docs/cli/overview> | Live CLI docs |
43
+ | <https://purveyors.io/docs/api/overview> | Live API docs |
44
+ | [AGENTS.md](./AGENTS.md) | Canonical contributor and agent guidance |
45
+ | [docs/CLI_STRATEGY.md](./docs/CLI_STRATEGY.md) | Historical architecture and shipped-surface retrospective |
46
+ | <https://github.com/reedwhetstone/purveyors-cli> | Repository, issues, and source |
47
+ | <https://www.npmjs.com/package/@purveyors/cli> | Package installation and release metadata |
48
+
49
+ Use the live docs on purveyors.io as the primary external reference. Use this README and `AGENTS.md` for repo-specific contributor detail.
50
+
51
+ ## Source-of-truth hierarchy
52
+
53
+ Use this hierarchy when references disagree:
54
+
55
+ 1. `src/program.ts`, `src/commands/*`, and `src/lib/manifest.ts` define the shipped command surface, help text, auth requirements, output modes, ID guidance, and manifest payload.
56
+ 2. `package.json` defines the package version, Node engine, binary entrypoint, scripts, and exported subpaths.
57
+ 3. `README.md`, `AGENTS.md`, and `docs/CLI_STRATEGY.md` explain the repo-specific contract for users, contributors, and agents.
58
+ 4. `https://purveyors.io/docs/cli/overview` and `https://purveyors.io/docs/api/overview` are the primary live product docs for external readers.
59
+
60
+ The CLI is an agent-first product surface. Treat the binary, exported functions, `purvey manifest`, `purvey context`, stdout/stderr behavior, and role-gated command boundaries as one contract.
61
+
25
62
  ## Quick Start
26
63
 
27
64
  ```bash
@@ -31,30 +68,74 @@ purvey auth login
31
68
  # For agents, CI, or remote machines, use headless flow:
32
69
  # purvey auth login --headless
33
70
 
71
+ # If automatic browser callback handling fails or you paste a bad URL, paste the full callback URL back into the terminal. The CLI keeps waiting until a valid callback is received.
72
+
34
73
  # 2. Confirm the session and role
35
74
  purvey auth status
36
75
 
37
- # 3. Search the catalog (requires viewer role, granted on sign-in)
76
+ # 3. Search the catalog (viewer role for basic filters, member role for structured process filters)
38
77
  purvey catalog search --origin "Ethiopia" --stocked --pretty
39
78
 
79
+ # Example with structured process filters and proof output
80
+ purvey catalog search --origin "Ethiopia" --processing-base-method "Washed" --include-proof --pretty
81
+
40
82
  # 4. Check inventory (requires member role)
41
83
  purvey inventory list --stocked --pretty
42
84
 
43
85
  # 5. Import a roast from Artisan
44
86
  purvey roast import ~/artisan/my-roast.alog --coffee-id 7 --pretty
45
87
 
46
- # 6. (Agents/scripts) Get the machine-readable CLI contract in one command
88
+ # 6. Get the machine-readable CLI contract
47
89
  purvey manifest
48
90
 
49
- # 7. (Humans/agents) Get the dense readable CLI reference
91
+ # 7. Get the dense readable CLI reference
50
92
  purvey context
51
93
  ```
52
94
 
53
- ## Authentication
95
+ ## Reference surfaces
96
+
97
+ Use the right reference surface for the job:
98
+
99
+ - `purvey manifest` is the preferred stable machine-readable contract for agents, scripts, generated tooling, and parity checks.
100
+ - `purvey context` is the dense human-readable operator reference for reviewers and interactive use.
101
+ - `purvey context --json` and `purvey context --pretty` emit the same JSON payload as `purvey manifest`, but exist mainly for compatibility with tooling that already shells out to `context`.
102
+ - `@purveyors/cli/manifest` exposes the same contract in-process for Node.js and agent runtimes.
103
+
104
+ ## Package exports and shared product surface
105
+
106
+ The npm package is both a binary and a shared TypeScript product surface. `coffee-app` and agent runtimes import CLI business functions directly, so exported subpaths are part of the supported machine contract.
107
+
108
+ | Import path | Use it for |
109
+ | -------------------------- | ------------------------------------------------------------------------------------ |
110
+ | `@purveyors/cli` | CLI entrypoint package root |
111
+ | `@purveyors/cli/catalog` | Catalog search, lookup, stats, premium ranking, supplier aggregates, similar coffees |
112
+ | `@purveyors/cli/inventory` | Green coffee inventory operations |
113
+ | `@purveyors/cli/roast` | Roast profile operations |
114
+ | `@purveyors/cli/sales` | Sales record operations |
115
+ | `@purveyors/cli/tasting` | Tasting note and rating operations |
116
+ | `@purveyors/cli/lib` | Shared library helpers |
117
+ | `@purveyors/cli/manifest` | Stable machine-readable CLI manifest |
118
+ | `@purveyors/cli/artisan` | Artisan `.alog` parsing and import utilities |
119
+ | `@purveyors/cli/ai` | AI helper surface used by CLI workflows |
120
+
121
+ Shell integrations should usually start with `purvey manifest`. In-process agent and website integrations should import the smallest relevant subpath instead of shelling out when they are already running in Node.js.
122
+
123
+ Export discipline:
124
+
125
+ - Add or remove subpaths only when the package contract intentionally changes.
126
+ - Keep `package.json`, `README.md`, `AGENTS.md`, `docs/CLI_STRATEGY.md`, `src/lib/manifest.ts`, and dist parity checks aligned in the same PR.
127
+ - Prefer the narrowest import path for application and agent code. For example, use `@purveyors/cli/catalog` for catalog operations instead of importing the package root.
128
+ - Treat export-shape changes as product changes because coffee-app and agent runtimes import these functions directly.
129
+
130
+ ## Authentication and access model
54
131
 
55
132
  No pre-existing session is required for `auth`, `config`, `context`, or `manifest`.
56
133
 
57
- **All remote data commands require a valid authenticated session.** `catalog`, `inventory`, `roast`, `sales`, and `tasting` all require auth.
134
+ All remote data commands require a valid authenticated session:
135
+
136
+ - `catalog` requires the `viewer` role by default
137
+ - `catalog search` structured process filters require the `member` role under the current session-authenticated CLI path
138
+ - `inventory`, `roast`, `sales`, and `tasting` require the `member` role
58
139
 
59
140
  `purvey` uses Google OAuth through purveyors.io.
60
141
 
@@ -73,6 +154,8 @@ purvey auth login --headless
73
154
  # Paste the full callback URL back into the terminal
74
155
  ```
75
156
 
157
+ If the browser cannot return to the CLI during interactive login, paste the full callback URL back into the terminal. Invalid pasted URLs are ignored, and the CLI keeps waiting until a valid callback is received. Use `purvey auth login --headless` when you need the CLI to print the OAuth URL for another browser.
158
+
76
159
  Status:
77
160
 
78
161
  ```bash
@@ -91,23 +174,20 @@ Credentials are stored at `~/.config/purvey/credentials.json`.
91
174
 
92
175
  ### Auth roles
93
176
 
94
- Two roles gate different command groups:
95
-
96
- | Role | Commands |
97
- | -------- | ------------------------------------------------------------------- |
98
- | `viewer` | `catalog search`, `catalog get`, `catalog stats`, `catalog similar` |
99
- | `member` | All viewer commands, plus `inventory`, `roast`, `sales`, `tasting` |
177
+ | Role | Access |
178
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
179
+ | `viewer` | `catalog search`, `catalog get`, `catalog stats`, excluding structured process filters |
180
+ | `member` | All viewer commands, `catalog similar`, structured process filters on `catalog search`, plus `inventory`, `roast`, `sales`, `tasting` under the session-authenticated CLI path |
100
181
 
101
- Both roles are granted when you sign in through purveyors.io. The `viewer` role is the minimum required for catalog access; the `member` role is required for any personal data or write operations.
182
+ `auth`, `config`, `context`, and `manifest` remain available without a pre-existing session.
102
183
 
103
- `auth`, `config`, `context`, and `manifest` work without a pre-existing session. `config list/get/set/reset` are local-only commands, `context` is a local reference command, and `manifest` emits the machine-readable CLI contract.
184
+ Commands that require a higher role exit with code `3` on auth failure. That includes not being signed in, an expired session, or an insufficient role.
104
185
 
105
- Commands that require a higher role will exit with code `3` (auth error) if you are not signed in or your role is insufficient. Run `purvey auth status` to confirm your current role before scripting.
186
+ ## Output contract and scripting
106
187
 
107
- ## Output and Scripting
188
+ Most commands write compact JSON to stdout by default. Use `--json` if you want to request that mode explicitly.
108
189
 
109
- Most commands write compact JSON to stdout by default.
110
- Use `--json` if you want to request that mode explicitly.
190
+ Machine-contract rule of thumb: stdout is for successful payloads, stderr is for status or errors, and exit codes communicate the failure class. That rule is more important than making terminal output look conversational.
111
191
 
112
192
  Pretty JSON:
113
193
 
@@ -115,10 +195,11 @@ Pretty JSON:
115
195
  purvey inventory list --pretty
116
196
  ```
117
197
 
118
- CSV output for array results:
198
+ CSV output for array results on supporting commands:
119
199
 
120
200
  ```bash
121
201
  purvey inventory list --csv > inventory.csv
202
+ purvey roast list --csv > roasts.csv
122
203
  purvey sales list --csv > sales.csv
123
204
  ```
124
205
 
@@ -130,7 +211,7 @@ purvey roast list --limit 5 | jq '.[].roast_id'
130
211
  purvey auth status 2>/dev/null | jq -r '.email'
131
212
  ```
132
213
 
133
- Operational messages go to stderr, so stdout stays script-friendly.
214
+ Operational messages go to stderr so stdout stays script-friendly.
134
215
 
135
216
  Fatal errors also stay on stderr, but the payload format depends on mode:
136
217
 
@@ -138,7 +219,7 @@ Fatal errors also stay on stderr, but the payload format depends on mode:
138
219
  - `--json`, `--pretty`, or `--csv`: JSON error envelope on stderr
139
220
  - piped or redirected with no explicit flag: compact JSON error envelope on stderr
140
221
 
141
- That same contract applies to parser-level mistakes too, including unknown options, unknown commands, and missing required arguments.
222
+ That contract also applies to parser-level mistakes such as unknown options, unknown commands, and missing required arguments.
142
223
 
143
224
  The JSON error envelope includes:
144
225
 
@@ -148,24 +229,25 @@ The JSON error envelope includes:
148
229
 
149
230
  ### Output caveats worth knowing
150
231
 
151
- - `purvey auth status` prints human-readable output in an interactive terminal unless you pass `--json`, `--pretty`, or `--csv`. When piped or redirected, it emits JSON on stdout even when unauthenticated.
232
+ - `purvey auth status` prints human-readable output in an interactive terminal unless you pass `--json`, `--pretty`, or `--csv`. When piped or redirected, it emits structured JSON on stdout even when unauthenticated.
152
233
  - `purvey config list/get/set/reset` stay human-readable in an interactive terminal, but emit JSON on stdout when you pass `--json` or `--pretty`, or when stdout is non-interactive. `--csv` is not supported for config commands.
153
- - `--json` is an explicit alias for the default compact JSON mode, and it forces JSON even in an interactive terminal.
234
+ - `purvey context` defaults to dense human-readable reference text. `--json` and `--pretty` make it emit the same JSON manifest as `purvey manifest`.
235
+ - `purvey manifest` always emits the machine-readable contract on stdout. `--pretty` only changes formatting.
154
236
  - `--csv` affects successful stdout output only; fatal errors still use JSON on stderr.
155
237
 
156
- ## Exit Codes
238
+ ## Exit codes
157
239
 
158
240
  All `purvey` commands exit with a numeric code your scripts can check with `$?`.
159
241
 
160
- | Code | Meaning |
161
- | ---- | ----------------------------------------------------------------------------------------- |
162
- | `0` | Success |
163
- | `1` | Unexpected or unclassified error |
164
- | `2` | Invalid argument or bad input |
165
- | `3` | Auth error: not logged in, expired session, or insufficient role |
166
- | `4` | Not found |
167
- | `5` | Dependency conflict (for example, `inventory delete` without `--force` when roasts exist) |
168
- | `6` | Local config error |
242
+ | Code | Meaning |
243
+ | ---- | ---------------------------------------------------------------- |
244
+ | `0` | Success |
245
+ | `1` | Unexpected or unclassified error |
246
+ | `2` | Invalid argument or bad input |
247
+ | `3` | Auth error: not logged in, expired session, or insufficient role |
248
+ | `4` | Not found |
249
+ | `5` | Dependency conflict |
250
+ | `6` | Local config error |
169
251
 
170
252
  Scripting pattern:
171
253
 
@@ -176,105 +258,191 @@ if [ $? -eq 3 ]; then
176
258
  fi
177
259
  ```
178
260
 
179
- ## Command Reference
261
+ ## Command reference
180
262
 
181
263
  ### auth
182
264
 
183
265
  - `purvey auth login`
184
266
  - `purvey auth login --headless`
185
267
  - `purvey auth status`
268
+ - `purvey auth status --json`
269
+ - `purvey auth status --pretty`
270
+ - `purvey auth status --csv`
186
271
  - `purvey auth logout`
187
272
 
273
+ Examples:
274
+
275
+ ```bash
276
+ purvey auth login
277
+ purvey auth login --headless
278
+ purvey auth status --pretty
279
+ purvey auth status --csv
280
+ purvey auth logout
281
+ ```
282
+
283
+ Notes:
284
+
285
+ - `auth login` uses browser-based Google OAuth, listens for the localhost callback, and also accepts a pasted callback URL if the browser cannot return to the CLI. Invalid pasted callback URLs are ignored so you can paste again while the CLI keeps waiting.
286
+ - `auth login --headless` prints an OAuth URL and accepts a pasted callback URL.
287
+ - `auth status --json` is the safest mode for scripts.
288
+ - `auth status --csv` is supported for spreadsheet-style checks, but JSON remains the better integration format.
289
+
188
290
  ### catalog
189
291
 
190
292
  - `purvey catalog search`
191
293
  - `purvey catalog get <id>`
192
294
  - `purvey catalog stats`
295
+ - `purvey catalog rank-premium`
296
+ - `purvey catalog supplier-list`
297
+ - `purvey catalog supplier-detail <supplier>`
298
+ - `purvey catalog supplier-rank`
193
299
  - `purvey catalog similar <id>`
194
300
 
195
301
  `catalog search` filters:
196
302
 
197
- - `--origin <text>` -- filter by origin (country, continent, or region)
198
- - `--process <method>` -- filter by processing method (e.g. natural, washed)
199
- - `--price-min <n>` -- minimum price per lb (USD)
200
- - `--price-max <n>` -- maximum price per lb (USD)
201
- - `--flavor <keywords>` -- flavor keywords, comma-separated
202
- - `--name <text>` -- filter by coffee name (partial match, case-insensitive)
203
- - `--supplier <name>` -- filter by supplier/source name (partial match, case-insensitive)
204
- - `--ids <n,n,...>` -- fetch specific catalog IDs (comma-separated, ignores limit)
205
- - `--stocked` -- only show currently stocked coffees
206
- - `--variety <text>` -- filter by coffee variety/cultivar (partial match)
207
- - `--drying-method <text>` -- filter by drying method (partial match)
208
- - `--stocked-days <n>` -- only show coffees stocked within N days
209
- - `--sort <price|price-desc|name|origin|newest>` -- sort results
210
- - `--offset <n>` -- skip N results for pagination
211
- - `--limit <n>` -- maximum results to return (default: 10)
303
+ - `--origin <text>`; origin, country, continent, or region
304
+ - `--process <method>`; processing method
305
+ - `--processing-base-method <method>`; canonical structured process base method
306
+ - `--fermentation-type <type>`; structured fermentation type
307
+ - `--process-additive <additive>`; disclosed process additive
308
+ - `--processing-disclosure-level <level>`; structured process disclosure level
309
+ - `--processing-confidence-min <n>`; minimum structured process confidence from `0` to `1`
310
+ - `--price-min <n>`; minimum USD/lb
311
+ - `--price-max <n>`; maximum USD/lb
312
+ - `--flavor <keywords>`; comma-separated flavor terms
313
+ - `--name <text>`; partial coffee name match
314
+ - `--supplier <name>`; partial supplier match
315
+ - `--ids <n,n,...>`; fetch specific catalog IDs, ignores limit and offset
316
+ - `--variety <text>`; partial cultivar match
317
+ - `--drying-method <text>`; partial drying method match
318
+ - `--stocked-days <n>`; stocked within N days
319
+ - `--stocked`; only currently stocked coffees
320
+ - `--sort <price|price-desc|name|origin|newest>`
321
+ - `--offset <n>`; pagination offset
322
+ - `--limit <n>`; default `10`
323
+ - `--include-proof`; request canonical proof summaries from `/v1/catalog?include=proof`
212
324
 
213
325
  `catalog similar <id>` options:
214
326
 
215
- - `--threshold <0-1>` -- minimum similarity score (default: 0.70)
216
- - `--limit <n>` -- max results (default: 10)
217
- - `--stocked-only` -- only show currently stocked beans
327
+ - `--threshold <score>`; canonical similarity threshold `0.5` to `0.99`, default `0.70`
328
+ - `--limit <count>`; default `10`, max `25`
329
+ - `--stocked-only`; request only currently stocked coffees
330
+ - `--mode <all|likely_same|similar_profile>`; default `all`
331
+
332
+ `catalog rank-premium` options:
333
+
334
+ - `--origin <origin>`; optional origin filter
335
+ - `--process <method>`; optional process filter
336
+ - `--supplier <name>`; optional supplier filter
337
+ - `--stocked`; only include currently stocked coffees
338
+ - `--price-max <n>`; maximum USD/lb
339
+ - `--min-score <n>`; minimum Purveyor Score
340
+ - `--include-unscored`; include unscored rows after scored rows
341
+ - `--sample-size <n>`; rows to sample before ranking, default `250`, max `5000`
342
+ - `--limit <n>`; default `10`, max `50`
343
+
344
+ `catalog supplier-*` options:
345
+
346
+ - `supplier-list`: `--stocked`, `--sample-size <n>` (catalog rows per fetch page, default `5000`, max `5000`), `--limit <n>`
347
+ - `supplier-detail <supplier>`: `--stocked`, `--top-coffees <n>`, `--sample-size <n>` (catalog rows per fetch page, default `5000`, max `5000`)
348
+ - `supplier-rank`: `--stocked`, `--min-coffees <n>`, `--sample-size <n>` (catalog rows per fetch page, default `5000`, max `5000`), `--limit <n>`
218
349
 
219
350
  Examples:
220
351
 
221
352
  ```bash
222
353
  purvey catalog search --origin "Ethiopia" --pretty
354
+ purvey catalog search --processing-base-method "Natural" --fermentation-type "Anaerobic" --pretty
355
+ purvey catalog search --process-additive "hops" --processing-confidence-min 0.8 --pretty
223
356
  purvey catalog search --supplier "Royal Coffee" --stocked --pretty
224
357
  purvey catalog search --ids "1182,1183,1200"
225
358
  purvey catalog search --stocked --sort price --offset 10 --limit 10
359
+ purvey catalog search --origin "Ethiopia" --include-proof --json
360
+ PARCHMENT_API_KEY="$PURVEYORS_API_KEY" purvey catalog search --origin "Ethiopia" --include-proof --limit 5 --json
226
361
  purvey catalog similar 1182 --threshold 0.85 --stocked-only --pretty
227
- purvey catalog similar 1182 --json | jq '.[0]'
362
+ purvey catalog similar 1182 --json | jq '.data.groups.canonical_candidates'
363
+ purvey catalog rank-premium --stocked --limit 10 --pretty
364
+ purvey catalog supplier-rank --stocked --min-coffees 3 --json
365
+ purvey catalog supplier-detail "Royal Coffee" --pretty
366
+ purvey catalog stats --pretty
367
+ purvey catalog get 1182 --pretty
368
+ purvey catalog get 1182 --include-proof --json
228
369
  ```
229
370
 
371
+ Notes:
372
+
373
+ - Catalog commands require an authenticated `viewer` role by default.
374
+ - Structured process filters on `catalog search` require an authenticated `member` role under the current session-authenticated CLI path.
375
+ - Structured process filters use the canonical `/v1/catalog` query contract names while preserving the legacy `--process` label filter.
376
+ - `--include-proof` is an opt-in API-backed catalog read. It consumes the canonical proof summary returned by `/v1/catalog?include=proof`; the CLI does not compute proof fields locally or duplicate web/API proof logic.
377
+ - `--include-proof` rejects CLI-only filters that `/v1/catalog` cannot yet preserve exactly, such as `--flavor`, `--supplier`, `--drying-method`, and `--sort newest`.
378
+ - If you want proof output against an API-key deployment, set `PARCHMENT_API_KEY` or `PURVEYORS_API_KEY` before running the command. Otherwise the CLI uses your logged-in Purveyors session token.
379
+ - `catalog get` and `catalog similar` both take `coffee_catalog.catalog_id`.
380
+ - `catalog rank-premium` reads `coffee_catalog.purveyor_score` and returns confidence, tier, factor breakdown, version, and update metadata in the `purveyor_score` object; the CLI does not recompute the upstream Purveyor Score model.
381
+ - Catalog intelligence responses include `meta.sample_limited`, `meta.sample_order`, and `meta.truncated` where relevant so agents can distinguish ranked samples from full supplier aggregates. Supplier aggregate responses also include `meta.rows_examined`.
382
+ - Supplier aggregate commands summarize catalog row counts, stocked counts, Purveyor Score coverage, average score, average confidence, price range, origin/process coverage, and representative top coffees with score qualifiers.
383
+ - `catalog similar` uses the beta canonical `/v1/catalog/{id}/similar` API contract, not the legacy direct RPC path.
384
+ - `catalog similar --json` requires member access or a paid API tier and returns the grouped canonical response object: `data.target`, `data.groups.canonical_candidates`, `data.groups.similar_recommendations`, optional `data.matches`, and `meta`.
385
+ - `canonical_candidates` are likely same-lot candidates; `similar_recommendations` are substitutes/profile matches and include blocker reasons when identity gates disagree.
386
+ - The command preserves `classification_version`, `query_strategy`, score dimensions, proof summaries, pricing metadata, and classification/blocker details supplied by the API.
387
+ - `catalog stats` returns aggregate catalog metrics, not your personal inventory metrics.
388
+
230
389
  ### inventory
231
390
 
232
391
  - `purvey inventory list`
233
392
  - `purvey inventory get <id>`
234
393
  - `purvey inventory add`
235
394
  - `purvey inventory update <id>`
236
- - `purvey inventory delete <id>` (use `--force` to cascade delete dependent roasts and sales)
395
+ - `purvey inventory delete <id>`
237
396
 
238
- `inventory list` options:
397
+ `inventory list` filters:
239
398
 
240
- - `--stocked` -- only show currently stocked beans
241
- - `--catalog-id <id>` -- filter by catalog ID
242
- - `--purchase-date-start <YYYY-MM-DD>` -- only show purchases on or after this date
243
- - `--purchase-date-end <YYYY-MM-DD>` -- only show purchases on or before this date
244
- - `--origin <country>` -- filter by country of origin (partial match)
245
- - `--limit <n>` -- maximum results (default: 20)
246
- - `--offset <n>` -- skip N results for pagination (default: 0)
399
+ - `--stocked`
400
+ - `--catalog-id <id>`
401
+ - `--purchase-date-start <YYYY-MM-DD>`
402
+ - `--purchase-date-end <YYYY-MM-DD>`
403
+ - `--origin <country>`
404
+ - `--limit <n>`; default `20`
405
+ - `--offset <n>`; default `0`
247
406
 
248
407
  `inventory add` flags:
249
408
 
250
- - `--catalog-id <id>` -- [REQUIRED] coffee_catalog.catalog_id
251
- - `--qty <lbs>` -- [REQUIRED] quantity in pounds
252
- - `--cost <dollars>` -- bean cost in dollars
253
- - `--tax-ship <dollars>` -- tax and shipping cost in dollars
254
- - `--notes <text>` -- notes for this inventory item
255
- - `--purchase-date <YYYY-MM-DD>` -- purchase date (defaults to today)
256
- - `--form` -- interactive form mode
409
+ - `--catalog-id <id>`; required in flag mode
410
+ - `--qty <lbs>`; required in flag mode
411
+ - `--cost <dollars>`
412
+ - `--tax-ship <dollars>`
413
+ - `--notes <text>`
414
+ - `--purchase-date <YYYY-MM-DD>`
415
+ - `--form`
257
416
 
258
417
  `inventory update <id>` flags:
259
418
 
260
- - `--qty <lbs>` -- updated quantity in pounds
261
- - `--cost <dollars>` -- updated bean cost
262
- - `--tax-ship <dollars>` -- updated tax/shipping cost
263
- - `--notes <text>` -- updated notes
264
- - `--stocked <true|false>` -- mark as stocked or not
419
+ - `--qty <lbs>`
420
+ - `--cost <dollars>`
421
+ - `--tax-ship <dollars>`
422
+ - `--notes <text>`
423
+ - `--stocked <true|false>`
424
+
425
+ `inventory delete <id>` options:
426
+
427
+ - `--force`; cascade dependent roasts and sales
428
+ - `--yes`; skip confirmation prompt
265
429
 
266
430
  Examples:
267
431
 
268
432
  ```bash
269
433
  purvey inventory list --stocked --pretty
270
- purvey inventory list --limit 20 --offset 20 # page 2
271
- purvey inventory list --csv > inventory.csv
272
434
  purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
273
- purvey inventory add --catalog-id 42 --qty 5 --cost 6.25 --tax-ship 4.00
435
+ purvey inventory add --form
274
436
  purvey inventory update 7 --stocked false
275
437
  purvey inventory delete 7 --yes
276
438
  ```
277
439
 
440
+ Notes:
441
+
442
+ - Inventory commands require an authenticated `member` role.
443
+ - Inventory `id` is `green_coffee_inv.id`, not `catalog_id`.
444
+ - `inventory delete` may require `--force` if dependent roasts or sales exist.
445
+
278
446
  ### roast
279
447
 
280
448
  - `purvey roast list`
@@ -287,69 +455,79 @@ purvey inventory delete 7 --yes
287
455
 
288
456
  `roast list` filters:
289
457
 
290
- - `--coffee-id <id>` -- filter by inventory item ID (green_coffee_inv.id)
291
- - `--roast-id <id>` -- filter by exact roast profile ID
292
- - `--batch-name <text>` -- filter by batch name (partial match, case-insensitive)
293
- - `--coffee-name <text>` -- filter by bean name (partial match, case-insensitive)
294
- - `--date-start <YYYY-MM-DD>` -- only show roasts on or after this date
295
- - `--date-end <YYYY-MM-DD>` -- only show roasts on or before this date
296
- - `--stocked` -- only show roasts for currently stocked beans
297
- - `--catalog-id <id>` -- filter by coffee_catalog ID
298
- - `--limit <n>` -- maximum results (default: 20)
299
- - `--offset <n>` -- skip N results for pagination (default: 0)
458
+ - `--coffee-id <id>`; inventory item ID
459
+ - `--roast-id <id>`; exact roast profile ID
460
+ - `--batch-name <text>`
461
+ - `--coffee-name <text>`
462
+ - `--date-start <YYYY-MM-DD>`
463
+ - `--date-end <YYYY-MM-DD>`
464
+ - `--stocked`
465
+ - `--catalog-id <id>`
466
+ - `--limit <n>`; default `20`
467
+ - `--offset <n>`; default `0`
300
468
 
301
469
  `roast get <id>` options:
302
470
 
303
- - `--include-temps` -- include temperature curve data
304
- - `--include-events` -- include roast event markers
471
+ - `--include-temps`
472
+ - `--include-events`
305
473
 
306
474
  `roast create` flags:
307
475
 
308
- - `--coffee-id <id>` -- [REQUIRED] green_coffee_inv ID
309
- - `--batch-name <name>` -- batch name (defaults to coffee name + today's date)
310
- - `--oz-in <oz>` -- green weight in ounces
311
- - `--oz-out <oz>` -- roasted weight in ounces
312
- - `--roast-date <YYYY-MM-DD>` -- roast date (defaults to today)
313
- - `--notes <text>` -- roast notes
314
- - `--form` -- interactive form mode
476
+ - `--coffee-id <id>`; required in flag mode
477
+ - `--batch-name <name>`
478
+ - `--oz-in <oz>`
479
+ - `--oz-out <oz>`
480
+ - `--roast-date <YYYY-MM-DD>`
481
+ - `--notes <text>`
482
+ - `--form`
315
483
 
316
- `roast update <id>` fields:
484
+ `roast update <id>` flags:
317
485
 
318
- - `--notes <text>` -- updated roast notes
319
- - `--oz-out <oz>` -- updated roasted weight (triggers weight loss recalculation)
320
- - `--batch-name <name>` -- updated batch name
321
- - `--targets <text>` -- updated roast targets
486
+ - `--batch-name <name>`
487
+ - `--oz-out <oz>`
488
+ - `--notes <text>`
489
+ - `--targets <text>`
322
490
 
323
491
  `roast import [file]` flags:
324
492
 
325
- - `--coffee-id <id>` -- [REQUIRED] green_coffee_inv ID
326
- - `--batch-name <name>` -- batch name (auto-generated if omitted)
327
- - `--oz-in <oz>` -- green weight (extracted from .alog if present, overridden here)
328
- - `--roast-notes <text>` -- additional roast notes
329
- - `--form` -- interactive form mode
493
+ - `--coffee-id <id>`; required unless `--form`
494
+ - `--batch-name <name>`
495
+ - `--oz-in <oz>`
496
+ - `--roast-notes <text>`
497
+ - `--roast-targets <text>`
498
+ - `--form`
330
499
 
331
500
  `roast watch [directory]` options:
332
501
 
333
- - `--coffee-id <id>` -- [REQUIRED unless --auto-match] inventory ID for all imports
334
- - `--batch-prefix <name>` -- batch name prefix for auto-named batches
335
- - `--prompt-each` -- prompt for bean selection on each new file
336
- - `--auto-match` -- auto-match beans per file (mutually exclusive with --coffee-id)
337
- - `--resume` -- resume a previous watch session
338
- - `--form` -- interactive setup wizard
502
+ - `--coffee-id <id>`; required unless `--auto-match`
503
+ - `--batch-prefix <name>`
504
+ - `--prompt-each`
505
+ - `--auto-match`
506
+ - `--commit-mode <batch|individual>`
507
+ - `--oz-in <oz>`
508
+ - `--roast-notes <text>`
509
+ - `--roast-targets <text>`
510
+ - `--resume`
511
+ - `--form`
339
512
 
340
513
  Examples:
341
514
 
342
515
  ```bash
343
516
  purvey roast list --catalog-id 128 --pretty
344
- purvey roast list --batch-name "Ethiopia Guji" --pretty
345
- purvey roast list --date-start 2026-03-01 --date-end 2026-03-31
346
- purvey roast list --limit 20 --offset 20 # page 2
517
+ purvey roast get 123 --include-temps --pretty
347
518
  purvey roast create --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz-in 16
348
- purvey roast update 123 --targets "Aim for FC at 390F, 18% dev"
349
- purvey roast import ~/artisan/ethiopia.alog --coffee-id 7
519
+ purvey roast import ~/artisan/ethiopia.alog --coffee-id 7 --roast-targets "Aim for 18% development"
350
520
  purvey roast watch ~/artisan/ --auto-match
351
521
  ```
352
522
 
523
+ Notes:
524
+
525
+ - Roast commands require an authenticated `member` role.
526
+ - `--coffee-id` uses inventory IDs.
527
+ - `roast import` and `roast watch` normalize pasted paths by trimming whitespace, removing one layer of matching quotes, and accepting common shell-escaped characters.
528
+ - `roast watch --auto-match` is mutually exclusive with `--coffee-id`.
529
+ - `roast watch --commit-mode` defaults to `batch`.
530
+
353
531
  ### sales
354
532
 
355
533
  - `purvey sales list`
@@ -359,123 +537,151 @@ purvey roast watch ~/artisan/ --auto-match
359
537
 
360
538
  `sales list` filters:
361
539
 
362
- - `--roast-id <id>` -- filter by roast profile ID
363
- - `--date-start <YYYY-MM-DD>` -- only show sales on or after this date
364
- - `--date-end <YYYY-MM-DD>` -- only show sales on or before this date
365
- - `--buyer <name>` -- filter by buyer name (partial match, case-insensitive)
366
- - `--limit <n>` -- maximum results (default: 20)
367
- - `--offset <n>` -- skip N results for pagination (default: 0)
540
+ - `--roast-id <id>`
541
+ - `--date-start <YYYY-MM-DD>`
542
+ - `--date-end <YYYY-MM-DD>`
543
+ - `--buyer <name>`
544
+ - `--limit <n>`; default `20`
545
+ - `--offset <n>`; default `0`
368
546
 
369
547
  `sales record` flags:
370
548
 
371
- - `--roast-id <id>` -- [REQUIRED] roast_data.roast_id
372
- - `--oz <amount>` -- [REQUIRED] ounces sold
373
- - `--price <dollars>` -- [REQUIRED] total sale price in dollars
374
- - `--buyer <name>` -- buyer name or identifier
375
- - `--sell-date <YYYY-MM-DD>` -- sale date (defaults to today)
376
- - `--form` -- interactive form mode
549
+ - `--roast-id <id>`; exact selector mode
550
+ - `--coffee-id <id>`; resolved selector mode, requires `--batch-name`
551
+ - `--batch-name <name>`; resolved selector mode, requires `--coffee-id`
552
+ - `--oz <amount>`; required in flag mode
553
+ - `--price <dollars>`; required in flag mode
554
+ - `--buyer <name>`
555
+ - `--sell-date <YYYY-MM-DD>`
556
+ - `--form`
377
557
 
378
558
  `sales update <id>` flags:
379
559
 
380
- - `--oz <amount>` -- updated ounces sold
381
- - `--price <dollars>` -- updated sale price
382
- - `--buyer <name>` -- updated buyer name
383
- - `--sell-date <YYYY-MM-DD>` -- updated sale date
560
+ - `--oz <amount>`
561
+ - `--price <dollars>`
562
+ - `--buyer <name>`
563
+ - `--sell-date <YYYY-MM-DD>`
564
+
565
+ `sales delete <id>` options:
566
+
567
+ - `--yes`
384
568
 
385
569
  Examples:
386
570
 
387
571
  ```bash
388
572
  purvey sales record --roast-id 123 --oz 12 --price 22.00 --buyer "Jane Smith"
573
+ purvey sales record --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz 8 --price 16.00
389
574
  purvey sales list --pretty
390
- purvey sales list --limit 20 --offset 20 # page 2
391
575
  purvey sales update 5 --price 24.00
392
576
  purvey sales delete 5 --yes
393
577
  ```
394
578
 
579
+ Notes:
580
+
581
+ - Sales commands require an authenticated `member` role.
582
+ - Use exactly one selector mode for `sales record`: exact `--roast-id`, or resolved `--coffee-id` plus `--batch-name`.
583
+ - If resolved mode matches multiple roasts, rerun with `--roast-id`.
584
+ - `--price` is total sale price, not per-ounce price.
585
+
395
586
  ### tasting
396
587
 
397
- - `purvey tasting get <catalog-id>` -- retrieve tasting notes (uses `coffee_catalog.catalog_id`)
398
- - `purvey tasting rate [inventory-id]` -- record cupping scores (uses `green_coffee_inv.id`)
588
+ - `purvey tasting get <bean-id>`
589
+ - `purvey tasting rate [bean-id]`
590
+
591
+ ID distinction:
399
592
 
400
- **ID distinction:** `tasting get` takes a `catalog_id`; `tasting rate` takes an `inventory id`. These are different numbers. Use `purvey catalog search` to find a `catalog_id` and `purvey inventory list` to find your `inventory id`.
593
+ - `tasting get <bean-id>` takes a `catalog_id`
594
+ - `tasting rate [bean-id]` takes an inventory ID
595
+ - The CLI token is `bean-id` for both tasting commands, but the backing ID type is different
401
596
 
402
- `purvey tasting get <catalog-id>` options:
597
+ `purvey tasting get <bean-id>` options:
403
598
 
404
- - `--filter <user|supplier|both>` -- which notes to show (default: both)
599
+ - `--filter <user|supplier|both>`; default `both`
405
600
 
406
- `purvey tasting rate [inventory-id]` options:
601
+ `purvey tasting rate [bean-id]` options:
407
602
 
408
- - `--aroma <1-5>` -- [REQUIRED in flag mode]
409
- - `--body <1-5>` -- [REQUIRED in flag mode]
410
- - `--acidity <1-5>` -- [REQUIRED in flag mode]
411
- - `--sweetness <1-5>` -- [REQUIRED in flag mode]
412
- - `--aftertaste <1-5>` -- [REQUIRED in flag mode]
413
- - `--brew-method <method>` -- brew method used (e.g. pour_over, espresso)
414
- - `--notes <text>` -- additional tasting notes
415
- - `--form` -- interactive form mode
603
+ - `--aroma <1-5>`; required in flag mode
604
+ - `--body <1-5>`; required in flag mode
605
+ - `--acidity <1-5>`; required in flag mode
606
+ - `--sweetness <1-5>`; required in flag mode
607
+ - `--aftertaste <1-5>`; required in flag mode
608
+ - `--brew-method <method>`
609
+ - `--notes <text>`
610
+ - `--form`
416
611
 
417
612
  Examples:
418
613
 
419
614
  ```bash
420
- # catalog_id 128 = the bean in the catalog
421
615
  purvey tasting get 128 --filter both --pretty
422
- # inventory id 7 = your physical purchase from inventory
423
616
  purvey tasting rate 7 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
424
- purvey tasting rate 42 --aroma 3 --body 3 --acidity 3 --sweetness 3 --aftertaste 3 --notes "Underextracted"
617
+ purvey tasting rate --form
425
618
  ```
426
619
 
427
- ### config
620
+ Notes:
428
621
 
429
- - `purvey config list`
430
- - `purvey config get <key>`
431
- - `purvey config set <key> <value>`
432
- - `purvey config reset`
622
+ - Tasting commands require an authenticated `member` role.
623
+ - `tasting get` combines supplier notes with your own notes when available.
624
+ - `tasting rate` writes scores back to your inventory row.
433
625
 
434
- Current config key:
626
+ ### config
435
627
 
436
- - `form-mode`: when set to `true`, write commands enter interactive mode when required args are missing
628
+ - `purvey config list` (supports `--json`, `--pretty`)
629
+ - `purvey config get <key>` (supports `--json`, `--pretty`)
630
+ - `purvey config set <key> <value>` (supports `--json`, `--pretty`)
631
+ - `purvey config reset` (supports `--json`, `--pretty`)
437
632
 
438
- Output behavior:
633
+ Current config key:
439
634
 
440
- - Interactive terminals keep config commands human-readable by default
441
- - `--json` or `--pretty`, or any non-interactive stdout, emits JSON on stdout
442
- - `--csv` is not supported for config commands
635
+ - `form-mode`: when `true`, write commands enter interactive mode when required args are missing
443
636
 
444
637
  Examples:
445
638
 
446
639
  ```bash
447
640
  purvey config set form-mode true
448
- purvey config get form-mode
449
641
  purvey config get form-mode --json
450
642
  purvey config reset --json
451
643
  ```
452
644
 
645
+ Notes:
646
+
647
+ - Config commands are local-only.
648
+ - Config file path: `~/.config/purvey/config.json`
649
+ - `--csv` is not supported.
650
+
453
651
  ### context
454
652
 
455
653
  - `purvey context`
456
654
  - `purvey context --json`
457
655
  - `purvey context --pretty`
458
656
 
459
- `purvey context` defaults to dense, human-readable, source-aware CLI reference text.
657
+ Notes:
460
658
 
461
- Use `--json` or `--pretty` when you want the same machine-readable manifest that `purvey manifest` emits.
659
+ - Default output is the dense human-readable operator reference text.
660
+ - `--json` and `--pretty` emit the same machine-readable manifest as `purvey manifest`.
661
+ - Prefer `purvey manifest` for new machine integrations. Use `purvey context --json` when you need compatibility with an existing `context`-based workflow.
662
+ - Use `@purveyors/cli/manifest` when you need that same contract in-process from Node.js or an agent runtime.
663
+ - `--csv` is not supported.
462
664
 
463
665
  ### manifest
464
666
 
465
667
  - `purvey manifest`
668
+ - `purvey manifest --json`
466
669
  - `purvey manifest --pretty`
467
670
 
468
- Use this when an agent or script needs the machine-readable contract directly. `purvey manifest` emits compact JSON by default.
469
-
470
- `purvey manifest` and `purvey context --json` emit the same JSON manifest.
671
+ Notes:
471
672
 
472
- If you want indented JSON, add `--pretty` to either command.
673
+ - `purvey manifest` emits the preferred stable machine-readable CLI contract on stdout.
674
+ - `purvey manifest` and `purvey manifest --json` both emit compact JSON.
675
+ - `purvey manifest --pretty` emits indented JSON.
676
+ - `purvey manifest` and `purvey context --json` emit the same JSON payload.
677
+ - Use `purvey manifest` for new automation and treat `purvey context --json` as a compatibility alias.
678
+ - `--csv` is not supported.
473
679
 
474
- ### in-process manifest
680
+ ### In-process manifest export
475
681
 
476
682
  - `@purveyors/cli/manifest`
477
683
 
478
- Use this when you need the same contract from Node.js without shelling out to the CLI. This package subpath is exported via `./manifest` in `package.json`.
684
+ Use this when you need the same stable contract from Node.js without shelling out to the CLI. This package subpath is exported via `./manifest` in `package.json` and is part of the supported public machine surface for agents and scripts.
479
685
 
480
686
  ```ts
481
687
  import { getCliManifest } from '@purveyors/cli/manifest';
@@ -483,17 +689,16 @@ import { getCliManifest } from '@purveyors/cli/manifest';
483
689
  const manifest = getCliManifest();
484
690
  ```
485
691
 
486
- ## Common Workflows
692
+ ## Common workflows
487
693
 
488
- ### Buy coffee, roast it, rate it, and record a sale
694
+ ### Catalog to inventory to roast to sale
489
695
 
490
696
  ```bash
491
697
  purvey catalog search --origin "Ethiopia" --process "natural" --stocked --pretty
492
698
  purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
493
- purvey inventory list --stocked --pretty
494
699
  purvey roast import ~/artisan/guji-light.alog --coffee-id 7 --pretty
495
700
  purvey tasting rate 7 --aroma 5 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
496
- purvey sales record --roast-id 123 --oz 12 --price 22.00 --buyer "Jane Smith"
701
+ purvey sales record --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz 12 --price 22.00 --buyer "Jane Smith"
497
702
  ```
498
703
 
499
704
  ### Continuous Artisan watch mode
@@ -504,6 +709,8 @@ purvey roast watch ~/artisan/ --auto-match
504
709
  purvey roast watch --resume
505
710
  ```
506
711
 
712
+ Watch mode runs until Ctrl+C or SIGTERM. On shutdown it waits for active imports, commits queued batch-mode roasts, prints the verification summary, and leaves session state available for `--resume`.
713
+
507
714
  ### Export records for spreadsheets
508
715
 
509
716
  ```bash
@@ -512,53 +719,67 @@ purvey roast list --csv > roasts.csv
512
719
  purvey sales list --csv > sales.csv
513
720
  ```
514
721
 
515
- ## ID Reference
722
+ ### Bootstrap an agent or script
723
+
724
+ ```bash
725
+ purvey manifest
726
+ purvey context
727
+ purvey auth login --headless
728
+ purvey auth status 2>/dev/null | jq .
729
+ ```
730
+
731
+ ## ID reference
516
732
 
517
733
  Use the right ID for the right command.
518
734
 
519
- - `catalog_id`: coffee_catalog rows; used by `catalog get`, `inventory add --catalog-id`, `tasting get`, `roast list --catalog-id`
520
- - `inventory id`: green_coffee_inv rows; used by `inventory get/update/delete`, `roast --coffee-id`, `tasting rate`, `roast list --coffee-id`
521
- - `roast_id`: roast_data rows; used by `roast get/delete`, `sales --roast-id`, `roast list --roast-id`
522
- - `sale id`: coffee_sales rows; used by `sales update/delete`
735
+ - `catalog_id`: `coffee_catalog` rows; used by `catalog get`, `catalog similar`, `inventory add --catalog-id`, `tasting get`, `roast list --catalog-id`
736
+ - `inventory id`: `green_coffee_inv` rows; used by `inventory get/update/delete`, `roast --coffee-id`, `tasting rate`, `roast list --coffee-id`
737
+ - `roast_id`: `roast_data` rows; used by `roast get/delete`, `sales --roast-id`, `roast list --roast-id`
738
+ - `sales record` also supports resolving a roast from `inventory id` plus `--batch-name`; if that selector is ambiguous, use exact `roast_id`
739
+ - `sale id`: `coffee_sales` rows; used by `sales update/delete`
523
740
 
524
- ## Environment Variables
741
+ ## Environment variables
525
742
 
526
743
  - `PURVEYORS_SUPABASE_URL`: override the Supabase project URL
527
744
  - `PURVEYORS_SUPABASE_ANON_KEY`: override the Supabase anon key
528
745
  - `PURVEYORS_BASE_URL`: override the Purveyors web base URL
746
+ - `PURVEYORS_API_KEY`: API-key token used by API-backed catalog proof reads and paid-tier similarity reads when you want to call canonical API contracts without relying on the local OAuth session
747
+ - `PARCHMENT_API_KEY`: alternate API-key variable accepted for the same API-backed proof and paid-tier similarity path
529
748
  - `PURVEY_DEBUG`: enable verbose error output
530
749
 
531
- ## For AI Agents
750
+ ## For AI agents
532
751
 
533
- Start here:
752
+ Recommended bootstrap order:
534
753
 
535
754
  ```bash
536
755
  purvey manifest
537
756
  purvey context
538
- ```
539
-
540
- Recommended flow:
541
-
542
- ```bash
543
757
  purvey auth login --headless
544
- purvey auth status 2>/dev/null | jq .
545
- purvey inventory list | jq '.[].id'
546
758
  ```
547
759
 
548
- The CLI is designed to be agent-friendly:
760
+ Use `purvey manifest` as the authoritative machine-readable entry point. Keep `purvey context` for dense operator context, or use `purvey context --json` only when you need compatibility with an existing wrapper.
761
+
762
+ Why this CLI works well for agents:
549
763
 
550
764
  - stable command names
551
- - structured output on stdout
552
- - headless auth flow
553
- - copy-pasteable examples
554
- - a dedicated `manifest` command for machine-readable onboarding
555
- - a dedicated `context` command for dense human-readable onboarding
765
+ - structured stdout by default
766
+ - browser auth with pasted-callback fallback and headless auth
767
+ - documented exit codes and role boundaries
768
+ - dedicated machine-readable manifest command
769
+ - dedicated dense human-readable reference command
556
770
  - `purvey context --json` parity with `purvey manifest`
557
- - an `@purveyors/cli/manifest` export for in-process access
558
- - documented exit codes for programmatic error handling
559
- - `--offset` + `--limit` pagination on all list commands
771
+ - `@purveyors/cli/manifest` export for in-process access
772
+ - `--offset` and `--limit` pagination across list commands
560
773
 
561
- The scripting contract: stdout is always structured success data (JSON or CSV). stderr carries status/spinner output plus fatal errors. In structured or non-interactive modes, fatal errors are JSON envelopes on stderr; in interactive no-flag mode, they stay human-readable. Exit codes are stable and documented above. Never expect success data on stderr.
774
+ The scripting contract is simple: stdout carries successful payloads, stderr carries status and fatal errors, and exit codes stay stable.
775
+
776
+ Agent integration rules of thumb:
777
+
778
+ - Discover first with `purvey manifest`, then call the narrowest command or package subpath that fits the job.
779
+ - Use `purvey context` when a human-readable operator summary is useful before tool selection.
780
+ - Prefer OAuth session tokens for normal user workflows; use `PURVEYORS_API_KEY` or `PARCHMENT_API_KEY` only when you intentionally need the API-key catalog proof path or a paid-tier similarity path.
781
+ - Treat `--include-proof` as API output, not a local scoring feature. If a filter cannot round-trip through `/v1/catalog?include=proof`, the CLI rejects that invocation instead of returning misleading proof data.
782
+ - Treat `catalog similar` as the canonical `/v1/catalog/{id}/similar` contract. Preserve the distinction between `canonical_candidates` and `similar_recommendations`; do not flatten or re-sort grouped results unless you have a specific downstream reason.
562
783
 
563
784
  ## Troubleshooting
564
785
 
@@ -566,31 +787,29 @@ The scripting contract: stdout is always structured success data (JSON or CSV).
566
787
 
567
788
  ```bash
568
789
  purvey auth login
569
- # or for headless environments:
790
+ # or
570
791
  purvey auth login --headless
571
792
  ```
572
793
 
573
- **Catalog commands fail with auth error after logging in**
794
+ **Catalog commands fail after logging in**
574
795
 
575
- Run `purvey auth status` to confirm you have a `viewer` or `member` role. Both are granted automatically on sign-in. If auth status shows a stale session, run `purvey auth logout` then log in again.
796
+ Run `purvey auth status` to confirm you have a `viewer` or `member` role. If the session is stale, run `purvey auth logout` and log in again.
576
797
 
577
798
  **Wrong ID type passed to a command**
578
799
 
579
- The most common mistake is mixing up ID types. Check the [ID Reference](#id-reference) section above. Use `purvey catalog search` to find `catalog_id` values; use `purvey inventory list` to find `inventory id` values.
800
+ Use the [ID reference](#id-reference) section above. `catalog_id` and inventory IDs are different values.
580
801
 
581
- **Pagination: only seeing the first 20 results**
802
+ **Pagination only shows the first page**
582
803
 
583
- All list commands default to 20 results. Use `--limit` and `--offset` to page through larger sets:
804
+ All list commands default to 20 results. Use `--limit` and `--offset`.
584
805
 
585
806
  ```bash
586
- purvey inventory list --limit 20 --offset 0 # page 1
587
- purvey inventory list --limit 20 --offset 20 # page 2
588
- purvey inventory list --limit 20 --offset 40 # page 3
807
+ purvey inventory list --limit 20 --offset 0
808
+ purvey inventory list --limit 20 --offset 20
809
+ purvey inventory list --limit 20 --offset 40
589
810
  ```
590
811
 
591
- **`inventory delete` fails with dependency conflict (exit code 5)**
592
-
593
- The item has dependent roast profiles or sales records. Use `--force` to cascade delete:
812
+ **`inventory delete` fails with dependency conflict**
594
813
 
595
814
  ```bash
596
815
  purvey inventory delete 7 --force --yes
@@ -609,11 +828,16 @@ git clone https://github.com/reedwhetstone/purveyors-cli
609
828
  cd purveyors-cli
610
829
  pnpm install
611
830
  npm run build
831
+ npm run verify:contract
832
+ npm run verify:dist
833
+ npm run verify:prepublish
612
834
  npm run check
613
835
  npm run lint
614
836
  npm test
615
837
  ```
616
838
 
839
+ `npm run verify:prepublish` is the release guardrail. It rebuilds first, re-runs the targeted manifest and output-contract suites, checks the compiled `dist/` artifact, verifies the `npm pack --dry-run` publish surface, and smoke-tests `package.json`, `README.md`, `purvey manifest`, `purvey context --json`, and `@purveyors/cli/manifest`.
840
+
617
841
  Key files:
618
842
 
619
843
  - `src/index.ts`: executable entrypoint
@@ -622,12 +846,15 @@ Key files:
622
846
  - `src/lib/`: business logic and Supabase integration
623
847
  - `src/commands/context.ts`: dense human-readable agent reference
624
848
  - `src/commands/manifest.ts`: machine-readable CLI manifest command
625
- - `src/lib/manifest.ts`: shared manifest contract and renderer
849
+ - `src/lib/manifest.ts`: shared manifest contract, package export list, command metadata, ID guidance, and context renderer
626
850
  - `package.json`: package metadata and export surface, including `./manifest`
627
- - `AGENTS.md`: contributor guide
851
+ - `tests/dist-contract.test.ts`: compiled artifact parity guardrails
852
+ - `AGENTS.md`: canonical contributor guide
853
+
854
+ Live documentation:
628
855
 
629
- Live documentation: [purveyors.io/docs](https://purveyors.io/docs)
630
- Package: [npmjs.com/package/@purveyors/cli](https://www.npmjs.com/package/@purveyors/cli)
856
+ - <https://purveyors.io/docs/cli/overview>
857
+ - <https://purveyors.io/docs/api/overview>
631
858
 
632
859
  ## License
633
860