@purveyors/cli 0.36.0 → 0.36.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 (54) hide show
  1. package/README.md +17 -17
  2. package/dist/commands/auth.d.ts.map +1 -1
  3. package/dist/commands/auth.js +6 -6
  4. package/dist/commands/auth.js.map +1 -1
  5. package/dist/commands/catalog.d.ts +3 -3
  6. package/dist/commands/catalog.d.ts.map +1 -1
  7. package/dist/commands/catalog.js +96 -105
  8. package/dist/commands/catalog.js.map +1 -1
  9. package/dist/commands/context.js +2 -2
  10. package/dist/commands/context.js.map +1 -1
  11. package/dist/commands/inventory.js +32 -32
  12. package/dist/commands/inventory.js.map +1 -1
  13. package/dist/commands/manifest.js +3 -3
  14. package/dist/commands/manifest.js.map +1 -1
  15. package/dist/commands/market.d.ts.map +1 -1
  16. package/dist/commands/market.js +24 -24
  17. package/dist/commands/market.js.map +1 -1
  18. package/dist/commands/price-index.d.ts.map +1 -1
  19. package/dist/commands/price-index.js +17 -17
  20. package/dist/commands/price-index.js.map +1 -1
  21. package/dist/commands/procurement.d.ts.map +1 -1
  22. package/dist/commands/procurement.js +3 -3
  23. package/dist/commands/procurement.js.map +1 -1
  24. package/dist/commands/reference-profile.d.ts.map +1 -1
  25. package/dist/commands/reference-profile.js +15 -15
  26. package/dist/commands/reference-profile.js.map +1 -1
  27. package/dist/commands/roast.d.ts.map +1 -1
  28. package/dist/commands/roast.js +50 -50
  29. package/dist/commands/roast.js.map +1 -1
  30. package/dist/commands/sales.js +21 -21
  31. package/dist/commands/sales.js.map +1 -1
  32. package/dist/commands/skill.d.ts.map +1 -1
  33. package/dist/commands/skill.js +7 -7
  34. package/dist/commands/skill.js.map +1 -1
  35. package/dist/commands/tasting.d.ts.map +1 -1
  36. package/dist/commands/tasting.js +16 -16
  37. package/dist/commands/tasting.js.map +1 -1
  38. package/dist/lib/agent-skill.d.ts.map +1 -1
  39. package/dist/lib/agent-skill.js +1 -5
  40. package/dist/lib/agent-skill.js.map +1 -1
  41. package/dist/lib/catalog.d.ts.map +1 -1
  42. package/dist/lib/catalog.js +11 -16
  43. package/dist/lib/catalog.js.map +1 -1
  44. package/dist/lib/manifest.d.ts.map +1 -1
  45. package/dist/lib/manifest.js +890 -364
  46. package/dist/lib/manifest.js.map +1 -1
  47. package/dist/lib/parchment.d.ts +5 -0
  48. package/dist/lib/parchment.d.ts.map +1 -1
  49. package/dist/lib/parchment.js +5 -1
  50. package/dist/lib/parchment.js.map +1 -1
  51. package/dist/program.d.ts.map +1 -1
  52. package/dist/program.js +66 -15
  53. package/dist/program.js.map +1 -1
  54. package/package.json +1 -1
@@ -15,19 +15,25 @@ const docs = [
15
15
  const roles = [
16
16
  {
17
17
  role: 'viewer',
18
- description: 'valid scoped API key; required for catalog commands except structured process filters',
18
+ description: 'any signed-in account, or any API key with catalog:read; enough for every catalog command',
19
19
  },
20
20
  {
21
21
  role: 'member',
22
- description: 'required for inventory, roast, sales, tasting, and catalog search structured process filters using the scoped API key created by `purvey auth login` or an explicit environment override',
22
+ description: 'a Purveyors membership; required for inventory, roast, sales, tasting, procurement, and reference-profile commands',
23
23
  },
24
24
  ];
25
25
  const globalOptions = [
26
- { flags: '--json', description: 'Output compact JSON explicitly' },
27
- { flags: '--pretty', description: 'Pretty-print JSON output with colors' },
28
- { flags: '--csv', description: 'Output array results as CSV where supported' },
29
- { flags: '--help', description: 'Show help for any command' },
30
- { flags: '--version', description: 'Show version number' },
26
+ {
27
+ flags: '--json',
28
+ description: 'Print results as compact JSON, the default for data commands; use it to be explicit',
29
+ },
30
+ { flags: '--pretty', description: 'Print results as indented, colorized JSON for reading' },
31
+ {
32
+ flags: '--csv',
33
+ description: 'Print list results as CSV on commands that support it',
34
+ },
35
+ { flags: '--help', description: 'Show help for purvey or any command' },
36
+ { flags: '--version', description: 'Print the installed CLI version' },
31
37
  ];
32
38
  const outputModes = [
33
39
  { mode: 'json', description: 'compact JSON, suitable for agents and scripts' },
@@ -39,30 +45,34 @@ const exitCodes = [
39
45
  {
40
46
  exitCode: EXIT_CODES.GENERAL_ERROR,
41
47
  code: 'GENERAL_ERROR',
42
- description: 'unexpected or unclassified error',
48
+ description: 'unexpected error',
43
49
  },
44
50
  {
45
51
  exitCode: EXIT_CODES.INVALID_ARGUMENT,
46
52
  code: 'INVALID_ARGUMENT',
47
- description: 'invalid argument or bad input',
53
+ description: 'invalid argument or input',
48
54
  },
49
55
  {
50
56
  exitCode: EXIT_CODES.AUTH_ERROR,
51
57
  code: 'AUTH_ERROR',
52
- description: 'auth error, missing/revoked key, or wrong role',
58
+ description: 'not signed in, the stored key was revoked, or your account or API key lacks access to this command',
53
59
  },
54
60
  { exitCode: EXIT_CODES.NOT_FOUND, code: 'NOT_FOUND', description: 'resource not found' },
55
61
  {
56
62
  exitCode: EXIT_CODES.DEPENDENCY_CONFLICT,
57
63
  code: 'DEPENDENCY_CONFLICT',
58
- description: 'dependency conflict, for example deleting an inventory lot with dependents',
64
+ description: 'the change conflicts with related records, such as deleting a coffee that still has roasts or sales',
65
+ },
66
+ {
67
+ exitCode: EXIT_CODES.CONFIG_ERROR,
68
+ code: 'CONFIG_ERROR',
69
+ description: 'configuration problem, such as a file path or API address the CLI cannot use',
59
70
  },
60
- { exitCode: EXIT_CODES.CONFIG_ERROR, code: 'CONFIG_ERROR', description: 'local config error' },
61
71
  ];
62
72
  const idTypes = [
63
73
  {
64
74
  name: 'catalog_id',
65
- source: 'coffee_catalog row',
75
+ source: 'a coffee listed in the Purveyors catalog',
66
76
  usedBy: [
67
77
  'catalog get',
68
78
  'catalog similar',
@@ -73,7 +83,7 @@ const idTypes = [
73
83
  },
74
84
  {
75
85
  name: 'inventory_id',
76
- source: 'green_coffee_inv row',
86
+ source: 'a coffee in your green inventory',
77
87
  usedBy: [
78
88
  'inventory get/update/delete',
79
89
  'roast --coffee-id',
@@ -83,7 +93,7 @@ const idTypes = [
83
93
  },
84
94
  {
85
95
  name: 'roast_id',
86
- source: 'roast_data row',
96
+ source: 'one of your roast profiles',
87
97
  usedBy: [
88
98
  'roast get/chart/delete',
89
99
  'roast list --roast-id',
@@ -93,12 +103,12 @@ const idTypes = [
93
103
  },
94
104
  {
95
105
  name: 'sale_id',
96
- source: 'coffee_sales row',
106
+ source: 'one of your recorded sales',
97
107
  usedBy: ['sales update/delete'],
98
108
  },
99
109
  {
100
110
  name: 'reference_profile_id',
101
- source: 'owner-scoped Studio reference profile',
111
+ source: 'one of your Studio reference profiles',
102
112
  usedBy: [
103
113
  'reference-profile get',
104
114
  'reference-profile chart',
@@ -118,10 +128,11 @@ const idTypes = [
118
128
  ],
119
129
  },
120
130
  ];
131
+ const SIMILAR_ACCESS = 'Needs a sign-in (`purvey auth login`) or any API key with catalog:read. The free Green API plan includes it within its monthly quota.';
121
132
  const commandGroups = [
122
133
  {
123
134
  name: 'auth',
124
- summary: 'Manage authentication with purveyors.io',
135
+ summary: 'Sign in to purveyors.io, check your login, and sign out',
125
136
  auth: 'none',
126
137
  subcommands: [
127
138
  {
@@ -130,7 +141,10 @@ const commandGroups = [
130
141
  auth: 'none',
131
142
  sdkMethods: ['cliAuth.create', 'cliAuth.exchange'],
132
143
  options: [
133
- { flags: '--headless', description: 'Print approval URL without opening a browser' },
144
+ {
145
+ flags: '--headless',
146
+ description: 'Print the approval URL instead of opening a browser; use it for agents, SSH sessions, and remote machines. The CLI finishes on its own once you approve',
147
+ },
134
148
  ],
135
149
  examples: ['purvey auth login', 'purvey auth login --headless'],
136
150
  },
@@ -139,7 +153,10 @@ const commandGroups = [
139
153
  summary: 'Show current login status and role',
140
154
  auth: 'none',
141
155
  sdkMethods: ['me'],
142
- options: [{ flags: '--pretty' }, { flags: '--csv' }],
156
+ options: [
157
+ { flags: '--pretty', description: 'Print the status as indented, colorized JSON' },
158
+ { flags: '--csv', description: 'Print the status as a CSV row' },
159
+ ],
143
160
  examples: [
144
161
  'purvey auth status',
145
162
  'purvey auth status --pretty',
@@ -148,13 +165,13 @@ const commandGroups = [
148
165
  },
149
166
  {
150
167
  name: 'whoami',
151
- summary: 'Show the canonical identity, plan, scopes, and capabilities for the active credential',
168
+ summary: 'Show the identity, plan, scopes, and capabilities of the active credential',
152
169
  auth: 'none',
153
170
  sdkMethods: ['me'],
154
171
  notes: [
155
- 'Prints the GET /v1/me response unchanged, including capabilities.profileStudio.',
172
+ 'Prints the account details unchanged: authenticated, userId, appRoles, primaryAppRole, apiPlan, ppiAccess, apiScopes, and capabilities (capabilities.profileStudio shows Studio access).',
156
173
  'Uses PARCHMENT_API_KEY or PURVEYORS_API_KEY when set, otherwise the stored login key.',
157
- 'Without a credential it prints the anonymous principal (authenticated: false).',
174
+ 'Without a credential it prints a signed-out result (authenticated: false).',
158
175
  ],
159
176
  examples: [
160
177
  'purvey auth whoami --pretty',
@@ -171,66 +188,111 @@ const commandGroups = [
171
188
  },
172
189
  {
173
190
  name: 'catalog',
174
- summary: 'Browse the coffee catalog; structured process filters require member access',
191
+ summary: 'Search, rank, and compare catalog coffees and suppliers, and find similar coffees',
175
192
  auth: 'viewer',
176
193
  subcommands: [
177
194
  {
178
195
  name: 'search',
179
- summary: 'Search coffees by origin, process, price, and catalog metadata; structured process filters require member access',
196
+ summary: 'Search coffees by origin, process, price, and catalog metadata',
180
197
  auth: 'viewer',
181
198
  sdkMethods: ['catalog.list'],
182
199
  options: [
183
- { flags: '--origin <origin>' },
184
- { flags: '--process <method>' },
185
- { flags: '--processing-base-method <method>' },
186
- { flags: '--fermentation-type <type>' },
187
- { flags: '--process-additive <additive>' },
188
- { flags: '--processing-disclosure-level <level>' },
189
- { flags: '--processing-confidence-min <n>' },
190
- { flags: '--price-min <n>' },
191
- { flags: '--price-max <n>' },
192
- { flags: '--name <text>' },
193
- { flags: '--ids <n,n,...>' },
194
- { flags: '--variety <text>' },
195
- { flags: '--stocked-days <n>' },
200
+ {
201
+ flags: '--origin <origin>',
202
+ description: 'Only coffees from this origin; matches continent, country, or region names, case-insensitive and partial',
203
+ },
204
+ {
205
+ flags: '--process <method>',
206
+ description: 'Only coffees whose processing label contains this text, such as natural or washed (case-insensitive)',
207
+ },
208
+ {
209
+ flags: '--processing-base-method <method>',
210
+ description: 'Only coffees whose structured base process exactly matches this value, such as Natural or Washed. List values with `purvey catalog facets processing_base_method`',
211
+ },
212
+ {
213
+ flags: '--fermentation-type <type>',
214
+ description: 'Only coffees whose structured fermentation type exactly matches this value, such as Anaerobic. List values with `purvey catalog facets fermentation_type`',
215
+ },
216
+ {
217
+ flags: '--process-additive <additive>',
218
+ description: 'Only coffees whose process discloses exactly this additive, such as hops',
219
+ },
220
+ {
221
+ flags: '--processing-disclosure-level <level>',
222
+ description: 'Only coffees whose supplier disclosed process details at exactly this level',
223
+ },
224
+ {
225
+ flags: '--processing-confidence-min <n>',
226
+ description: 'Only coffees whose process details were identified with at least this confidence',
227
+ minimum: 0,
228
+ maximum: 1,
229
+ },
230
+ {
231
+ flags: '--price-min <n>',
232
+ description: 'Lowest price per pound to include, in US dollars',
233
+ },
234
+ {
235
+ flags: '--price-max <n>',
236
+ description: 'Highest price per pound to include, in US dollars',
237
+ },
238
+ {
239
+ flags: '--name <text>',
240
+ description: 'Only coffees whose name contains this text (case-insensitive)',
241
+ },
242
+ {
243
+ flags: '--ids <n,n,...>',
244
+ description: 'Fetch these catalog IDs, comma-separated, instead of searching; --limit and --offset are ignored',
245
+ },
246
+ {
247
+ flags: '--variety <text>',
248
+ description: 'Only coffees whose variety or cultivar contains this text (case-insensitive)',
249
+ },
250
+ {
251
+ flags: '--stocked-days <n>',
252
+ description: 'Only coffees that came into stock within the last N days',
253
+ },
196
254
  {
197
255
  flags: '--supplier <name>',
198
- description: 'Canonical /v1/catalog supplier filter (partial source-name match)',
256
+ description: 'Only coffees from suppliers whose name contains this text (case-insensitive)',
199
257
  },
200
258
  {
201
259
  flags: '--drying-method <method>',
202
- description: 'Canonical /v1/catalog dryingMethod filter; Parchment owns matching',
260
+ description: 'Only coffees whose drying method or processing text contains this value, such as raised bed (case-insensitive)',
203
261
  },
204
262
  {
205
263
  flags: '--flavor <keywords>',
206
- description: 'Comma-separated canonical /v1/catalog flavorKeywords; a row matches any keyword',
264
+ description: 'Comma-separated flavor keywords searched in coffee descriptions and tasting notes; a coffee matches if it mentions any of them',
265
+ },
266
+ {
267
+ flags: '--stocked',
268
+ description: 'Only coffees currently in stock; without it, results also include coffees no longer stocked',
269
+ },
270
+ {
271
+ flags: '--sort <field>',
272
+ description: 'Sort by price (cheapest first), price-desc, name, or origin (by country); without it, the most recently stocked coffees come first',
273
+ },
274
+ {
275
+ flags: '--offset <n>',
276
+ description: 'Number of results to skip when paging; must be a multiple of --limit',
277
+ defaultValue: 0,
207
278
  },
208
- { flags: '--stocked' },
209
- { flags: '--sort <field>' },
210
- { flags: '--offset <n>', defaultValue: 0 },
211
279
  {
212
280
  flags: '--limit <n>',
213
- description: `Maximum results to return, ${CLI_NUMERIC_BOUNDS.catalogSearchLimit.minimum}-${CLI_NUMERIC_BOUNDS.catalogSearchLimit.maximum}`,
281
+ description: 'Maximum number of coffees to return',
214
282
  defaultValue: 10,
215
283
  minimum: CLI_NUMERIC_BOUNDS.catalogSearchLimit.minimum,
216
284
  maximum: CLI_NUMERIC_BOUNDS.catalogSearchLimit.maximum,
217
285
  },
218
286
  {
219
287
  flags: '--include-proof',
220
- description: 'Request canonical proof summaries from /v1/catalog?include=proof',
221
- notes: [
222
- 'Consumes the API proof summary; the CLI does not compute proof fields locally.',
223
- 'The SDK supplies the canonical proof-summary-v1 row projection.',
224
- ],
288
+ description: 'Add a proof summary to each coffee: how well its process, provenance, freshness, and pricing details are supported (strong, partial, limited, or not available)',
225
289
  },
226
290
  ],
227
291
  notes: [
228
292
  'All filters are optional. Without flags, returns up to --limit results.',
229
- 'Structured process filters map to canonical /v1/catalog query names.',
230
- 'Structured process filters require member access through the scoped API key created by `purvey auth login` or an explicit environment override.',
231
293
  '--ids fetches specific catalog items by ID, ignoring --limit and --offset.',
232
294
  '--offset + --limit enables pagination through large result sets.',
233
- '--include-proof uses the canonical /v1/catalog proof summary include and preserves the default output shape when omitted.',
295
+ 'Without --include-proof, the output shape is unchanged.',
234
296
  'PURVEYORS_API_KEY or PARCHMENT_API_KEY overrides the scoped API key stored by `purvey auth login`.',
235
297
  ],
236
298
  examples: [
@@ -252,7 +314,7 @@ const commandGroups = [
252
314
  {
253
315
  name: 'catalog_id',
254
316
  cliToken: 'id',
255
- description: 'coffee_catalog.catalog_id',
317
+ description: 'Catalog ID of the coffee',
256
318
  required: true,
257
319
  idType: 'catalog_id',
258
320
  },
@@ -260,10 +322,7 @@ const commandGroups = [
260
322
  options: [
261
323
  {
262
324
  flags: '--include-proof',
263
- description: 'Request the canonical proof summary from /v1/catalog?include=proof',
264
- notes: [
265
- 'Consumes the API proof summary; the CLI does not compute proof fields locally.',
266
- ],
325
+ description: "Add the proof summary: how well the coffee's process, provenance, freshness, and pricing details are supported",
267
326
  },
268
327
  ],
269
328
  examples: [
@@ -286,21 +345,21 @@ const commandGroups = [
286
345
  arguments: [
287
346
  {
288
347
  name: 'field',
289
- description: 'optional facet field: supplier, country, processing_base_method, fermentation_type, drying_method, grade, wholesale',
348
+ description: 'The facet to list: supplier, country, processing_base_method, fermentation_type, drying_method, grade, or wholesale. Without it, every facet is listed',
290
349
  required: false,
291
350
  },
292
351
  ],
293
352
  options: [
294
353
  {
295
354
  flags: '--all',
296
- description: 'Use all visible catalog rows instead of the default stocked-only scope.',
355
+ description: 'Count every coffee you can see, including ones no longer stocked; without it, only coffees currently in stock are counted',
297
356
  },
298
357
  ],
299
358
  notes: [
300
- 'Without a field, prints the canonical /v1/catalog/facets envelope (values, facets, meta) unchanged.',
301
- "With a field, prints { field, facet, data, meta }: that facet's counted values and Parchment's meta.",
302
- 'Counts are computed by Parchment; counts for multi-valued dimensions can overlap, so do not sum them.',
303
- 'Defaults to currently stocked catalog rows; use --all for all visible rows.',
359
+ 'Without a field, prints every facet with its counted values and meta (values, facets, meta).',
360
+ "With a field, prints { field, facet, data, meta }: that facet's counted values and meta.",
361
+ 'Counts for multi-valued dimensions can overlap, so do not sum them.',
362
+ 'Defaults to currently stocked coffees; use --all for every coffee you can see.',
304
363
  ],
305
364
  examples: [
306
365
  'purvey catalog facets supplier --pretty',
@@ -310,34 +369,59 @@ const commandGroups = [
310
369
  },
311
370
  {
312
371
  name: 'rank',
313
- summary: 'Rank catalog candidates by deterministic objective',
372
+ summary: 'Rank catalog coffees for a goal: premium, value, fresh arrivals, or rare origins',
314
373
  auth: 'viewer',
315
374
  sdkMethods: ['catalog.rank'],
316
375
  options: [
317
- { flags: '--objective <objective>', defaultValue: 'premium' },
376
+ {
377
+ flags: '--objective <objective>',
378
+ description: 'What to rank for: premium (highest Purveyor Score), value (score per dollar), fresh_arrival (newest arrivals), or rare_origin (least common origins in the sample)',
379
+ defaultValue: 'premium',
380
+ },
318
381
  {
319
382
  flags: '--supplier <name>',
320
- description: 'Canonical /v1/catalog/rank supplier filter',
383
+ description: 'Only coffees from suppliers whose name contains this text',
384
+ },
385
+ { flags: '--country <country>', description: 'Only coffees from this country' },
386
+ {
387
+ flags: '--process <method>',
388
+ description: 'Only coffees with this processing method, such as washed or natural',
389
+ },
390
+ {
391
+ flags: '--stocked',
392
+ description: 'Only coffees currently in stock; this is already the default unless you pass --all',
321
393
  },
322
- { flags: '--country <country>' },
323
- { flags: '--process <method>' },
324
- { flags: '--stocked' },
325
394
  {
326
395
  flags: '--all',
327
- description: 'Use all visible catalog rows instead of the default stocked-only scope.',
396
+ description: 'Rank every coffee you can see, including ones no longer stocked, instead of only coffees in stock',
328
397
  },
329
- { flags: '--price-max <n>' },
330
- { flags: '--min-score <n>' },
398
+ {
399
+ flags: '--price-max <n>',
400
+ description: 'Highest price per pound to include, in US dollars',
401
+ },
402
+ { flags: '--min-score <n>', description: 'Lowest Purveyor Score to include' },
331
403
  {
332
404
  flags: '--non-wholesale-only',
333
- description: 'Apply a query-level filter for non-wholesale or unknown-wholesale listings before sampling.',
405
+ description: 'Leave out wholesale listings, keeping retail and listings with unknown wholesale status, before sampling',
406
+ },
407
+ {
408
+ flags: '--sample-size <n>',
409
+ description: 'Number of matching coffees to consider before ranking; a smaller sample is faster but may miss candidates',
410
+ defaultValue: 5000,
411
+ minimum: 1,
412
+ maximum: 5000,
413
+ },
414
+ {
415
+ flags: '--limit <n>',
416
+ description: 'Maximum number of ranked coffees to return',
417
+ defaultValue: 10,
418
+ minimum: 1,
419
+ maximum: 50,
334
420
  },
335
- { flags: '--sample-size <n>', defaultValue: 5000 },
336
- { flags: '--limit <n>', defaultValue: 10 },
337
421
  ],
338
422
  notes: [
339
423
  'Objectives are premium, value, fresh_arrival, and rare_origin.',
340
- 'Uses coffee_catalog.purveyor_score as the canonical quality signal.',
424
+ 'Uses the Purveyor Score as the quality signal.',
341
425
  'Output metadata reports stocked_only/scope, sample scope, and truncation; rare_origin is rarity within sampled matching candidates.',
342
426
  ],
343
427
  examples: [
@@ -352,17 +436,44 @@ const commandGroups = [
352
436
  auth: 'viewer',
353
437
  sdkMethods: ['catalog.rankPremium'],
354
438
  options: [
355
- { flags: '--origin <origin>' },
356
- { flags: '--process <method>' },
357
- { flags: '--stocked' },
358
- { flags: '--price-max <n>' },
359
- { flags: '--min-score <n>' },
360
- { flags: '--include-unscored' },
361
- { flags: '--sample-size <n>', defaultValue: 250 },
362
- { flags: '--limit <n>', defaultValue: 10 },
439
+ {
440
+ flags: '--origin <origin>',
441
+ description: 'Only coffees from this continent, country, or region (case-insensitive)',
442
+ },
443
+ {
444
+ flags: '--process <method>',
445
+ description: 'Only coffees with this processing method, such as washed or natural',
446
+ },
447
+ {
448
+ flags: '--stocked',
449
+ description: 'Only coffees currently in stock; without it, coffees no longer stocked are ranked too',
450
+ },
451
+ {
452
+ flags: '--price-max <n>',
453
+ description: 'Highest price per pound to include, in US dollars',
454
+ },
455
+ { flags: '--min-score <n>', description: 'Lowest Purveyor Score to include' },
456
+ {
457
+ flags: '--include-unscored',
458
+ description: 'Also list coffees without a Purveyor Score, after all scored coffees',
459
+ },
460
+ {
461
+ flags: '--sample-size <n>',
462
+ description: 'Number of matching coffees to consider before ranking',
463
+ defaultValue: 250,
464
+ minimum: 1,
465
+ maximum: 5000,
466
+ },
467
+ {
468
+ flags: '--limit <n>',
469
+ description: 'Maximum number of ranked coffees to return',
470
+ defaultValue: 10,
471
+ minimum: 1,
472
+ maximum: 50,
473
+ },
363
474
  ],
364
475
  notes: [
365
- 'Exposes coffee_catalog.purveyor_score plus confidence, tier, factor breakdown, version, and update metadata; the CLI does not recompute the upstream score model.',
476
+ 'Shows the Purveyor Score with its confidence, tier, factor breakdown, version, and update metadata.',
366
477
  'Output includes rank, catalog context, pricing, stocked status, Purveyor Score qualifiers, and transparent ranking signals for agents.',
367
478
  'Output metadata reports sample ordering and whether more rows matched than the requested sample size.',
368
479
  ],
@@ -373,19 +484,37 @@ const commandGroups = [
373
484
  },
374
485
  {
375
486
  name: 'supplier-list',
376
- summary: 'List supplier aggregates from catalog rows',
487
+ summary: 'Summarize suppliers from the catalog coffees they list',
377
488
  auth: 'viewer',
378
489
  sdkMethods: ['catalog.suppliers'],
379
490
  options: [
380
- { flags: '--country <country>' },
381
- { flags: '--stocked' },
382
- { flags: '--non-wholesale-only' },
383
- { flags: '--sample-size <n>', defaultValue: 5000 },
384
- { flags: '--limit <n>', defaultValue: 25 },
491
+ { flags: '--country <country>', description: 'Only count coffees from this country' },
492
+ {
493
+ flags: '--stocked',
494
+ description: 'Only count coffees currently in stock; without it, coffees no longer stocked count too',
495
+ },
496
+ {
497
+ flags: '--non-wholesale-only',
498
+ description: 'Leave out wholesale listings before suppliers are summarized',
499
+ },
500
+ {
501
+ flags: '--sample-size <n>',
502
+ description: 'Number of catalog coffees to read per page before summarizing suppliers',
503
+ defaultValue: 5000,
504
+ minimum: 1,
505
+ maximum: 5000,
506
+ },
507
+ {
508
+ flags: '--limit <n>',
509
+ description: 'Maximum number of suppliers to return',
510
+ defaultValue: 25,
511
+ minimum: 1,
512
+ maximum: 100,
513
+ },
385
514
  ],
386
515
  notes: [
387
516
  'Summarizes supplier counts, stocked counts, Purveyor Score coverage, average score, average confidence, price range, origin/process coverage, and representative top coffees with score qualifiers.',
388
- 'Country and non-wholesale filters are applied at the catalog query layer before supplier aggregation.',
517
+ 'Country and non-wholesale filters apply to coffees before suppliers are summarized.',
389
518
  'Output metadata reports source-ordered pagination, rows examined, and whether the aggregate is sample-limited.',
390
519
  ],
391
520
  examples: ['purvey catalog supplier-list --country Ethiopia --non-wholesale-only --pretty'],
@@ -398,16 +527,34 @@ const commandGroups = [
398
527
  arguments: [
399
528
  {
400
529
  name: 'supplier',
401
- description: 'supplier/source name query',
530
+ description: 'Supplier name to look up (case-insensitive, partial match)',
402
531
  required: true,
403
532
  },
404
533
  ],
405
534
  options: [
406
- { flags: '--country <country>' },
407
- { flags: '--stocked' },
408
- { flags: '--non-wholesale-only' },
409
- { flags: '--top-coffees <n>', defaultValue: 5 },
410
- { flags: '--sample-size <n>', defaultValue: 5000 },
535
+ { flags: '--country <country>', description: 'Only count coffees from this country' },
536
+ {
537
+ flags: '--stocked',
538
+ description: 'Only count coffees currently in stock; without it, coffees no longer stocked count too',
539
+ },
540
+ {
541
+ flags: '--non-wholesale-only',
542
+ description: 'Leave out wholesale listings before the supplier is summarized',
543
+ },
544
+ {
545
+ flags: '--top-coffees <n>',
546
+ description: 'Number of representative top coffees to include',
547
+ defaultValue: 5,
548
+ minimum: 1,
549
+ maximum: 25,
550
+ },
551
+ {
552
+ flags: '--sample-size <n>',
553
+ description: 'Number of catalog coffees to read per page before summarizing',
554
+ defaultValue: 5000,
555
+ minimum: 1,
556
+ maximum: 5000,
557
+ },
411
558
  ],
412
559
  notes: [
413
560
  'Supplier matching is case-insensitive and partial, mirroring catalog search.',
@@ -421,22 +568,40 @@ const commandGroups = [
421
568
  auth: 'viewer',
422
569
  sdkMethods: ['catalog.supplierRank'],
423
570
  options: [
424
- { flags: '--country <country>' },
425
- { flags: '--stocked' },
426
- { flags: '--non-wholesale-only' },
571
+ { flags: '--country <country>', description: 'Only count coffees from this country' },
572
+ {
573
+ flags: '--stocked',
574
+ description: 'Only count coffees currently in stock; without it, coffees no longer stocked count too',
575
+ },
576
+ {
577
+ flags: '--non-wholesale-only',
578
+ description: 'Leave out wholesale listings before suppliers are ranked',
579
+ },
427
580
  {
428
581
  flags: '--min-coffees <n>',
429
- description: `Minimum catalog rows required per supplier, ${CLI_NUMERIC_BOUNDS.supplierMinCoffees.minimum}-${CLI_NUMERIC_BOUNDS.supplierMinCoffees.maximum}`,
582
+ description: 'Minimum number of matching coffees a supplier needs to be ranked',
430
583
  defaultValue: 1,
431
584
  minimum: CLI_NUMERIC_BOUNDS.supplierMinCoffees.minimum,
432
585
  maximum: CLI_NUMERIC_BOUNDS.supplierMinCoffees.maximum,
433
586
  },
434
- { flags: '--sample-size <n>', defaultValue: 5000 },
435
- { flags: '--limit <n>', defaultValue: 25 },
587
+ {
588
+ flags: '--sample-size <n>',
589
+ description: 'Number of catalog coffees to read per page before ranking suppliers',
590
+ defaultValue: 5000,
591
+ minimum: 1,
592
+ maximum: 5000,
593
+ },
594
+ {
595
+ flags: '--limit <n>',
596
+ description: 'Maximum number of suppliers to return',
597
+ defaultValue: 25,
598
+ minimum: 1,
599
+ maximum: 100,
600
+ },
436
601
  ],
437
602
  notes: [
438
603
  'Ranks suppliers by average Purveyor Score, then currently stocked count.',
439
- 'Country and non-wholesale filters are applied at the catalog query layer before supplier aggregation.',
604
+ 'Country and non-wholesale filters apply to coffees before suppliers are ranked.',
440
605
  'Output metadata reports source-ordered pagination, rows examined, and whether the aggregate is sample-limited.',
441
606
  ],
442
607
  examples: [
@@ -445,14 +610,14 @@ const commandGroups = [
445
610
  },
446
611
  {
447
612
  name: 'similar',
448
- summary: 'Fetch beta canonical /v1/catalog/{id}/similar groups for likely same-lot candidates and similar recommendations',
449
- auth: 'member',
613
+ summary: 'Beta: find likely same-lot candidates and similar coffees for a catalog coffee',
614
+ auth: 'viewer',
450
615
  sdkMethods: ['catalog.similar'],
451
616
  arguments: [
452
617
  {
453
618
  name: 'catalog_id',
454
619
  cliToken: 'id',
455
- description: 'coffee_catalog.catalog_id',
620
+ description: 'Catalog ID of the coffee to match',
456
621
  required: true,
457
622
  idType: 'catalog_id',
458
623
  },
@@ -460,23 +625,34 @@ const commandGroups = [
460
625
  options: [
461
626
  {
462
627
  flags: '--threshold <score>',
628
+ description: 'Minimum similarity score a match needs; higher values return fewer, closer matches',
463
629
  defaultValue: 0.7,
464
- description: 'Minimum canonical similarity threshold, 0.5-0.99',
630
+ minimum: 0.5,
631
+ maximum: 0.99,
632
+ },
633
+ {
634
+ flags: '--limit <count>',
635
+ description: 'Maximum number of matches to return',
636
+ defaultValue: 10,
637
+ minimum: 1,
638
+ maximum: 25,
639
+ },
640
+ {
641
+ flags: '--stocked-only',
642
+ description: 'Only return coffees currently in stock; without it, matches can include coffees no longer stocked',
465
643
  },
466
- { flags: '--limit <count>', defaultValue: 10, description: 'Maximum results, 1-25' },
467
- { flags: '--stocked-only', description: 'Restrict results to stocked coffees' },
468
644
  {
469
645
  flags: '--mode <mode>',
646
+ description: 'Which matches to return: all, likely_same (probably the same lot listed elsewhere), or similar_profile (substitutes with a similar profile)',
470
647
  defaultValue: 'all',
471
- description: 'Filter canonical groups: all, likely_same, or similar_profile',
472
648
  },
473
649
  ],
474
650
  notes: [
475
- 'Uses the beta canonical /v1/catalog/{id}/similar API contract, not the legacy direct RPC path.',
476
- 'Default JSON output is the grouped canonical response object with data.target, data.groups.canonical_candidates, data.groups.similar_recommendations, optional data.matches, and meta.',
477
- 'canonical_candidates are likely same-lot candidates; similar_recommendations are profile substitutes and expose blocker reasons when identity gates disagree.',
478
- 'Preserves classification_version, query_strategy, proof summaries, pricing metadata, blocker details, and score dimensions supplied by the API.',
479
- 'Use the scoped member API key created by `purvey auth login`, or override it with PURVEYORS_API_KEY or PARCHMENT_API_KEY.',
651
+ SIMILAR_ACCESS,
652
+ 'Results are beta candidates to review, not confirmed matches.',
653
+ 'Default JSON output groups results under data.target, data.groups.canonical_candidates, data.groups.similar_recommendations, optional data.matches, and meta.',
654
+ 'canonical_candidates are likely same-lot candidates; similar_recommendations are profile substitutes and expose blocker reasons when identity details disagree.',
655
+ 'Keeps classification_version, query_strategy, proof summaries, pricing metadata, blocker details, and score dimensions as returned.',
480
656
  ],
481
657
  examples: [
482
658
  'purvey catalog similar 1182 --threshold 0.85 --stocked-only --json',
@@ -496,16 +672,36 @@ const commandGroups = [
496
672
  auth: 'member',
497
673
  sdkMethods: ['inventory.list'],
498
674
  options: [
499
- { flags: '--stocked' },
500
- { flags: '--catalog-id <id>' },
501
- { flags: '--purchase-date-start <YYYY-MM-DD>' },
502
- { flags: '--purchase-date-end <YYYY-MM-DD>' },
503
- { flags: '--origin <country>' },
504
- { flags: '--limit <n>', defaultValue: 20 },
505
- { flags: '--offset <n>', defaultValue: 0 },
675
+ { flags: '--stocked', description: 'Only coffees you have marked as in stock' },
676
+ {
677
+ flags: '--catalog-id <id>',
678
+ description: 'Only inventory items bought from this catalog coffee (a catalog ID)',
679
+ },
680
+ {
681
+ flags: '--purchase-date-start <YYYY-MM-DD>',
682
+ description: 'Only purchases made on or after this date',
683
+ },
684
+ {
685
+ flags: '--purchase-date-end <YYYY-MM-DD>',
686
+ description: 'Only purchases made on or before this date',
687
+ },
688
+ {
689
+ flags: '--origin <country>',
690
+ description: 'Only coffees whose country of origin contains this text',
691
+ },
692
+ {
693
+ flags: '--limit <n>',
694
+ description: 'Maximum number of items to return',
695
+ defaultValue: 20,
696
+ },
697
+ {
698
+ flags: '--offset <n>',
699
+ description: 'Number of items to skip when paging',
700
+ defaultValue: 0,
701
+ },
506
702
  ],
507
703
  notes: [
508
- 'Returns green_coffee_inv rows joined with catalog details.',
704
+ 'Returns your inventory items with their catalog details.',
509
705
  'The returned id field is the inventory ID, distinct from catalog_id.',
510
706
  '--offset + --limit enables pagination through large result sets.',
511
707
  ],
@@ -523,7 +719,7 @@ const commandGroups = [
523
719
  {
524
720
  name: 'inventory_id',
525
721
  cliToken: 'id',
526
- description: 'green_coffee_inv.id',
722
+ description: 'Inventory ID of the item',
527
723
  required: true,
528
724
  idType: 'inventory_id',
529
725
  },
@@ -539,25 +735,41 @@ const commandGroups = [
539
735
  options: [
540
736
  {
541
737
  flags: '--catalog-id <id>',
542
- description: 'Catalog lot to add; exactly one of --catalog-id or --manual-name',
738
+ description: 'Catalog ID of the coffee you bought; use exactly one of --catalog-id or --manual-name',
543
739
  },
544
740
  {
545
741
  flags: '--manual-name <name>',
546
- description: 'Coffee name for a lot that is not in the catalog',
742
+ description: 'Name for a coffee that is not in the catalog; use exactly one of --catalog-id or --manual-name',
743
+ },
744
+ {
745
+ flags: '--qty <lbs>',
746
+ description: 'Quantity purchased, in pounds',
747
+ requiredInFlagMode: true,
748
+ },
749
+ {
750
+ flags: '--cost <dollars>',
751
+ description: 'Total amount paid for the beans, in US dollars (not the price per pound)',
752
+ },
753
+ {
754
+ flags: '--tax-ship <dollars>',
755
+ description: 'Tax and shipping paid for this purchase, in US dollars',
756
+ },
757
+ { flags: '--notes <text>', description: 'Free-text notes about this purchase' },
758
+ {
759
+ flags: '--purchase-date <YYYY-MM-DD>',
760
+ description: 'Date of purchase; defaults to today',
761
+ },
762
+ {
763
+ flags: '--form',
764
+ description: 'Prompt for a catalog coffee, quantity, total cost, and notes; the purchase date is set to today',
547
765
  },
548
- { flags: '--qty <lbs>', requiredInFlagMode: true },
549
- { flags: '--cost <dollars>' },
550
- { flags: '--tax-ship <dollars>' },
551
- { flags: '--notes <text>' },
552
- { flags: '--purchase-date <YYYY-MM-DD>' },
553
- { flags: '--form' },
554
766
  ],
555
767
  notes: [
556
768
  'Flag mode requires --qty and exactly one of --catalog-id or --manual-name.',
557
- '--manual-name creates a manual coffee record when Parchment has manual inventory writes enabled.',
769
+ '--manual-name creates a manual coffee record when manual inventory entries are enabled.',
558
770
  ],
559
771
  examples: [
560
- 'purvey inventory add --catalog-id 128 --qty 10 --cost 8.50 --pretty',
772
+ 'purvey inventory add --catalog-id 128 --qty 10 --cost 85.00 --pretty',
561
773
  'purvey inventory add --manual-name "Farm-gate Ethiopia lot 7" --qty 12 --cost 96 --pretty',
562
774
  ],
563
775
  },
@@ -571,18 +783,30 @@ const commandGroups = [
571
783
  {
572
784
  name: 'inventory_id',
573
785
  cliToken: 'id',
574
- description: 'green_coffee_inv.id',
786
+ description: 'Inventory ID of the item',
575
787
  required: true,
576
788
  idType: 'inventory_id',
577
789
  },
578
790
  ],
579
791
  options: [
580
- { flags: '--qty <lbs>' },
581
- { flags: '--cost <dollars>' },
582
- { flags: '--tax-ship <dollars>' },
583
- { flags: '--notes <text>' },
584
- { flags: '--stocked <true|false>' },
585
- { flags: '--rank <n>', description: 'Owner-assigned integer rank for this lot' },
792
+ { flags: '--qty <lbs>', description: 'New purchased quantity, in pounds' },
793
+ {
794
+ flags: '--cost <dollars>',
795
+ description: 'New total bean cost, in US dollars (not the price per pound)',
796
+ },
797
+ {
798
+ flags: '--tax-ship <dollars>',
799
+ description: 'New tax and shipping amount, in US dollars',
800
+ },
801
+ { flags: '--notes <text>', description: 'Replace the notes for this item' },
802
+ {
803
+ flags: '--stocked <true|false>',
804
+ description: 'Mark the coffee as in stock (true) or used up (false)',
805
+ },
806
+ {
807
+ flags: '--rank <n>',
808
+ description: 'Your own whole-number ranking for this coffee, used to order your inventory',
809
+ },
586
810
  ],
587
811
  },
588
812
  {
@@ -594,18 +818,23 @@ const commandGroups = [
594
818
  {
595
819
  name: 'inventory_id',
596
820
  cliToken: 'id',
597
- description: 'green_coffee_inv.id',
821
+ description: 'Inventory ID of the item',
598
822
  required: true,
599
823
  idType: 'inventory_id',
600
824
  },
601
825
  ],
602
- options: [{ flags: '--yes' }],
826
+ options: [
827
+ {
828
+ flags: '--yes',
829
+ description: 'Delete without asking for confirmation; needed in scripts and agents',
830
+ },
831
+ ],
603
832
  },
604
833
  ],
605
834
  },
606
835
  {
607
836
  name: 'roast',
608
- summary: 'Browse and manage your roast profiles',
837
+ summary: 'Record roasts, import Artisan .alog files, and watch a folder for new roasts',
609
838
  auth: 'member',
610
839
  subcommands: [
611
840
  {
@@ -614,20 +843,46 @@ const commandGroups = [
614
843
  auth: 'member',
615
844
  sdkMethods: ['roasts.list'],
616
845
  options: [
617
- { flags: '--coffee-id <id>' },
618
- { flags: '--roast-id <id>' },
619
- { flags: '--batch-name <text>' },
620
- { flags: '--coffee-name <text>' },
621
- { flags: '--date-start <YYYY-MM-DD>' },
622
- { flags: '--date-end <YYYY-MM-DD>' },
623
- { flags: '--stocked' },
624
- { flags: '--catalog-id <id>' },
625
- { flags: '--limit <n>', defaultValue: 20 },
626
- { flags: '--offset <n>', defaultValue: 0 },
846
+ {
847
+ flags: '--coffee-id <id>',
848
+ description: 'Only roasts of this inventory item (an inventory ID, not a catalog ID)',
849
+ },
850
+ { flags: '--roast-id <id>', description: 'Only the roast with this roast ID' },
851
+ {
852
+ flags: '--batch-name <text>',
853
+ description: 'Only roasts whose batch name contains this text (case-insensitive)',
854
+ },
855
+ {
856
+ flags: '--coffee-name <text>',
857
+ description: 'Only roasts of coffees whose name contains this text (case-insensitive)',
858
+ },
859
+ {
860
+ flags: '--date-start <YYYY-MM-DD>',
861
+ description: 'Only roasts on or after this date',
862
+ },
863
+ { flags: '--date-end <YYYY-MM-DD>', description: 'Only roasts on or before this date' },
864
+ {
865
+ flags: '--stocked',
866
+ description: 'Only roasts of coffees still marked as in stock in your inventory',
867
+ },
868
+ {
869
+ flags: '--catalog-id <id>',
870
+ description: 'Only roasts of coffees bought from this catalog coffee (a catalog ID)',
871
+ },
872
+ {
873
+ flags: '--limit <n>',
874
+ description: 'Maximum number of roasts to return',
875
+ defaultValue: 20,
876
+ },
877
+ {
878
+ flags: '--offset <n>',
879
+ description: 'Number of roasts to skip when paging',
880
+ defaultValue: 0,
881
+ },
627
882
  ],
628
883
  notes: [
629
884
  '--coffee-id expects inventory_id, not catalog_id.',
630
- '--catalog-id filters by coffee_catalog.catalog_id.',
885
+ '--catalog-id filters by catalog ID.',
631
886
  '--date-start and --date-end accept YYYY-MM-DD format.',
632
887
  '--offset + --limit enables pagination through large result sets.',
633
888
  ],
@@ -642,23 +897,32 @@ const commandGroups = [
642
897
  {
643
898
  name: 'roast_id',
644
899
  cliToken: 'id',
645
- description: 'roast_data.roast_id',
900
+ description: 'Roast ID',
646
901
  required: true,
647
902
  idType: 'roast_id',
648
903
  },
649
904
  ],
650
- options: [{ flags: '--include-temps' }, { flags: '--include-events' }],
905
+ options: [
906
+ {
907
+ flags: '--include-temps',
908
+ description: 'Add the full temperature curve; output can be large',
909
+ },
910
+ {
911
+ flags: '--include-events',
912
+ description: 'Add roast event markers such as first crack and drop',
913
+ },
914
+ ],
651
915
  },
652
916
  {
653
917
  name: 'chart',
654
- summary: 'Fetch the sampled chart model for one roast (series, events, revision)',
918
+ summary: 'Get sampled chart data for one roast: temperature series, events, and chart revision',
655
919
  auth: 'member',
656
920
  sdkMethods: ['roasts.chartData'],
657
921
  arguments: [
658
922
  {
659
923
  name: 'roast_id',
660
924
  cliToken: 'id',
661
- description: 'roast_data.roast_id',
925
+ description: 'Roast ID',
662
926
  required: true,
663
927
  idType: 'roast_id',
664
928
  },
@@ -666,13 +930,14 @@ const commandGroups = [
666
930
  options: [
667
931
  {
668
932
  flags: '--target-points <n>',
669
- description: 'Approximate samples per series, 50-1000; Parchment defaults to 400',
933
+ description: 'Approximate number of samples per temperature series',
934
+ defaultValue: 400,
670
935
  minimum: 50,
671
936
  maximum: 1000,
672
937
  },
673
938
  ],
674
939
  notes: [
675
- "Returns Parchment's canonical chart-data envelope unchanged: sampled series, events, and metadata.",
940
+ 'Returns the roast chart data unchanged: sampled series, events, and metadata.',
676
941
  'data.metadata.revision identifies the immutable chart revision used by reference-profile compare.',
677
942
  ],
678
943
  examples: [
@@ -687,15 +952,34 @@ const commandGroups = [
687
952
  sdkMethods: ['roasts.create'],
688
953
  confirmedActionEquivalents: ['create_roast_session'],
689
954
  options: [
690
- { flags: '--coffee-id <id>', requiredInFlagMode: true },
691
- { flags: '--batch-name <name>' },
692
- { flags: '--oz-in <oz>' },
693
- { flags: '--oz-out <oz>' },
694
- { flags: '--roast-date <YYYY-MM-DD>' },
695
- { flags: '--notes <text>' },
696
- { flags: '--targets <text>' },
697
- { flags: '--roaster-type <text>' },
698
- { flags: '--form' },
955
+ {
956
+ flags: '--coffee-id <id>',
957
+ description: 'Inventory ID of the coffee you roasted (not a catalog ID)',
958
+ requiredInFlagMode: true,
959
+ },
960
+ {
961
+ flags: '--batch-name <name>',
962
+ description: "Name for this roast batch; defaults to the coffee name plus today's date",
963
+ },
964
+ { flags: '--oz-in <oz>', description: 'Green coffee weight going in, in ounces' },
965
+ { flags: '--oz-out <oz>', description: 'Roasted coffee weight coming out, in ounces' },
966
+ {
967
+ flags: '--roast-date <YYYY-MM-DD>',
968
+ description: 'Date of the roast; defaults to today',
969
+ },
970
+ { flags: '--notes <text>', description: 'Free-text notes about the roast' },
971
+ {
972
+ flags: '--targets <text>',
973
+ description: 'What you were aiming for, such as "FC at 390F, 18% development"',
974
+ },
975
+ {
976
+ flags: '--roaster-type <text>',
977
+ description: 'Roaster model or type, such as Aillio Bullet',
978
+ },
979
+ {
980
+ flags: '--form',
981
+ description: 'Prompt for an inventory coffee, batch name, weight in, notes, and targets; the roast date is set to today and targets are saved in the notes',
982
+ },
699
983
  ],
700
984
  },
701
985
  {
@@ -708,16 +992,19 @@ const commandGroups = [
708
992
  {
709
993
  name: 'roast_id',
710
994
  cliToken: 'id',
711
- description: 'roast_data.roast_id',
995
+ description: 'Roast ID',
712
996
  required: true,
713
997
  idType: 'roast_id',
714
998
  },
715
999
  ],
716
1000
  options: [
717
- { flags: '--notes <text>' },
718
- { flags: '--oz-out <oz>' },
719
- { flags: '--batch-name <name>' },
720
- { flags: '--targets <text>' },
1001
+ { flags: '--notes <text>', description: 'Replace the roast notes' },
1002
+ {
1003
+ flags: '--oz-out <oz>',
1004
+ description: 'New roasted weight, in ounces; weight loss is recalculated when the green weight is known',
1005
+ },
1006
+ { flags: '--batch-name <name>', description: 'New batch name' },
1007
+ { flags: '--targets <text>', description: 'Replace the roast targets' },
721
1008
  ],
722
1009
  },
723
1010
  {
@@ -729,12 +1016,17 @@ const commandGroups = [
729
1016
  {
730
1017
  name: 'roast_id',
731
1018
  cliToken: 'id',
732
- description: 'roast_data.roast_id',
1019
+ description: 'Roast ID',
733
1020
  required: true,
734
1021
  idType: 'roast_id',
735
1022
  },
736
1023
  ],
737
- options: [{ flags: '--yes' }],
1024
+ options: [
1025
+ {
1026
+ flags: '--yes',
1027
+ description: 'Delete without asking for confirmation; needed in scripts and agents',
1028
+ },
1029
+ ],
738
1030
  },
739
1031
  {
740
1032
  name: 'import',
@@ -743,12 +1035,28 @@ const commandGroups = [
743
1035
  sdkMethods: ['roasts.import'],
744
1036
  arguments: [{ name: 'file', description: 'Path to Artisan .alog file', required: false }],
745
1037
  options: [
746
- { flags: '--coffee-id <id>', requiredInFlagMode: true },
747
- { flags: '--batch-name <name>' },
748
- { flags: '--oz-in <oz>' },
749
- { flags: '--roast-notes <text>' },
750
- { flags: '--roast-targets <text>' },
751
- { flags: '--form' },
1038
+ {
1039
+ flags: '--coffee-id <id>',
1040
+ description: 'Inventory ID of the coffee you roasted (not a catalog ID)',
1041
+ requiredInFlagMode: true,
1042
+ },
1043
+ {
1044
+ flags: '--batch-name <name>',
1045
+ description: 'Name for this roast batch; defaults to the coffee name plus the roast date',
1046
+ },
1047
+ {
1048
+ flags: '--oz-in <oz>',
1049
+ description: 'Green coffee weight going in, in ounces; overrides the weight read from the .alog file',
1050
+ },
1051
+ { flags: '--roast-notes <text>', description: 'Notes to save with the imported roast' },
1052
+ {
1053
+ flags: '--roast-targets <text>',
1054
+ description: 'What you were aiming for, saved with the imported roast',
1055
+ },
1056
+ {
1057
+ flags: '--form',
1058
+ description: 'Pick the file and inventory item interactively',
1059
+ },
752
1060
  ],
753
1061
  },
754
1062
  {
@@ -758,20 +1066,48 @@ const commandGroups = [
758
1066
  sdkMethods: ['roasts.import', 'inventory.list', 'roasts.classify'],
759
1067
  arguments: [{ name: 'directory', description: 'Directory to watch', required: false }],
760
1068
  options: [
761
- { flags: '--coffee-id <inventory_id>' },
762
- { flags: '--batch-prefix <name>' },
763
- { flags: '--prompt-each' },
764
- { flags: '--auto-match' },
765
- { flags: '--commit-mode <batch|individual>', defaultValue: 'batch' },
766
- { flags: '--oz-in <oz>' },
767
- { flags: '--roast-notes <text>' },
768
- { flags: '--roast-targets <text>' },
769
- { flags: '--resume' },
770
- { flags: '--form' },
1069
+ {
1070
+ flags: '--coffee-id <inventory_id>',
1071
+ description: 'Inventory ID to attach every new roast to; required unless you use --auto-match, --resume, or --form',
1072
+ },
1073
+ {
1074
+ flags: '--batch-prefix <name>',
1075
+ description: 'Prefix for batch names, which are numbered like "<prefix> #1"; defaults to the coffee name',
1076
+ },
1077
+ {
1078
+ flags: '--prompt-each',
1079
+ description: 'Ask which inventory item each new file belongs to instead of attaching every file to --coffee-id',
1080
+ },
1081
+ {
1082
+ flags: '--auto-match',
1083
+ description: 'Match each new roast to a stocked inventory item automatically from details in its .alog file; cannot be combined with --coffee-id',
1084
+ },
1085
+ {
1086
+ flags: '--commit-mode <batch|individual>',
1087
+ description: 'batch queues new roasts and saves them together when you stop watching; individual saves each roast as soon as its file appears',
1088
+ defaultValue: 'batch',
1089
+ },
1090
+ {
1091
+ flags: '--oz-in <oz>',
1092
+ description: 'Green coffee weight, in ounces, to record for every watched import',
1093
+ },
1094
+ { flags: '--roast-notes <text>', description: 'Notes to save with every watched import' },
1095
+ {
1096
+ flags: '--roast-targets <text>',
1097
+ description: 'Roast targets to save with every watched import',
1098
+ },
1099
+ {
1100
+ flags: '--resume',
1101
+ description: 'Continue the last watch session with its saved directory, settings, and import progress',
1102
+ },
1103
+ {
1104
+ flags: '--form',
1105
+ description: 'Set up the directory, coffee, and commit mode interactively',
1106
+ },
771
1107
  ],
772
1108
  notes: [
773
1109
  '--auto-match is mutually exclusive with --coffee-id.',
774
- '--auto-match classifies roast metadata against stocked inventory through the canonical Parchment POST /v1/roasts/classify SDK operation.',
1110
+ '--auto-match matches each new roast to a stocked inventory item from its metadata.',
775
1111
  '--commit-mode defaults to batch so new roasts are queued until the session ends.',
776
1112
  ],
777
1113
  },
@@ -788,15 +1124,29 @@ const commandGroups = [
788
1124
  auth: 'member',
789
1125
  sdkMethods: ['sales.list'],
790
1126
  options: [
791
- { flags: '--coffee-id <id>' },
792
- { flags: '--date-start <YYYY-MM-DD>' },
793
- { flags: '--date-end <YYYY-MM-DD>' },
794
- { flags: '--buyer <name>' },
795
- { flags: '--limit <n>', defaultValue: 20 },
796
- { flags: '--offset <n>', defaultValue: 0 },
1127
+ {
1128
+ flags: '--coffee-id <id>',
1129
+ description: 'Only sales of this inventory item (an inventory ID)',
1130
+ },
1131
+ { flags: '--date-start <YYYY-MM-DD>', description: 'Only sales on or after this date' },
1132
+ { flags: '--date-end <YYYY-MM-DD>', description: 'Only sales on or before this date' },
1133
+ {
1134
+ flags: '--buyer <name>',
1135
+ description: 'Only sales to buyers whose name contains this text (case-insensitive)',
1136
+ },
1137
+ {
1138
+ flags: '--limit <n>',
1139
+ description: 'Maximum number of sales to return',
1140
+ defaultValue: 20,
1141
+ },
1142
+ {
1143
+ flags: '--offset <n>',
1144
+ description: 'Number of sales to skip when paging',
1145
+ defaultValue: 0,
1146
+ },
797
1147
  ],
798
1148
  notes: [
799
- '--coffee-id filters by green_coffee_inv.id through the canonical sales API.',
1149
+ '--coffee-id filters by inventory ID.',
800
1150
  '--date-start and --date-end accept YYYY-MM-DD and compose into a range.',
801
1151
  '--offset + --limit enables pagination through large result sets.',
802
1152
  ],
@@ -808,18 +1158,37 @@ const commandGroups = [
808
1158
  sdkMethods: ['sales.create', 'roasts.list', 'roasts.get'],
809
1159
  confirmedActionEquivalents: ['record_sale'],
810
1160
  options: [
811
- { flags: '--roast-id <id>' },
812
- { flags: '--coffee-id <id>' },
813
- { flags: '--batch-name <name>' },
814
- { flags: '--oz <amount>', requiredInFlagMode: true },
815
- { flags: '--price <dollars>', requiredInFlagMode: true },
816
- { flags: '--buyer <name>' },
817
- { flags: '--sell-date <YYYY-MM-DD>' },
818
- { flags: '--form' },
1161
+ {
1162
+ flags: '--roast-id <id>',
1163
+ description: 'Roast ID the coffee came from; the CLI looks up its inventory item and batch. Use instead of --coffee-id with --batch-name',
1164
+ },
1165
+ {
1166
+ flags: '--coffee-id <id>',
1167
+ description: 'Inventory ID of the coffee sold; use with --batch-name instead of --roast-id',
1168
+ },
1169
+ {
1170
+ flags: '--batch-name <name>',
1171
+ description: 'Batch name of the roast sold; use with --coffee-id',
1172
+ },
1173
+ {
1174
+ flags: '--oz <amount>',
1175
+ description: 'Roasted coffee sold, in ounces',
1176
+ requiredInFlagMode: true,
1177
+ },
1178
+ {
1179
+ flags: '--price <dollars>',
1180
+ description: 'Total sale price, in US dollars (not per ounce)',
1181
+ requiredInFlagMode: true,
1182
+ },
1183
+ { flags: '--buyer <name>', description: 'Buyer name or identifier' },
1184
+ { flags: '--sell-date <YYYY-MM-DD>', description: 'Date of the sale; defaults to today' },
1185
+ {
1186
+ flags: '--form',
1187
+ description: 'Prompt for a roast, ounces sold, sale price, and buyer; the sale date is set to today',
1188
+ },
819
1189
  ],
820
1190
  notes: [
821
1191
  'Selector modes: --roast-id resolves its inventory + batch, or pass --coffee-id + --batch-name directly.',
822
- 'Creates use the canonical sales API with an idempotency key; selectors resolve through canonical roast endpoints.',
823
1192
  'Use exactly one selector mode.',
824
1193
  'Sales retain inventory + batch, not roast ID; duplicate batch names on one inventory item are rejected.',
825
1194
  ],
@@ -833,16 +1202,16 @@ const commandGroups = [
833
1202
  {
834
1203
  name: 'sale_id',
835
1204
  cliToken: 'id',
836
- description: 'coffee_sales row id',
1205
+ description: 'Sale ID',
837
1206
  required: true,
838
1207
  idType: 'sale_id',
839
1208
  },
840
1209
  ],
841
1210
  options: [
842
- { flags: '--oz <amount>' },
843
- { flags: '--price <dollars>' },
844
- { flags: '--buyer <name>' },
845
- { flags: '--sell-date <YYYY-MM-DD>' },
1211
+ { flags: '--oz <amount>', description: 'New amount sold, in ounces' },
1212
+ { flags: '--price <dollars>', description: 'New total sale price, in US dollars' },
1213
+ { flags: '--buyer <name>', description: 'New buyer name or identifier' },
1214
+ { flags: '--sell-date <YYYY-MM-DD>', description: 'New sale date' },
846
1215
  ],
847
1216
  },
848
1217
  {
@@ -854,18 +1223,23 @@ const commandGroups = [
854
1223
  {
855
1224
  name: 'sale_id',
856
1225
  cliToken: 'id',
857
- description: 'coffee_sales row id',
1226
+ description: 'Sale ID',
858
1227
  required: true,
859
1228
  idType: 'sale_id',
860
1229
  },
861
1230
  ],
862
- options: [{ flags: '--yes' }],
1231
+ options: [
1232
+ {
1233
+ flags: '--yes',
1234
+ description: 'Delete without asking for confirmation; needed in scripts and agents',
1235
+ },
1236
+ ],
863
1237
  },
864
1238
  ],
865
1239
  },
866
1240
  {
867
1241
  name: 'tasting',
868
- summary: 'View and record tasting notes for a coffee bean',
1242
+ summary: 'Read supplier tasting notes and record your own cupping scores',
869
1243
  auth: 'member',
870
1244
  subcommands: [
871
1245
  {
@@ -877,12 +1251,18 @@ const commandGroups = [
877
1251
  {
878
1252
  name: 'catalog_id',
879
1253
  cliToken: 'bean-id',
880
- description: 'coffee_catalog.catalog_id, not inventory id',
1254
+ description: 'Catalog ID of the coffee, not an inventory ID',
881
1255
  required: true,
882
1256
  idType: 'catalog_id',
883
1257
  },
884
1258
  ],
885
- options: [{ flags: '--filter <user|supplier|both>', defaultValue: 'both' }],
1259
+ options: [
1260
+ {
1261
+ flags: '--filter <user|supplier|both>',
1262
+ description: "Which notes to return: user (your cupping scores), supplier (the supplier's flavor notes), or both",
1263
+ defaultValue: 'both',
1264
+ },
1265
+ ],
886
1266
  },
887
1267
  {
888
1268
  name: 'rate',
@@ -893,27 +1273,64 @@ const commandGroups = [
893
1273
  {
894
1274
  name: 'inventory_id',
895
1275
  cliToken: 'bean-id',
896
- description: 'green_coffee_inv.id, not catalog_id',
1276
+ description: 'Inventory ID of the coffee, not a catalog ID',
897
1277
  required: false,
898
1278
  idType: 'inventory_id',
899
1279
  },
900
1280
  ],
901
1281
  options: [
902
- { flags: '--aroma <1-5>', requiredInFlagMode: true },
903
- { flags: '--body <1-5>', requiredInFlagMode: true },
904
- { flags: '--acidity <1-5>', requiredInFlagMode: true },
905
- { flags: '--sweetness <1-5>', requiredInFlagMode: true },
906
- { flags: '--aftertaste <1-5>', requiredInFlagMode: true },
907
- { flags: '--brew-method <method>' },
908
- { flags: '--notes <text>' },
909
- { flags: '--form' },
1282
+ {
1283
+ flags: '--aroma <1-5>',
1284
+ description: 'Aroma score, a whole number; higher is better',
1285
+ minimum: 1,
1286
+ maximum: 5,
1287
+ requiredInFlagMode: true,
1288
+ },
1289
+ {
1290
+ flags: '--body <1-5>',
1291
+ description: 'Body score, a whole number; higher is better',
1292
+ minimum: 1,
1293
+ maximum: 5,
1294
+ requiredInFlagMode: true,
1295
+ },
1296
+ {
1297
+ flags: '--acidity <1-5>',
1298
+ description: 'Acidity score, a whole number; higher is better',
1299
+ minimum: 1,
1300
+ maximum: 5,
1301
+ requiredInFlagMode: true,
1302
+ },
1303
+ {
1304
+ flags: '--sweetness <1-5>',
1305
+ description: 'Sweetness score, a whole number; higher is better',
1306
+ minimum: 1,
1307
+ maximum: 5,
1308
+ requiredInFlagMode: true,
1309
+ },
1310
+ {
1311
+ flags: '--aftertaste <1-5>',
1312
+ description: 'Aftertaste score, a whole number; higher is better',
1313
+ minimum: 1,
1314
+ maximum: 5,
1315
+ requiredInFlagMode: true,
1316
+ },
1317
+ {
1318
+ flags: '--brew-method <method>',
1319
+ description: 'How you brewed the coffee, such as pour_over, french_press, or espresso',
1320
+ },
1321
+ { flags: '--notes <text>', description: 'Free-text tasting notes' },
1322
+ {
1323
+ flags: '--form',
1324
+ description: 'Pick the coffee, then enter the five scores and notes interactively',
1325
+ },
910
1326
  ],
1327
+ notes: ['Rating again replaces the earlier scores for that inventory item.'],
911
1328
  },
912
1329
  ],
913
1330
  },
914
1331
  {
915
1332
  name: 'config',
916
- summary: 'Manage purvey CLI settings',
1333
+ summary: 'Manage local purvey CLI settings',
917
1334
  auth: 'none',
918
1335
  subcommands: [
919
1336
  {
@@ -969,13 +1386,22 @@ const commandGroups = [
969
1386
  },
970
1387
  {
971
1388
  name: 'context',
972
- summary: 'Emit dense human-readable operator reference or manifest-parity JSON',
1389
+ summary: 'Print a dense human-readable CLI reference, or the manifest as JSON',
973
1390
  auth: 'none',
974
1391
  command: {
975
1392
  name: 'context',
976
- summary: 'Emit dense human-readable operator reference or manifest-parity JSON',
1393
+ summary: 'Print a dense human-readable CLI reference, or the manifest as JSON',
977
1394
  auth: 'none',
978
- options: [{ flags: '--json' }, { flags: '--pretty' }],
1395
+ options: [
1396
+ {
1397
+ flags: '--json',
1398
+ description: 'Print the machine-readable manifest as compact JSON instead of the text reference',
1399
+ },
1400
+ {
1401
+ flags: '--pretty',
1402
+ description: 'Print the machine-readable manifest as indented JSON instead of the text reference',
1403
+ },
1404
+ ],
979
1405
  notes: [
980
1406
  'Use `purvey context` for dense human-readable operator reference text.',
981
1407
  'Prefer `purvey manifest` for the stable machine-readable CLI contract.',
@@ -987,13 +1413,16 @@ const commandGroups = [
987
1413
  },
988
1414
  {
989
1415
  name: 'manifest',
990
- summary: 'Emit the preferred stable machine-readable CLI manifest contract',
1416
+ summary: 'Print the stable machine-readable CLI contract as JSON',
991
1417
  auth: 'none',
992
1418
  command: {
993
1419
  name: 'manifest',
994
- summary: 'Emit the preferred stable machine-readable CLI manifest contract',
1420
+ summary: 'Print the stable machine-readable CLI contract as JSON',
995
1421
  auth: 'none',
996
- options: [{ flags: '--json' }, { flags: '--pretty' }],
1422
+ options: [
1423
+ { flags: '--json', description: 'Print the manifest as compact JSON, the default' },
1424
+ { flags: '--pretty', description: 'Print the manifest as indented JSON' },
1425
+ ],
997
1426
  notes: [
998
1427
  '`purvey manifest` is the preferred machine-readable entrypoint and emits compact JSON by default.',
999
1428
  '`purvey context --json` remains available for compatibility with existing context-based callers.',
@@ -1014,11 +1443,10 @@ const commandGroups = [
1014
1443
  options: [
1015
1444
  {
1016
1445
  flags: '--file <file>',
1017
- description: 'Skill file to print: SKILL.md, workflows.md, or all',
1446
+ description: 'Skill file to print: SKILL.md, workflows.md, or all; all needs --json or --pretty',
1018
1447
  defaultValue: 'SKILL.md',
1019
1448
  notes: [
1020
1449
  'The skill is a folder: SKILL.md, which agents load when the skill triggers, and workflows.md, the step-by-step workflows SKILL.md points to',
1021
- 'all needs --json or --pretty',
1022
1450
  ],
1023
1451
  },
1024
1452
  {
@@ -1045,9 +1473,8 @@ const commandGroups = [
1045
1473
  options: [
1046
1474
  {
1047
1475
  flags: '--target <target>',
1048
- description: 'claude, agents, or agents-md',
1476
+ description: "Required. Where to install: claude (Claude Code), agents (Codex, Cursor, and other Agent Skills clients), or agents-md (this repository's AGENTS.md)",
1049
1477
  notes: [
1050
- 'required',
1051
1478
  'claude: SKILL.md and workflows.md in ~/.claude/skills/purveyors/ (project scope: .claude/skills/purveyors/); use this for Claude Code, which loads skills only from .claude/skills',
1052
1479
  'agents: SKILL.md and workflows.md in ~/.agents/skills/purveyors/, read by Codex, Cursor, and other Agent Skills clients (project scope: .agents/skills/purveyors/); Claude Code does not read .agents/',
1053
1480
  'agents-md: ./AGENTS.md in the current directory; adds or refreshes one marked block and leaves the rest of the file alone. Claude Code reads AGENTS.md only when no CLAUDE.md, .claude/CLAUDE.md, or CLAUDE.local.md exists in the current directory or above it; see --link-claude-md',
@@ -1055,7 +1482,7 @@ const commandGroups = [
1055
1482
  },
1056
1483
  {
1057
1484
  flags: '--scope <scope>',
1058
- description: 'user (home directory) or project (current directory); defaults to user, or project for agents-md',
1485
+ description: 'user (your home directory) or project (the current directory); defaults to user, or project for agents-md',
1059
1486
  },
1060
1487
  {
1061
1488
  flags: '--force',
@@ -1091,7 +1518,7 @@ const commandGroups = [
1091
1518
  },
1092
1519
  {
1093
1520
  name: 'market',
1094
- summary: 'Market Index decision surface: value signals, movement stats, metadata trends, overview, and evidence via the canonical API',
1521
+ summary: 'Market Index value signals, price movement, metadata trends, market overview, and named-lot evidence',
1095
1522
  auth: 'mixed',
1096
1523
  subcommands: [
1097
1524
  {
@@ -1100,28 +1527,48 @@ const commandGroups = [
1100
1527
  auth: 'none',
1101
1528
  sdkMethods: ['market.signals'],
1102
1529
  options: [
1103
- { flags: '--summary' },
1530
+ {
1531
+ flags: '--summary',
1532
+ description: 'Return only signal counts; the one view that works without signing in or Parchment Intelligence',
1533
+ },
1104
1534
  {
1105
1535
  flags: '--type <type>',
1106
- notes: ['repeatable or comma-separated: price_drop|below_market|value_quality'],
1536
+ description: 'Signal types to include: price_drop, below_market, or value_quality; repeat the flag or separate with commas. Without it, all three',
1537
+ },
1538
+ { flags: '--origin <origin>', description: 'Only signals for this origin (exact name)' },
1539
+ {
1540
+ flags: '--process <method>',
1541
+ description: 'Only signals for this process group (exact name), such as washed',
1542
+ },
1543
+ {
1544
+ flags: '--market <retail|wholesale|all>',
1545
+ description: 'Which listings to read: retail, wholesale, or all',
1546
+ defaultValue: 'retail',
1547
+ },
1548
+ {
1549
+ flags: '--min-discount <n>',
1550
+ description: 'Only signals at least this large, as a percent price drop or discount',
1551
+ },
1552
+ {
1553
+ flags: '--min-score <n>',
1554
+ description: 'Only coffees with at least this supplier-stated cupping score',
1555
+ },
1556
+ {
1557
+ flags: '--window <7d|30d>',
1558
+ description: 'Time window for price-movement signals; value_quality signals are not time-windowed and always appear',
1559
+ defaultValue: '30d',
1107
1560
  },
1108
- { flags: '--origin <origin>' },
1109
- { flags: '--process <method>' },
1110
- { flags: '--market <retail|wholesale|all>' },
1111
- { flags: '--min-discount <n>' },
1112
- { flags: '--min-score <n>' },
1113
- { flags: '--window <7d|30d>' },
1114
1561
  {
1115
1562
  flags: '--limit <n>',
1116
- description: `Results per page, ${CLI_NUMERIC_BOUNDS.marketSignalsLimit.minimum}-${CLI_NUMERIC_BOUNDS.marketSignalsLimit.maximum}`,
1563
+ description: 'Number of signals per page',
1564
+ defaultValue: 20,
1117
1565
  minimum: CLI_NUMERIC_BOUNDS.marketSignalsLimit.minimum,
1118
1566
  maximum: CLI_NUMERIC_BOUNDS.marketSignalsLimit.maximum,
1119
1567
  },
1120
1568
  ],
1121
1569
  notes: [
1122
- 'Backed by the canonical API GET /v1/market/signals via @purveyors/sdk against api.purveyors.io.',
1123
- '--summary is the only unauthenticated slice (counts only); any filter requires Parchment Intelligence access, enforced server-side (403 on denial).',
1124
- '--json emits the API response verbatim (§3.3 evidence object, §3.4 enums); no client-side reshaping.',
1570
+ '--summary is the only view that works without credentials (counts only); every filter requires Parchment Intelligence access (exit 3 on denial).',
1571
+ '--json emits the API response unchanged.',
1125
1572
  ],
1126
1573
  examples: [
1127
1574
  'purvey market signals --summary --pretty',
@@ -1134,15 +1581,31 @@ const commandGroups = [
1134
1581
  auth: 'none',
1135
1582
  sdkMethods: ['priceIndex.stats'],
1136
1583
  options: [
1137
- { flags: '--origin <origin>' },
1138
- { flags: '--process <method>' },
1139
- { flags: '--market <retail|wholesale|all>' },
1140
- { flags: '--window <7d|30d>' },
1141
- { flags: '--baseline-weeks <n>' },
1584
+ { flags: '--origin <origin>', description: 'Only this origin (exact name)' },
1585
+ {
1586
+ flags: '--process <method>',
1587
+ description: 'Only this process group (exact name), such as washed',
1588
+ },
1589
+ {
1590
+ flags: '--market <retail|wholesale|all>',
1591
+ description: 'Which listings to measure: retail, wholesale, or all',
1592
+ defaultValue: 'retail',
1593
+ },
1594
+ {
1595
+ flags: '--window <7d|30d>',
1596
+ description: 'Length of the price move being measured',
1597
+ defaultValue: '7d',
1598
+ },
1599
+ {
1600
+ flags: '--baseline-weeks <n>',
1601
+ description: 'Weeks of history the move is compared against to judge how unusual it is',
1602
+ defaultValue: 12,
1603
+ minimum: 8,
1604
+ maximum: 52,
1605
+ },
1142
1606
  ],
1143
1607
  notes: [
1144
- 'Backed by the canonical API GET /v1/price-index/stats via @purveyors/sdk.',
1145
- 'The no-origin/no-process retail slice is public; origin/process/wholesale filters require Intelligence access.',
1608
+ 'The view with no origin or process at market=retail works without credentials; origin, process, and wholesale views require Parchment Intelligence access.',
1146
1609
  ],
1147
1610
  examples: [
1148
1611
  'purvey market stats --pretty',
@@ -1155,17 +1618,28 @@ const commandGroups = [
1155
1618
  auth: 'none',
1156
1619
  sdkMethods: ['market.metadataIndex'],
1157
1620
  options: [
1158
- { flags: '--dimension <process|disclosure|score>' },
1159
- { flags: '--origin <origin>' },
1160
- { flags: '--market <retail|wholesale|all>' },
1161
- { flags: '--grain <week|month>' },
1162
- { flags: '--from <date>' },
1163
- { flags: '--to <date>' },
1621
+ {
1622
+ flags: '--dimension <process|disclosure|score>',
1623
+ description: 'What to trend: process (process mix), disclosure (how much suppliers disclose), or score (Purveyor Score)',
1624
+ defaultValue: 'process',
1625
+ },
1626
+ { flags: '--origin <origin>', description: 'Only this origin (exact name)' },
1627
+ {
1628
+ flags: '--market <retail|wholesale|all>',
1629
+ description: 'Which listings to read: retail, wholesale, or all',
1630
+ defaultValue: 'retail',
1631
+ },
1632
+ {
1633
+ flags: '--grain <week|month>',
1634
+ description: 'Size of each period in the trend',
1635
+ defaultValue: 'month',
1636
+ },
1637
+ { flags: '--from <date>', description: 'First period to include, as YYYY-MM-DD' },
1638
+ { flags: '--to <date>', description: 'Last period to include, as YYYY-MM-DD' },
1164
1639
  ],
1165
1640
  notes: [
1166
- 'Backed by the canonical API GET /v1/market/metadata-index via @purveyors/sdk.',
1167
- 'Public slice: dimension=process, no origin, market=retail, grain=month; anything else requires Intelligence access.',
1168
- 'cultivar and drying dimensions are out of scope for v1 (await taxonomy normalization).',
1641
+ 'Public view: dimension=process, no origin, market=retail, grain=month; anything else requires Parchment Intelligence access.',
1642
+ 'Cultivar and drying dimensions are not available yet.',
1169
1643
  ],
1170
1644
  examples: [
1171
1645
  'purvey market metadata --pretty',
@@ -1178,9 +1652,8 @@ const commandGroups = [
1178
1652
  auth: 'viewer',
1179
1653
  sdkMethods: ['market.overview'],
1180
1654
  notes: [
1181
- 'Backed by the canonical API GET /v1/market/overview via @purveyors/sdk.',
1182
- 'The API rejects anonymous requests; any signed-in session or API key with catalog read access receives the same public evidence.',
1183
- 'Emits the API response verbatim: daily change, coverage, movement, process distribution, and origin price distributions.',
1655
+ 'Anonymous requests are rejected; any signed-in account or API key with catalog read access receives the same public evidence.',
1656
+ 'Emits the API response unchanged: daily change, coverage, movement, process distribution, and origin price distributions.',
1184
1657
  ],
1185
1658
  examples: ['purvey market overview --pretty'],
1186
1659
  },
@@ -1189,41 +1662,57 @@ const commandGroups = [
1189
1662
  summary: 'Named arrivals, delistings, comparable lots, and supplier health and price ranges',
1190
1663
  auth: 'member',
1191
1664
  sdkMethods: ['market.evidence'],
1192
- notes: [
1193
- 'Backed by the canonical API GET /v1/market/evidence via @purveyors/sdk.',
1194
- 'Requires Parchment Intelligence plus catalog read access, enforced server-side (401/403 on denial).',
1195
- ],
1665
+ notes: ['Requires Parchment Intelligence plus catalog read access (exit 3 on denial).'],
1196
1666
  examples: ['purvey market evidence --json'],
1197
1667
  },
1198
1668
  ],
1199
1669
  },
1200
1670
  {
1201
1671
  name: 'price-index',
1202
- summary: 'Parchment Price Index snapshots, matched 30-day comparisons, and chart history via the canonical API',
1672
+ summary: 'Parchment Price Index snapshots, matched 30-day price comparisons, and chart history',
1203
1673
  auth: 'mixed',
1204
1674
  command: {
1205
1675
  name: 'price-index',
1206
- summary: 'Fetch Parchment Price Index aggregate snapshots from the canonical API',
1676
+ summary: 'Fetch Parchment Price Index aggregate snapshots',
1207
1677
  auth: 'member',
1208
1678
  sdkMethods: ['priceIndex.list'],
1209
1679
  options: [
1210
- { flags: '--origin <origin>' },
1211
- { flags: '--process <method>' },
1212
- { flags: '--grade <grade>' },
1213
- { flags: '--from <date>' },
1214
- { flags: '--to <date>' },
1215
- { flags: '--wholesale <true|false>' },
1216
- { flags: '--page <n>' },
1680
+ {
1681
+ flags: '--origin <origin>',
1682
+ description: 'Only this origin (case-insensitive exact name)',
1683
+ },
1684
+ {
1685
+ flags: '--process <method>',
1686
+ description: 'Only this process (case-insensitive exact name), such as washed',
1687
+ },
1688
+ { flags: '--grade <grade>', description: 'Only this grade (case-insensitive exact name)' },
1689
+ {
1690
+ flags: '--from <date>',
1691
+ description: 'Only snapshots on or after this date, as YYYY-MM-DD',
1692
+ },
1693
+ {
1694
+ flags: '--to <date>',
1695
+ description: 'Only snapshots on or before this date, as YYYY-MM-DD',
1696
+ },
1697
+ {
1698
+ flags: '--wholesale <true|false>',
1699
+ description: 'true for wholesale prices only, false for retail only; without it, both',
1700
+ },
1701
+ {
1702
+ flags: '--page <n>',
1703
+ description: 'Page number, starting at 1',
1704
+ defaultValue: 1,
1705
+ },
1217
1706
  {
1218
1707
  flags: '--limit <n>',
1219
- description: `Results per page, ${CLI_NUMERIC_BOUNDS.priceIndexLimit.minimum}-${CLI_NUMERIC_BOUNDS.priceIndexLimit.maximum}`,
1708
+ description: 'Number of snapshots per page',
1709
+ defaultValue: 100,
1220
1710
  minimum: CLI_NUMERIC_BOUNDS.priceIndexLimit.minimum,
1221
1711
  maximum: CLI_NUMERIC_BOUNDS.priceIndexLimit.maximum,
1222
1712
  },
1223
1713
  ],
1224
1714
  notes: [
1225
- 'Backed by the canonical API GET /v1/price-index via @purveyors/sdk against api.purveyors.io.',
1226
- 'Requires a scoped member API key with price-index (PPI) access; the API enforces the entitlement.',
1715
+ 'Requires a scoped member API key with Parchment Price Index access.',
1227
1716
  'PARCHMENT_API_KEY or PURVEYORS_API_KEY overrides the scoped API key stored by `purvey auth login`.',
1228
1717
  '--from and --to accept ISO dates (YYYY-MM-DD); --wholesale accepts "true" or "false".',
1229
1718
  ],
@@ -1239,12 +1728,17 @@ const commandGroups = [
1239
1728
  summary: 'Available exact 30-day matched price comparisons with significance',
1240
1729
  auth: 'member',
1241
1730
  sdkMethods: ['priceIndex.comparisons'],
1242
- options: [{ flags: '--wholesale <true|false|all>' }],
1731
+ options: [
1732
+ {
1733
+ flags: '--wholesale <true|false|all>',
1734
+ description: 'true for wholesale listings, false for retail, or all for both',
1735
+ defaultValue: 'false',
1736
+ },
1737
+ ],
1243
1738
  notes: [
1244
- 'Backed by the canonical API GET /v1/price-index/comparisons via @purveyors/sdk.',
1245
- 'Requires Parchment Intelligence access, enforced server-side.',
1246
- 'The API discovers origins with an exact 30-day matched comparison; an empty comparisons list means none qualify.',
1247
- 'Each comparison carries significance verbatim; classification (quiet|normal|notable|exceptional) is null until eight baseline windows exist, meaning not enough history, never quiet.',
1739
+ 'Requires Parchment Intelligence access.',
1740
+ 'Lists every origin with an exact 30-day matched comparison; an empty comparisons list means none qualify.',
1741
+ 'Each comparison carries significance unchanged; classification (quiet|normal|notable|exceptional) is null until eight baseline windows exist, meaning not enough history, never quiet.',
1248
1742
  ],
1249
1743
  examples: [
1250
1744
  'purvey price-index comparisons --pretty',
@@ -1257,14 +1751,23 @@ const commandGroups = [
1257
1751
  auth: 'member',
1258
1752
  sdkMethods: ['priceIndex.comparison'],
1259
1753
  options: [
1260
- { flags: '--origin <origin>', notes: ['required'] },
1261
- { flags: '--from <date>', notes: ['required; ISO date YYYY-MM-DD'] },
1262
- { flags: '--to <date>', notes: ['required; ISO date within 365 days of --from'] },
1263
- { flags: '--wholesale <true|false>' },
1754
+ { flags: '--origin <origin>', description: 'Required. Origin to compare' },
1755
+ {
1756
+ flags: '--from <date>',
1757
+ description: 'Required. Starting date, as YYYY-MM-DD (UTC)',
1758
+ },
1759
+ {
1760
+ flags: '--to <date>',
1761
+ description: 'Required. Ending date, as YYYY-MM-DD (UTC), within 365 days of --from',
1762
+ },
1763
+ {
1764
+ flags: '--wholesale <true|false>',
1765
+ description: 'true to compare wholesale listings, false for retail',
1766
+ defaultValue: 'false',
1767
+ },
1264
1768
  ],
1265
1769
  notes: [
1266
- 'Backed by the canonical API GET /v1/price-index/comparison via @purveyors/sdk.',
1267
- 'Requires Parchment Intelligence access, enforced server-side.',
1770
+ 'Requires Parchment Intelligence access.',
1268
1771
  'status insufficient_fresh_coverage returns a null changePercent, never zero.',
1269
1772
  ],
1270
1773
  examples: [
@@ -1273,28 +1776,37 @@ const commandGroups = [
1273
1776
  },
1274
1777
  {
1275
1778
  name: 'history',
1276
- summary: 'Tier-one price-index chart history; windows up to 90 days are public',
1779
+ summary: 'Daily price-index history for charts; windows up to 90 days are public',
1277
1780
  auth: 'none',
1278
1781
  sdkMethods: ['priceIndex.history'],
1279
1782
  options: [
1280
1783
  {
1281
1784
  flags: '--window-days <n>',
1282
- description: `Trailing window in days, ${CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.minimum}-${CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.maximum}`,
1785
+ description: 'Number of past days to include; up to 90 works without signing in, longer windows need Parchment Intelligence',
1786
+ defaultValue: 90,
1283
1787
  minimum: CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.minimum,
1284
1788
  maximum: CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.maximum,
1285
1789
  },
1286
- { flags: '--page <n>' },
1790
+ {
1791
+ flags: '--page <n>',
1792
+ description: 'Page number, starting at 1',
1793
+ defaultValue: 1,
1794
+ },
1287
1795
  {
1288
1796
  flags: '--limit <n>',
1289
- description: `Results per page, ${CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.minimum}-${CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.maximum}`,
1797
+ description: 'Number of data points per page',
1798
+ defaultValue: 100,
1290
1799
  minimum: CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.minimum,
1291
1800
  maximum: CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.maximum,
1292
1801
  },
1293
- { flags: '--order <asc|desc>' },
1802
+ {
1803
+ flags: '--order <asc|desc>',
1804
+ description: 'Date order: asc (oldest first) or desc (newest first)',
1805
+ defaultValue: 'asc',
1806
+ },
1294
1807
  ],
1295
1808
  notes: [
1296
- 'Backed by the canonical API GET /v1/price-index/history via @purveyors/sdk.',
1297
- 'Windows up to 90 days (the default) run without credentials; 91-365 days require Parchment Intelligence access, enforced server-side.',
1809
+ 'Windows up to 90 days (the default) run without credentials; 91-365 days require Parchment Intelligence access.',
1298
1810
  ],
1299
1811
  examples: [
1300
1812
  'purvey price-index history --pretty',
@@ -1305,7 +1817,7 @@ const commandGroups = [
1305
1817
  },
1306
1818
  {
1307
1819
  name: 'procurement',
1308
- summary: 'Read saved sourcing briefs and their catalog matches from the canonical API',
1820
+ summary: 'Read your saved sourcing briefs and the catalog coffees that match them',
1309
1821
  auth: 'member',
1310
1822
  subcommands: [
1311
1823
  {
@@ -1313,26 +1825,22 @@ const commandGroups = [
1313
1825
  summary: 'List your saved sourcing briefs',
1314
1826
  auth: 'member',
1315
1827
  sdkMethods: ['procurement.briefs.list'],
1316
- notes: [
1317
- 'Backed by the canonical API GET /v1/procurement/briefs via @purveyors/sdk.',
1318
- 'Brief creation is a write handled by the Phase 2 write build-out (PADR-0016), not this read surface.',
1319
- ],
1828
+ notes: ['The CLI reads saved briefs; it does not create them.'],
1320
1829
  examples: ['purvey procurement list --pretty'],
1321
1830
  },
1322
1831
  {
1323
1832
  name: 'get',
1324
- summary: 'Get a single saved sourcing brief by id',
1833
+ summary: 'Get a single saved sourcing brief by ID',
1325
1834
  auth: 'member',
1326
1835
  sdkMethods: ['procurement.briefs.get'],
1327
1836
  arguments: [
1328
1837
  {
1329
1838
  name: 'brief_id',
1330
1839
  cliToken: 'id',
1331
- description: 'sourcing brief id',
1840
+ description: 'ID of the saved sourcing brief',
1332
1841
  required: true,
1333
1842
  },
1334
1843
  ],
1335
- notes: ['Backed by the canonical API GET /v1/procurement/briefs/{id} via @purveyors/sdk.'],
1336
1844
  examples: ['purvey procurement get <brief-id> --pretty'],
1337
1845
  },
1338
1846
  {
@@ -1344,23 +1852,24 @@ const commandGroups = [
1344
1852
  {
1345
1853
  name: 'brief_id',
1346
1854
  cliToken: 'id',
1347
- description: 'sourcing brief id',
1855
+ description: 'ID of the saved sourcing brief',
1348
1856
  required: true,
1349
1857
  },
1350
1858
  ],
1351
1859
  options: [
1352
- { flags: '--page <n>' },
1860
+ {
1861
+ flags: '--page <n>',
1862
+ description: 'Page number, starting at 1',
1863
+ defaultValue: 1,
1864
+ },
1353
1865
  {
1354
1866
  flags: '--limit <n>',
1355
- description: `Matches per page, ${CLI_NUMERIC_BOUNDS.procurementMatchesLimit.minimum}-${CLI_NUMERIC_BOUNDS.procurementMatchesLimit.maximum}`,
1867
+ description: 'Number of matching coffees per page',
1868
+ defaultValue: 25,
1356
1869
  minimum: CLI_NUMERIC_BOUNDS.procurementMatchesLimit.minimum,
1357
1870
  maximum: CLI_NUMERIC_BOUNDS.procurementMatchesLimit.maximum,
1358
1871
  },
1359
1872
  ],
1360
- notes: [
1361
- 'Backed by the canonical API GET /v1/procurement/briefs/{id}/matches via @purveyors/sdk.',
1362
- '--limit maxes at 100 (default 25); --page is 1-based (default 1).',
1363
- ],
1364
1873
  examples: [
1365
1874
  'purvey procurement matches <brief-id> --pretty',
1366
1875
  'purvey procurement matches <brief-id> --page 2 --limit 50 --json',
@@ -1370,7 +1879,7 @@ const commandGroups = [
1370
1879
  },
1371
1880
  {
1372
1881
  name: 'reference-profile',
1373
- summary: 'Compare, preview, save, and export Studio reference plans through the canonical API',
1882
+ summary: 'Import, compare, adjust, save, and export Studio reference roast plans',
1374
1883
  auth: 'member',
1375
1884
  subcommands: [
1376
1885
  {
@@ -1378,11 +1887,13 @@ const commandGroups = [
1378
1887
  summary: 'List your Studio reference profiles',
1379
1888
  auth: 'member',
1380
1889
  sdkMethods: ['referenceProfiles.list'],
1381
- options: [{ flags: '--include-archived' }],
1382
- notes: [
1383
- 'Backed by GET /v1/reference-profiles via @purveyors/sdk.',
1384
- 'Requires a member credential and Studio access; entitlement is enforced by Parchment.',
1890
+ options: [
1891
+ {
1892
+ flags: '--include-archived',
1893
+ description: 'Also list reference profiles you have archived',
1894
+ },
1385
1895
  ],
1896
+ notes: ['Requires a member credential and Studio access on your account.'],
1386
1897
  examples: ['purvey reference-profile list --pretty'],
1387
1898
  },
1388
1899
  {
@@ -1394,7 +1905,7 @@ const commandGroups = [
1394
1905
  {
1395
1906
  name: 'reference_profile_id',
1396
1907
  cliToken: 'profile-id',
1397
- description: 'owner-scoped reference profile UUID',
1908
+ description: 'Reference profile UUID',
1398
1909
  required: true,
1399
1910
  idType: 'reference_profile_id',
1400
1911
  },
@@ -1404,28 +1915,25 @@ const commandGroups = [
1404
1915
  },
1405
1916
  {
1406
1917
  name: 'chart',
1407
- summary: 'Get the typed Artisan chart for a reference revision',
1918
+ summary: 'Get the Artisan chart for a reference revision',
1408
1919
  auth: 'member',
1409
1920
  sdkMethods: ['referenceProfiles.chart'],
1410
1921
  arguments: [
1411
1922
  {
1412
1923
  name: 'reference_profile_id',
1413
1924
  cliToken: 'profile-id',
1414
- description: 'owner-scoped reference profile UUID',
1925
+ description: 'Reference profile UUID',
1415
1926
  required: true,
1416
1927
  idType: 'reference_profile_id',
1417
1928
  },
1418
1929
  {
1419
1930
  name: 'reference_revision_id',
1420
1931
  cliToken: 'revision-id',
1421
- description: 'immutable revision UUID belonging to the profile',
1932
+ description: 'Revision UUID belonging to the profile',
1422
1933
  required: true,
1423
1934
  idType: 'reference_revision_id',
1424
1935
  },
1425
1936
  ],
1426
- notes: [
1427
- 'Backed by GET /v1/reference-profiles/{id}/revisions/{revisionId}/chart via @purveyors/sdk.',
1428
- ],
1429
1937
  examples: [
1430
1938
  'purvey reference-profile chart 5ea1af6f-234c-43a9-9bf8-5678dd24f854 8d2c41e0-7b9a-4f3e-a6d1-2c9e5f07b3a4 --pretty',
1431
1939
  ],
@@ -1443,13 +1951,16 @@ const commandGroups = [
1443
1951
  },
1444
1952
  ],
1445
1953
  options: [
1446
- { flags: '--title <text>' },
1447
- { flags: '--notes <text>' },
1448
- { flags: '--idempotency-key <key>', description: 'Stable key for safe retries' },
1954
+ { flags: '--title <text>', description: 'Title for the new reference profile' },
1955
+ { flags: '--notes <text>', description: 'Notes about the reference profile' },
1956
+ {
1957
+ flags: '--idempotency-key <key>',
1958
+ description: 'Key that makes retries safe: repeating the upload with the same key does not create a second profile. A new key is generated when omitted',
1959
+ },
1449
1960
  ],
1450
1961
  notes: [
1451
- 'Backed by POST /v1/reference-profiles/imports via @purveyors/sdk; parsing and private retention are server-owned.',
1452
- 'A new idempotency key is generated when omitted; reuse an explicit key to replay the same upload.',
1962
+ 'The uploaded file is parsed on purveyors.io and kept private to your account.',
1963
+ 'Reuse an explicit idempotency key to replay the same upload.',
1453
1964
  'Import creates a reference profile, not executed roast history.',
1454
1965
  ],
1455
1966
  examples: [
@@ -1474,19 +1985,23 @@ const commandGroups = [
1474
1985
  },
1475
1986
  ],
1476
1987
  options: [
1477
- { flags: '--unit <F|C>', defaultValue: 'F' },
1988
+ {
1989
+ flags: '--unit <F|C>',
1990
+ description: 'Temperature unit for the compared series: F (Fahrenheit) or C (Celsius)',
1991
+ defaultValue: 'F',
1992
+ },
1478
1993
  {
1479
1994
  flags: '--target-points <n>',
1480
- description: 'Samples per aligned series, 50-1000',
1995
+ description: 'Number of samples in each aligned temperature series',
1481
1996
  defaultValue: 400,
1482
1997
  minimum: 50,
1483
1998
  maximum: 1000,
1484
1999
  },
1485
2000
  ],
1486
2001
  notes: [
1487
- 'Backed by POST /v1/profile-comparisons via @purveyors/sdk; output is the canonical comparison envelope.',
2002
+ 'Output is the comparison result unchanged.',
1488
2003
  'profile:<uuid> resolves the profile currentRevisionId; roast:<roast-id> resolves data.metadata.revision from roast chart data.',
1489
- 'Parchment aligns both sides at charge; a reference profile is never executed roast history.',
2004
+ 'Both sides are aligned at charge; a reference profile is never executed roast history.',
1490
2005
  ],
1491
2006
  examples: [
1492
2007
  'purvey reference-profile compare roast:123 profile:5ea1af6f-234c-43a9-9bf8-5678dd24f854 --pretty',
@@ -1502,24 +2017,28 @@ const commandGroups = [
1502
2017
  {
1503
2018
  name: 'reference_profile_id',
1504
2019
  cliToken: 'profile-id',
1505
- description: 'owner-scoped reference profile UUID',
2020
+ description: 'Reference profile UUID',
1506
2021
  required: true,
1507
2022
  idType: 'reference_profile_id',
1508
2023
  },
1509
2024
  {
1510
2025
  name: 'reference_revision_id',
1511
2026
  cliToken: 'revision-id',
1512
- description: 'immutable parent revision UUID',
2027
+ description: 'Revision UUID to adjust; it is never changed',
1513
2028
  required: true,
1514
2029
  idType: 'reference_revision_id',
1515
2030
  },
1516
2031
  ],
1517
- options: [{ flags: '--request <file>', requiredInFlagMode: true }],
2032
+ options: [
2033
+ {
2034
+ flags: '--request <file>',
2035
+ description: 'Required. JSON file with a title and up to 12 temperature adjustments; save accepts the same file',
2036
+ },
2037
+ ],
1518
2038
  notes: [
1519
- 'Backed by POST /v1/reference-profiles/{id}/revisions/{revisionId}/preview via @purveyors/sdk.',
1520
2039
  'Request JSON contains title, optional notes, optional userGoal (600 chars), modelRecommendation (800), and userEdits (800) provenance, and changes.temperatureAdjustments; each adjustment has kind, startMilliseconds, endMilliseconds, and delta.',
1521
2040
  'At most 12 adjustments are accepted; intervals must be ordered and each nonzero delta is bounded to -20 through 20.',
1522
- 'Parchment recalculates from the immutable parent; preview never persists or changes the source.',
2041
+ 'The plan is recalculated from the unchanged parent revision; preview never saves anything.',
1523
2042
  ],
1524
2043
  examples: [
1525
2044
  'purvey reference-profile preview 5ea1af6f-234c-43a9-9bf8-5678dd24f854 8d2c41e0-7b9a-4f3e-a6d1-2c9e5f07b3a4 --request changes.json --pretty',
@@ -1535,25 +2054,30 @@ const commandGroups = [
1535
2054
  {
1536
2055
  name: 'reference_profile_id',
1537
2056
  cliToken: 'profile-id',
1538
- description: 'owner-scoped reference profile UUID',
2057
+ description: 'Reference profile UUID',
1539
2058
  required: true,
1540
2059
  idType: 'reference_profile_id',
1541
2060
  },
1542
2061
  {
1543
2062
  name: 'reference_revision_id',
1544
2063
  cliToken: 'revision-id',
1545
- description: 'immutable parent revision UUID',
2064
+ description: 'Revision UUID the new plan is based on; it is never changed',
1546
2065
  required: true,
1547
2066
  idType: 'reference_revision_id',
1548
2067
  },
1549
2068
  ],
1550
2069
  options: [
1551
- { flags: '--request <file>', requiredInFlagMode: true },
1552
- { flags: '--idempotency-key <key>', description: 'Stable key for safe retries' },
2070
+ {
2071
+ flags: '--request <file>',
2072
+ description: 'Required. The same JSON request file you previewed',
2073
+ },
2074
+ {
2075
+ flags: '--idempotency-key <key>',
2076
+ description: 'Key that makes retries safe: repeating the save with the same key does not create a second plan. A new key is generated when omitted',
2077
+ },
1553
2078
  ],
1554
2079
  notes: [
1555
- 'Backed by POST /v1/reference-profiles/{id}/revisions/{revisionId}/generated via @purveyors/sdk.',
1556
- 'A new idempotency key is generated when omitted; reuse an explicit key to replay the same save.',
2080
+ 'Reuse an explicit idempotency key to replay the same save.',
1557
2081
  'Generated references are plans and never create executed roast history.',
1558
2082
  ],
1559
2083
  examples: [
@@ -1569,24 +2093,26 @@ const commandGroups = [
1569
2093
  {
1570
2094
  name: 'reference_profile_id',
1571
2095
  cliToken: 'profile-id',
1572
- description: 'saved generated reference profile UUID',
2096
+ description: 'UUID of a saved generated reference profile',
1573
2097
  required: true,
1574
2098
  idType: 'reference_profile_id',
1575
2099
  },
1576
2100
  {
1577
2101
  name: 'reference_revision_id',
1578
2102
  cliToken: 'revision-id',
1579
- description: 'saved generated revision UUID',
2103
+ description: 'UUID of the saved generated revision',
1580
2104
  required: true,
1581
2105
  idType: 'reference_revision_id',
1582
2106
  },
1583
2107
  ],
1584
2108
  options: [
1585
- { flags: '--output <file>', requiredInFlagMode: true },
1586
- { flags: '--force', description: 'Overwrite the destination file if it exists' },
2109
+ {
2110
+ flags: '--output <file>',
2111
+ description: 'Required. Path to write the .alog file to',
2112
+ },
2113
+ { flags: '--force', description: 'Overwrite the output file if it already exists' },
1587
2114
  ],
1588
2115
  notes: [
1589
- 'Backed by GET /v1/reference-profiles/{id}/revisions/{revisionId}/export via @purveyors/sdk.',
1590
2116
  'Only a saved generated revision can be exported; output is a local file plus a JSON receipt.',
1591
2117
  'The CLI does not claim verified Artisan 4.2 playback compatibility.',
1592
2118
  ],
@@ -1602,7 +2128,7 @@ const workflows = [
1602
2128
  title: 'Catalog to inventory',
1603
2129
  commands: [
1604
2130
  'purvey catalog search --origin "Ethiopia" --stocked --pretty',
1605
- 'purvey inventory add --catalog-id 128 --qty 10 --cost 8.50',
2131
+ 'purvey inventory add --catalog-id 128 --qty 10 --cost 85.00',
1606
2132
  ],
1607
2133
  },
1608
2134
  {
@@ -1758,7 +2284,7 @@ export function getCliManifest() {
1758
2284
  'Use --json to request compact JSON explicitly.',
1759
2285
  'Use --pretty for indented JSON.',
1760
2286
  'Use --csv for array-shaped results that support CSV output.',
1761
- 'Set PURVEYORS_API_KEY or PARCHMENT_API_KEY when intentionally using API-key backed catalog proof reads.',
2287
+ 'PURVEYORS_API_KEY or PARCHMENT_API_KEY, when set, is used instead of the key stored by `purvey auth login`.',
1762
2288
  '`purvey context` prints dense human-readable operator reference text unless --json or --pretty is passed.',
1763
2289
  '`purvey manifest` is the preferred machine-readable contract and always emits it on stdout.',
1764
2290
  '`purvey context --json` stays available for compatibility parity with existing context-based callers.',