@purveyors/cli 0.33.3 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +95 -11
  2. package/dist/commands/auth.d.ts.map +1 -1
  3. package/dist/commands/auth.js +21 -0
  4. package/dist/commands/auth.js.map +1 -1
  5. package/dist/commands/catalog.d.ts.map +1 -1
  6. package/dist/commands/catalog.js +40 -13
  7. package/dist/commands/catalog.js.map +1 -1
  8. package/dist/commands/inventory.d.ts.map +1 -1
  9. package/dist/commands/inventory.js +34 -11
  10. package/dist/commands/inventory.js.map +1 -1
  11. package/dist/commands/market.d.ts.map +1 -1
  12. package/dist/commands/market.js +40 -2
  13. package/dist/commands/market.js.map +1 -1
  14. package/dist/commands/price-index.d.ts +4 -2
  15. package/dist/commands/price-index.d.ts.map +1 -1
  16. package/dist/commands/price-index.js +148 -5
  17. package/dist/commands/price-index.js.map +1 -1
  18. package/dist/commands/reference-profile.d.ts.map +1 -1
  19. package/dist/commands/reference-profile.js +47 -4
  20. package/dist/commands/reference-profile.js.map +1 -1
  21. package/dist/commands/roast.d.ts.map +1 -1
  22. package/dist/commands/roast.js +42 -1
  23. package/dist/commands/roast.js.map +1 -1
  24. package/dist/lib/catalog.d.ts +26 -18
  25. package/dist/lib/catalog.d.ts.map +1 -1
  26. package/dist/lib/catalog.js +38 -46
  27. package/dist/lib/catalog.js.map +1 -1
  28. package/dist/lib/inventory.d.ts +8 -1
  29. package/dist/lib/inventory.d.ts.map +1 -1
  30. package/dist/lib/inventory.js +19 -5
  31. package/dist/lib/inventory.js.map +1 -1
  32. package/dist/lib/manifest.d.ts +13 -0
  33. package/dist/lib/manifest.d.ts.map +1 -1
  34. package/dist/lib/manifest.js +286 -18
  35. package/dist/lib/manifest.js.map +1 -1
  36. package/dist/lib/market.d.ts.map +1 -1
  37. package/dist/lib/numeric-contracts.d.ts +8 -0
  38. package/dist/lib/numeric-contracts.d.ts.map +1 -1
  39. package/dist/lib/numeric-contracts.js +2 -0
  40. package/dist/lib/numeric-contracts.js.map +1 -1
  41. package/dist/lib/parchment.d.ts +10 -1
  42. package/dist/lib/parchment.d.ts.map +1 -1
  43. package/dist/lib/parchment.js +11 -1
  44. package/dist/lib/parchment.js.map +1 -1
  45. package/dist/lib/reference-profiles.d.ts +80 -0
  46. package/dist/lib/reference-profiles.d.ts.map +1 -1
  47. package/dist/lib/reference-profiles.js +91 -0
  48. package/dist/lib/reference-profiles.js.map +1 -1
  49. package/dist/lib/roast.d.ts +19 -0
  50. package/dist/lib/roast.d.ts.map +1 -1
  51. package/dist/lib/roast.js +23 -0
  52. package/dist/lib/roast.js.map +1 -1
  53. package/dist/program.d.ts.map +1 -1
  54. package/dist/program.js +10 -0
  55. package/dist/program.js.map +1 -1
  56. package/package.json +2 -2
@@ -84,7 +84,12 @@ const idTypes = [
84
84
  {
85
85
  name: 'roast_id',
86
86
  source: 'roast_data row',
87
- usedBy: ['roast get/delete', 'roast list --roast-id', 'sales record --roast-id'],
87
+ usedBy: [
88
+ 'roast get/chart/delete',
89
+ 'roast list --roast-id',
90
+ 'sales record --roast-id',
91
+ 'reference-profile compare roast:<roast-id>',
92
+ ],
88
93
  },
89
94
  {
90
95
  name: 'sale_id',
@@ -99,6 +104,7 @@ const idTypes = [
99
104
  'reference-profile chart',
100
105
  'reference-profile preview/save',
101
106
  'reference-profile export',
107
+ 'reference-profile compare profile:<uuid>',
102
108
  ],
103
109
  },
104
110
  {
@@ -108,6 +114,7 @@ const idTypes = [
108
114
  'reference-profile chart',
109
115
  'reference-profile preview/save',
110
116
  'reference-profile export',
117
+ 'reference-profile compare revision:<uuid>',
111
118
  ],
112
119
  },
113
120
  ];
@@ -121,6 +128,7 @@ const commandGroups = [
121
128
  name: 'login',
122
129
  summary: 'Log in to purveyors.io',
123
130
  auth: 'none',
131
+ sdkMethods: ['cliAuth.create', 'cliAuth.exchange'],
124
132
  options: [
125
133
  { flags: '--headless', description: 'Print approval URL without opening a browser' },
126
134
  ],
@@ -130,6 +138,7 @@ const commandGroups = [
130
138
  name: 'status',
131
139
  summary: 'Show current login status and role',
132
140
  auth: 'none',
141
+ sdkMethods: ['me'],
133
142
  options: [{ flags: '--pretty' }, { flags: '--csv' }],
134
143
  examples: [
135
144
  'purvey auth status',
@@ -137,6 +146,21 @@ const commandGroups = [
137
146
  'purvey auth status --json',
138
147
  ],
139
148
  },
149
+ {
150
+ name: 'whoami',
151
+ summary: 'Show the canonical identity, plan, scopes, and capabilities for the active credential',
152
+ auth: 'none',
153
+ sdkMethods: ['me'],
154
+ notes: [
155
+ 'Prints the GET /v1/me response unchanged, including capabilities.profileStudio.',
156
+ '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).',
158
+ ],
159
+ examples: [
160
+ 'purvey auth whoami --pretty',
161
+ "purvey auth whoami | jq '.capabilities.profileStudio'",
162
+ ],
163
+ },
140
164
  {
141
165
  name: 'logout',
142
166
  summary: 'Clear stored credentials',
@@ -154,6 +178,7 @@ const commandGroups = [
154
178
  name: 'search',
155
179
  summary: 'Search coffees by origin, process, price, and catalog metadata; structured process filters require member access',
156
180
  auth: 'viewer',
181
+ sdkMethods: ['catalog.list'],
157
182
  options: [
158
183
  { flags: '--origin <origin>' },
159
184
  { flags: '--process <method>' },
@@ -168,6 +193,18 @@ const commandGroups = [
168
193
  { flags: '--ids <n,n,...>' },
169
194
  { flags: '--variety <text>' },
170
195
  { flags: '--stocked-days <n>' },
196
+ {
197
+ flags: '--supplier <name>',
198
+ description: 'Canonical /v1/catalog supplier filter (partial source-name match)',
199
+ },
200
+ {
201
+ flags: '--drying-method <method>',
202
+ description: 'Canonical /v1/catalog dryingMethod filter; Parchment owns matching',
203
+ },
204
+ {
205
+ flags: '--flavor <keywords>',
206
+ description: 'Comma-separated canonical /v1/catalog flavorKeywords; a row matches any keyword',
207
+ },
171
208
  { flags: '--stocked' },
172
209
  { flags: '--sort <field>' },
173
210
  { flags: '--offset <n>', defaultValue: 0 },
@@ -202,6 +239,7 @@ const commandGroups = [
202
239
  'purvey catalog search --process-additive "hops" --processing-confidence-min 0.8 --pretty',
203
240
  'purvey catalog search --variety "gesha" --stocked --pretty',
204
241
  'purvey catalog search --stocked-days 30 --pretty',
242
+ 'purvey catalog search --supplier "Royal" --flavor "blueberry,jasmine" --stocked --pretty',
205
243
  'purvey catalog search --origin "Ethiopia" --include-proof --json',
206
244
  ],
207
245
  },
@@ -209,6 +247,7 @@ const commandGroups = [
209
247
  name: 'get',
210
248
  summary: 'Get details for a specific coffee by ID',
211
249
  auth: 'viewer',
250
+ sdkMethods: ['catalog.list'],
212
251
  arguments: [
213
252
  {
214
253
  name: 'catalog_id',
@@ -236,17 +275,19 @@ const commandGroups = [
236
275
  name: 'stats',
237
276
  summary: 'Aggregate statistics for the catalog',
238
277
  auth: 'viewer',
278
+ sdkMethods: ['catalog.stats'],
239
279
  examples: ['purvey catalog stats --pretty'],
240
280
  },
241
281
  {
242
282
  name: 'facets',
243
- summary: 'List distinct catalog facet values with counts',
283
+ summary: 'List counted catalog facet values for filter discovery',
244
284
  auth: 'viewer',
285
+ sdkMethods: ['catalog.facets'],
245
286
  arguments: [
246
287
  {
247
288
  name: 'field',
248
- description: 'facet field: supplier, country, processing_base_method, fermentation_type, drying_method, grade, wholesale',
249
- required: true,
289
+ description: 'optional facet field: supplier, country, processing_base_method, fermentation_type, drying_method, grade, wholesale',
290
+ required: false,
250
291
  },
251
292
  ],
252
293
  options: [
@@ -254,24 +295,30 @@ const commandGroups = [
254
295
  flags: '--all',
255
296
  description: 'Use all visible catalog rows instead of the default stocked-only scope.',
256
297
  },
257
- { flags: '--limit <n>', defaultValue: 60 },
258
298
  ],
259
299
  notes: [
260
- 'Counts values from catalog rows visible to the current client.',
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.',
261
303
  'Defaults to currently stocked catalog rows; use --all for all visible rows.',
262
- 'Output metadata reports stocked_only/scope, rows_examined, and truncation for deterministic agent use.',
263
304
  ],
264
305
  examples: [
265
306
  'purvey catalog facets supplier --pretty',
266
- 'purvey catalog facets country --limit 25 --json',
307
+ 'purvey catalog facets country --all --json',
308
+ 'purvey catalog facets --pretty',
267
309
  ],
268
310
  },
269
311
  {
270
312
  name: 'rank',
271
313
  summary: 'Rank catalog candidates by deterministic objective',
272
314
  auth: 'viewer',
315
+ sdkMethods: ['catalog.rank'],
273
316
  options: [
274
317
  { flags: '--objective <objective>', defaultValue: 'premium' },
318
+ {
319
+ flags: '--supplier <name>',
320
+ description: 'Canonical /v1/catalog/rank supplier filter',
321
+ },
275
322
  { flags: '--country <country>' },
276
323
  { flags: '--process <method>' },
277
324
  { flags: '--stocked' },
@@ -296,12 +343,14 @@ const commandGroups = [
296
343
  examples: [
297
344
  'purvey catalog rank --objective premium --stocked --pretty',
298
345
  'purvey catalog rank --objective value --country Ethiopia --price-max 12 --json',
346
+ 'purvey catalog rank --objective premium --supplier "Royal Coffee" --limit 5 --json',
299
347
  ],
300
348
  },
301
349
  {
302
350
  name: 'rank-premium',
303
351
  summary: 'Rank premium catalog candidates by Purveyor Score',
304
352
  auth: 'viewer',
353
+ sdkMethods: ['catalog.rankPremium'],
305
354
  options: [
306
355
  { flags: '--origin <origin>' },
307
356
  { flags: '--process <method>' },
@@ -326,6 +375,7 @@ const commandGroups = [
326
375
  name: 'supplier-list',
327
376
  summary: 'List supplier aggregates from catalog rows',
328
377
  auth: 'viewer',
378
+ sdkMethods: ['catalog.suppliers'],
329
379
  options: [
330
380
  { flags: '--country <country>' },
331
381
  { flags: '--stocked' },
@@ -344,6 +394,7 @@ const commandGroups = [
344
394
  name: 'supplier-detail',
345
395
  summary: 'Show aggregate detail for a supplier query',
346
396
  auth: 'viewer',
397
+ sdkMethods: ['catalog.supplierDetail'],
347
398
  arguments: [
348
399
  {
349
400
  name: 'supplier',
@@ -368,6 +419,7 @@ const commandGroups = [
368
419
  name: 'supplier-rank',
369
420
  summary: 'Rank suppliers by average Purveyor Score and stocked coverage',
370
421
  auth: 'viewer',
422
+ sdkMethods: ['catalog.supplierRank'],
371
423
  options: [
372
424
  { flags: '--country <country>' },
373
425
  { flags: '--stocked' },
@@ -395,6 +447,7 @@ const commandGroups = [
395
447
  name: 'similar',
396
448
  summary: 'Fetch beta canonical /v1/catalog/{id}/similar groups for likely same-lot candidates and similar recommendations',
397
449
  auth: 'member',
450
+ sdkMethods: ['catalog.similar'],
398
451
  arguments: [
399
452
  {
400
453
  name: 'catalog_id',
@@ -441,6 +494,7 @@ const commandGroups = [
441
494
  name: 'list',
442
495
  summary: 'List your green coffee inventory with catalog details',
443
496
  auth: 'member',
497
+ sdkMethods: ['inventory.list'],
444
498
  options: [
445
499
  { flags: '--stocked' },
446
500
  { flags: '--catalog-id <id>' },
@@ -464,6 +518,7 @@ const commandGroups = [
464
518
  name: 'get',
465
519
  summary: 'Get a single inventory item',
466
520
  auth: 'member',
521
+ sdkMethods: ['inventory.list'],
467
522
  arguments: [
468
523
  {
469
524
  name: 'inventory_id',
@@ -479,8 +534,17 @@ const commandGroups = [
479
534
  name: 'add',
480
535
  summary: 'Add a bean to your inventory',
481
536
  auth: 'member',
537
+ sdkMethods: ['inventory.create'],
538
+ confirmedActionEquivalents: ['add_bean_to_inventory'],
482
539
  options: [
483
- { flags: '--catalog-id <id>', requiredInFlagMode: true },
540
+ {
541
+ flags: '--catalog-id <id>',
542
+ description: 'Catalog lot to add; exactly one of --catalog-id or --manual-name',
543
+ },
544
+ {
545
+ flags: '--manual-name <name>',
546
+ description: 'Coffee name for a lot that is not in the catalog',
547
+ },
484
548
  { flags: '--qty <lbs>', requiredInFlagMode: true },
485
549
  { flags: '--cost <dollars>' },
486
550
  { flags: '--tax-ship <dollars>' },
@@ -488,12 +552,21 @@ const commandGroups = [
488
552
  { flags: '--purchase-date <YYYY-MM-DD>' },
489
553
  { flags: '--form' },
490
554
  ],
491
- examples: ['purvey inventory add --catalog-id 128 --qty 10 --cost 8.50 --pretty'],
555
+ notes: [
556
+ '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.',
558
+ ],
559
+ examples: [
560
+ 'purvey inventory add --catalog-id 128 --qty 10 --cost 8.50 --pretty',
561
+ 'purvey inventory add --manual-name "Farm-gate Ethiopia lot 7" --qty 12 --cost 96 --pretty',
562
+ ],
492
563
  },
493
564
  {
494
565
  name: 'update',
495
566
  summary: 'Update an inventory item',
496
567
  auth: 'member',
568
+ sdkMethods: ['inventory.update'],
569
+ confirmedActionEquivalents: ['update_bean'],
497
570
  arguments: [
498
571
  {
499
572
  name: 'inventory_id',
@@ -509,12 +582,14 @@ const commandGroups = [
509
582
  { flags: '--tax-ship <dollars>' },
510
583
  { flags: '--notes <text>' },
511
584
  { flags: '--stocked <true|false>' },
585
+ { flags: '--rank <n>', description: 'Owner-assigned integer rank for this lot' },
512
586
  ],
513
587
  },
514
588
  {
515
589
  name: 'delete',
516
590
  summary: 'Delete an inventory item',
517
591
  auth: 'member',
592
+ sdkMethods: ['inventory.delete'],
518
593
  arguments: [
519
594
  {
520
595
  name: 'inventory_id',
@@ -537,6 +612,7 @@ const commandGroups = [
537
612
  name: 'list',
538
613
  summary: 'List your roast profiles, sorted by date',
539
614
  auth: 'member',
615
+ sdkMethods: ['roasts.list'],
540
616
  options: [
541
617
  { flags: '--coffee-id <id>' },
542
618
  { flags: '--roast-id <id>' },
@@ -561,6 +637,7 @@ const commandGroups = [
561
637
  name: 'get',
562
638
  summary: 'Get a single roast profile',
563
639
  auth: 'member',
640
+ sdkMethods: ['roasts.get'],
564
641
  arguments: [
565
642
  {
566
643
  name: 'roast_id',
@@ -572,10 +649,43 @@ const commandGroups = [
572
649
  ],
573
650
  options: [{ flags: '--include-temps' }, { flags: '--include-events' }],
574
651
  },
652
+ {
653
+ name: 'chart',
654
+ summary: 'Fetch the sampled chart model for one roast (series, events, revision)',
655
+ auth: 'member',
656
+ sdkMethods: ['roasts.chartData'],
657
+ arguments: [
658
+ {
659
+ name: 'roast_id',
660
+ cliToken: 'id',
661
+ description: 'roast_data.roast_id',
662
+ required: true,
663
+ idType: 'roast_id',
664
+ },
665
+ ],
666
+ options: [
667
+ {
668
+ flags: '--target-points <n>',
669
+ description: 'Approximate samples per series, 50-1000; Parchment defaults to 400',
670
+ minimum: 50,
671
+ maximum: 1000,
672
+ },
673
+ ],
674
+ notes: [
675
+ "Returns Parchment's canonical chart-data envelope unchanged: sampled series, events, and metadata.",
676
+ 'data.metadata.revision identifies the immutable chart revision used by reference-profile compare.',
677
+ ],
678
+ examples: [
679
+ 'purvey roast chart 123 --pretty',
680
+ "purvey roast chart 123 --target-points 120 | jq '.data.metadata.revision'",
681
+ ],
682
+ },
575
683
  {
576
684
  name: 'create',
577
685
  summary: 'Create a new roast profile',
578
686
  auth: 'member',
687
+ sdkMethods: ['roasts.create'],
688
+ confirmedActionEquivalents: ['create_roast_session'],
579
689
  options: [
580
690
  { flags: '--coffee-id <id>', requiredInFlagMode: true },
581
691
  { flags: '--batch-name <name>' },
@@ -583,6 +693,8 @@ const commandGroups = [
583
693
  { flags: '--oz-out <oz>' },
584
694
  { flags: '--roast-date <YYYY-MM-DD>' },
585
695
  { flags: '--notes <text>' },
696
+ { flags: '--targets <text>' },
697
+ { flags: '--roaster-type <text>' },
586
698
  { flags: '--form' },
587
699
  ],
588
700
  },
@@ -590,6 +702,8 @@ const commandGroups = [
590
702
  name: 'update',
591
703
  summary: 'Update a roast profile',
592
704
  auth: 'member',
705
+ sdkMethods: ['roasts.update'],
706
+ confirmedActionEquivalents: ['update_roast_notes'],
593
707
  arguments: [
594
708
  {
595
709
  name: 'roast_id',
@@ -610,6 +724,7 @@ const commandGroups = [
610
724
  name: 'delete',
611
725
  summary: 'Delete a roast profile',
612
726
  auth: 'member',
727
+ sdkMethods: ['roasts.delete'],
613
728
  arguments: [
614
729
  {
615
730
  name: 'roast_id',
@@ -625,6 +740,7 @@ const commandGroups = [
625
740
  name: 'import',
626
741
  summary: 'Import an Artisan .alog roast file',
627
742
  auth: 'member',
743
+ sdkMethods: ['roasts.import'],
628
744
  arguments: [{ name: 'file', description: 'Path to Artisan .alog file', required: false }],
629
745
  options: [
630
746
  { flags: '--coffee-id <id>', requiredInFlagMode: true },
@@ -639,6 +755,7 @@ const commandGroups = [
639
755
  name: 'watch',
640
756
  summary: 'Watch a directory for new Artisan .alog files',
641
757
  auth: 'member',
758
+ sdkMethods: ['roasts.import', 'inventory.list', 'roasts.classify'],
642
759
  arguments: [{ name: 'directory', description: 'Directory to watch', required: false }],
643
760
  options: [
644
761
  { flags: '--coffee-id <inventory_id>' },
@@ -669,6 +786,7 @@ const commandGroups = [
669
786
  name: 'list',
670
787
  summary: 'List your sales, sorted by sell date',
671
788
  auth: 'member',
789
+ sdkMethods: ['sales.list'],
672
790
  options: [
673
791
  { flags: '--coffee-id <id>' },
674
792
  { flags: '--date-start <YYYY-MM-DD>' },
@@ -687,6 +805,8 @@ const commandGroups = [
687
805
  name: 'record',
688
806
  summary: 'Record a new sale',
689
807
  auth: 'member',
808
+ sdkMethods: ['sales.create', 'roasts.list', 'roasts.get'],
809
+ confirmedActionEquivalents: ['record_sale'],
690
810
  options: [
691
811
  { flags: '--roast-id <id>' },
692
812
  { flags: '--coffee-id <id>' },
@@ -708,6 +828,7 @@ const commandGroups = [
708
828
  name: 'update',
709
829
  summary: 'Update a sale record',
710
830
  auth: 'member',
831
+ sdkMethods: ['sales.update'],
711
832
  arguments: [
712
833
  {
713
834
  name: 'sale_id',
@@ -728,6 +849,7 @@ const commandGroups = [
728
849
  name: 'delete',
729
850
  summary: 'Delete a sale record',
730
851
  auth: 'member',
852
+ sdkMethods: ['sales.delete'],
731
853
  arguments: [
732
854
  {
733
855
  name: 'sale_id',
@@ -750,6 +872,7 @@ const commandGroups = [
750
872
  name: 'get',
751
873
  summary: 'Get tasting notes for a coffee',
752
874
  auth: 'member',
875
+ sdkMethods: ['tasting.get'],
753
876
  arguments: [
754
877
  {
755
878
  name: 'catalog_id',
@@ -765,6 +888,7 @@ const commandGroups = [
765
888
  name: 'rate',
766
889
  summary: 'Rate a coffee bean with cupping scores',
767
890
  auth: 'member',
891
+ sdkMethods: ['tasting.rate'],
768
892
  arguments: [
769
893
  {
770
894
  name: 'inventory_id',
@@ -880,13 +1004,14 @@ const commandGroups = [
880
1004
  },
881
1005
  {
882
1006
  name: 'market',
883
- summary: 'Market Index decision surface: value signals, movement stats, and metadata trends via the canonical API',
1007
+ summary: 'Market Index decision surface: value signals, movement stats, metadata trends, overview, and evidence via the canonical API',
884
1008
  auth: 'mixed',
885
1009
  subcommands: [
886
1010
  {
887
1011
  name: 'signals',
888
1012
  summary: 'Actionable market value signals; unfiltered summary is a public teaser',
889
1013
  auth: 'none',
1014
+ sdkMethods: ['market.signals'],
890
1015
  options: [
891
1016
  { flags: '--summary' },
892
1017
  {
@@ -920,6 +1045,7 @@ const commandGroups = [
920
1045
  name: 'stats',
921
1046
  summary: 'Price movement-significance stats; unfiltered retail slice is public',
922
1047
  auth: 'none',
1048
+ sdkMethods: ['priceIndex.stats'],
923
1049
  options: [
924
1050
  { flags: '--origin <origin>' },
925
1051
  { flags: '--process <method>' },
@@ -940,6 +1066,7 @@ const commandGroups = [
940
1066
  name: 'metadata',
941
1067
  summary: 'Metadata-trend index; process/retail/month slice is public',
942
1068
  auth: 'none',
1069
+ sdkMethods: ['market.metadataIndex'],
943
1070
  options: [
944
1071
  { flags: '--dimension <process|disclosure|score>' },
945
1072
  { flags: '--origin <origin>' },
@@ -958,16 +1085,40 @@ const commandGroups = [
958
1085
  'purvey market metadata --dimension score --origin "Ethiopia" --grain month --json',
959
1086
  ],
960
1087
  },
1088
+ {
1089
+ name: 'overview',
1090
+ summary: 'Aggregate-only green-coffee market overview over the public catalog',
1091
+ auth: 'viewer',
1092
+ sdkMethods: ['market.overview'],
1093
+ notes: [
1094
+ 'Backed by the canonical API GET /v1/market/overview via @purveyors/sdk.',
1095
+ 'The API rejects anonymous requests; any signed-in session or API key with catalog read access receives the same public evidence.',
1096
+ 'Emits the API response verbatim: daily change, coverage, movement, process distribution, and origin price distributions.',
1097
+ ],
1098
+ examples: ['purvey market overview --pretty'],
1099
+ },
1100
+ {
1101
+ name: 'evidence',
1102
+ summary: 'Named arrivals, delistings, comparable lots, and supplier health and price ranges',
1103
+ auth: 'member',
1104
+ sdkMethods: ['market.evidence'],
1105
+ notes: [
1106
+ 'Backed by the canonical API GET /v1/market/evidence via @purveyors/sdk.',
1107
+ 'Requires Parchment Intelligence plus catalog read access, enforced server-side (401/403 on denial).',
1108
+ ],
1109
+ examples: ['purvey market evidence --json'],
1110
+ },
961
1111
  ],
962
1112
  },
963
1113
  {
964
1114
  name: 'price-index',
965
- summary: 'Parchment Price Index aggregate snapshots; requires price-index (PPI) access',
966
- auth: 'member',
1115
+ summary: 'Parchment Price Index snapshots, matched 30-day comparisons, and chart history via the canonical API',
1116
+ auth: 'mixed',
967
1117
  command: {
968
1118
  name: 'price-index',
969
1119
  summary: 'Fetch Parchment Price Index aggregate snapshots from the canonical API',
970
1120
  auth: 'member',
1121
+ sdkMethods: ['priceIndex.list'],
971
1122
  options: [
972
1123
  { flags: '--origin <origin>' },
973
1124
  { flags: '--process <method>' },
@@ -995,6 +1146,75 @@ const commandGroups = [
995
1146
  'purvey price-index --from 2026-01-01 --to 2026-06-30 --pretty',
996
1147
  ],
997
1148
  },
1149
+ subcommands: [
1150
+ {
1151
+ name: 'comparisons',
1152
+ summary: 'Available exact 30-day matched price comparisons with significance',
1153
+ auth: 'member',
1154
+ sdkMethods: ['priceIndex.comparisons'],
1155
+ options: [{ flags: '--wholesale <true|false|all>' }],
1156
+ notes: [
1157
+ 'Backed by the canonical API GET /v1/price-index/comparisons via @purveyors/sdk.',
1158
+ 'Requires Parchment Intelligence access, enforced server-side.',
1159
+ 'The API discovers origins with an exact 30-day matched comparison; an empty comparisons list means none qualify.',
1160
+ 'Each comparison carries significance verbatim; classification (quiet|normal|notable|exceptional) is null until eight baseline windows exist, meaning not enough history, never quiet.',
1161
+ ],
1162
+ examples: [
1163
+ 'purvey price-index comparisons --pretty',
1164
+ 'purvey price-index comparisons --wholesale all --json',
1165
+ ],
1166
+ },
1167
+ {
1168
+ name: 'comparison',
1169
+ summary: 'Matched-listing price comparison for one origin between two exact dates',
1170
+ auth: 'member',
1171
+ sdkMethods: ['priceIndex.comparison'],
1172
+ options: [
1173
+ { flags: '--origin <origin>', notes: ['required'] },
1174
+ { flags: '--from <date>', notes: ['required; ISO date YYYY-MM-DD'] },
1175
+ { flags: '--to <date>', notes: ['required; ISO date within 365 days of --from'] },
1176
+ { flags: '--wholesale <true|false>' },
1177
+ ],
1178
+ notes: [
1179
+ 'Backed by the canonical API GET /v1/price-index/comparison via @purveyors/sdk.',
1180
+ 'Requires Parchment Intelligence access, enforced server-side.',
1181
+ 'status insufficient_fresh_coverage returns a null changePercent, never zero.',
1182
+ ],
1183
+ examples: [
1184
+ 'purvey price-index comparison --origin "Ethiopia" --from 2026-08-31 --to 2026-09-30 --pretty',
1185
+ ],
1186
+ },
1187
+ {
1188
+ name: 'history',
1189
+ summary: 'Tier-one price-index chart history; windows up to 90 days are public',
1190
+ auth: 'none',
1191
+ sdkMethods: ['priceIndex.history'],
1192
+ options: [
1193
+ {
1194
+ flags: '--window-days <n>',
1195
+ description: `Trailing window in days, ${CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.minimum}-${CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.maximum}`,
1196
+ minimum: CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.minimum,
1197
+ maximum: CLI_NUMERIC_BOUNDS.priceIndexHistoryWindowDays.maximum,
1198
+ },
1199
+ { flags: '--page <n>' },
1200
+ {
1201
+ flags: '--limit <n>',
1202
+ description: `Results per page, ${CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.minimum}-${CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.maximum}`,
1203
+ minimum: CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.minimum,
1204
+ maximum: CLI_NUMERIC_BOUNDS.priceIndexHistoryLimit.maximum,
1205
+ },
1206
+ { flags: '--order <asc|desc>' },
1207
+ ],
1208
+ notes: [
1209
+ 'Backed by the canonical API GET /v1/price-index/history via @purveyors/sdk.',
1210
+ 'Windows up to 90 days (the default) run without credentials; 91-365 days require Parchment Intelligence access, enforced server-side.',
1211
+ ],
1212
+ examples: [
1213
+ 'purvey price-index history --pretty',
1214
+ 'purvey price-index history --window-days 365 --limit 500 --json',
1215
+ ],
1216
+ },
1217
+ ],
998
1218
  },
999
1219
  {
1000
1220
  name: 'procurement',
@@ -1005,6 +1225,7 @@ const commandGroups = [
1005
1225
  name: 'list',
1006
1226
  summary: 'List your saved sourcing briefs',
1007
1227
  auth: 'member',
1228
+ sdkMethods: ['procurement.briefs.list'],
1008
1229
  notes: [
1009
1230
  'Backed by the canonical API GET /v1/procurement/briefs via @purveyors/sdk.',
1010
1231
  'Brief creation is a write handled by the Phase 2 write build-out (PADR-0016), not this read surface.',
@@ -1015,6 +1236,7 @@ const commandGroups = [
1015
1236
  name: 'get',
1016
1237
  summary: 'Get a single saved sourcing brief by id',
1017
1238
  auth: 'member',
1239
+ sdkMethods: ['procurement.briefs.get'],
1018
1240
  arguments: [
1019
1241
  {
1020
1242
  name: 'brief_id',
@@ -1030,6 +1252,7 @@ const commandGroups = [
1030
1252
  name: 'matches',
1031
1253
  summary: 'Run a saved brief against the catalog and page through matches',
1032
1254
  auth: 'member',
1255
+ sdkMethods: ['procurement.briefs.matches'],
1033
1256
  arguments: [
1034
1257
  {
1035
1258
  name: 'brief_id',
@@ -1060,13 +1283,14 @@ const commandGroups = [
1060
1283
  },
1061
1284
  {
1062
1285
  name: 'reference-profile',
1063
- summary: 'Preview, save, and export Studio reference plans through the canonical API',
1286
+ summary: 'Compare, preview, save, and export Studio reference plans through the canonical API',
1064
1287
  auth: 'member',
1065
1288
  subcommands: [
1066
1289
  {
1067
1290
  name: 'list',
1068
1291
  summary: 'List your Studio reference profiles',
1069
1292
  auth: 'member',
1293
+ sdkMethods: ['referenceProfiles.list'],
1070
1294
  options: [{ flags: '--include-archived' }],
1071
1295
  notes: [
1072
1296
  'Backed by GET /v1/reference-profiles via @purveyors/sdk.',
@@ -1078,6 +1302,7 @@ const commandGroups = [
1078
1302
  name: 'get',
1079
1303
  summary: 'Get one reference profile and its current revision',
1080
1304
  auth: 'member',
1305
+ sdkMethods: ['referenceProfiles.get'],
1081
1306
  arguments: [
1082
1307
  {
1083
1308
  name: 'reference_profile_id',
@@ -1094,6 +1319,7 @@ const commandGroups = [
1094
1319
  name: 'chart',
1095
1320
  summary: 'Get the typed Artisan chart for a reference revision',
1096
1321
  auth: 'member',
1322
+ sdkMethods: ['referenceProfiles.chart'],
1097
1323
  arguments: [
1098
1324
  {
1099
1325
  name: 'reference_profile_id',
@@ -1121,6 +1347,7 @@ const commandGroups = [
1121
1347
  name: 'import',
1122
1348
  summary: 'Upload an Artisan file as a private Studio reference profile',
1123
1349
  auth: 'member',
1350
+ sdkMethods: ['referenceProfiles.import'],
1124
1351
  arguments: [
1125
1352
  {
1126
1353
  name: 'file',
@@ -1142,10 +1369,48 @@ const commandGroups = [
1142
1369
  'purvey reference-profile import ~/artisan/ethiopia.alog --title "Ethiopia baseline"',
1143
1370
  ],
1144
1371
  },
1372
+ {
1373
+ name: 'compare',
1374
+ summary: 'Compare two immutable roast or reference revisions with measured deltas',
1375
+ auth: 'member',
1376
+ sdkMethods: ['referenceProfiles.compare', 'referenceProfiles.get', 'roasts.chartData'],
1377
+ arguments: [
1378
+ {
1379
+ name: 'left',
1380
+ description: 'selector: revision:<uuid>, profile:<uuid>, roast:<roast-id>, or roast:<roast-id>@<chart-revision>',
1381
+ required: true,
1382
+ },
1383
+ {
1384
+ name: 'right',
1385
+ description: 'selector: revision:<uuid>, profile:<uuid>, roast:<roast-id>, or roast:<roast-id>@<chart-revision>',
1386
+ required: true,
1387
+ },
1388
+ ],
1389
+ options: [
1390
+ { flags: '--unit <F|C>', defaultValue: 'F' },
1391
+ {
1392
+ flags: '--target-points <n>',
1393
+ description: 'Samples per aligned series, 50-1000',
1394
+ defaultValue: 400,
1395
+ minimum: 50,
1396
+ maximum: 1000,
1397
+ },
1398
+ ],
1399
+ notes: [
1400
+ 'Backed by POST /v1/profile-comparisons via @purveyors/sdk; output is the canonical comparison envelope.',
1401
+ 'profile:<uuid> resolves the profile currentRevisionId; roast:<roast-id> resolves data.metadata.revision from roast chart data.',
1402
+ 'Parchment aligns both sides at charge; a reference profile is never executed roast history.',
1403
+ ],
1404
+ examples: [
1405
+ 'purvey reference-profile compare roast:123 profile:5ea1af6f-234c-43a9-9bf8-5678dd24f854 --pretty',
1406
+ 'purvey reference-profile compare roast:123 roast:124 --unit C --json',
1407
+ ],
1408
+ },
1145
1409
  {
1146
1410
  name: 'preview',
1147
1411
  summary: 'Preview bounded temperature adjustments without saving',
1148
1412
  auth: 'member',
1413
+ sdkMethods: ['referenceProfiles.preview'],
1149
1414
  arguments: [
1150
1415
  {
1151
1416
  name: 'reference_profile_id',
@@ -1165,7 +1430,7 @@ const commandGroups = [
1165
1430
  options: [{ flags: '--request <file>', requiredInFlagMode: true }],
1166
1431
  notes: [
1167
1432
  'Backed by POST /v1/reference-profiles/{id}/revisions/{revisionId}/preview via @purveyors/sdk.',
1168
- 'Request JSON contains title, optional notes, and changes.temperatureAdjustments; each adjustment has kind, startMilliseconds, endMilliseconds, and delta.',
1433
+ '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.',
1169
1434
  'At most 12 adjustments are accepted; intervals must be ordered and each nonzero delta is bounded to -20 through 20.',
1170
1435
  'Parchment recalculates from the immutable parent; preview never persists or changes the source.',
1171
1436
  ],
@@ -1177,6 +1442,8 @@ const commandGroups = [
1177
1442
  name: 'save',
1178
1443
  summary: 'Save changes as a new immutable generated reference plan',
1179
1444
  auth: 'member',
1445
+ sdkMethods: ['referenceProfiles.generate'],
1446
+ confirmedActionEquivalents: ['create_generated_reference'],
1180
1447
  arguments: [
1181
1448
  {
1182
1449
  name: 'reference_profile_id',
@@ -1210,6 +1477,7 @@ const commandGroups = [
1210
1477
  name: 'export',
1211
1478
  summary: 'Download a saved generated reference as an unsigned Artisan .alog plan',
1212
1479
  auth: 'member',
1480
+ sdkMethods: ['referenceProfiles.exportGenerated'],
1213
1481
  arguments: [
1214
1482
  {
1215
1483
  name: 'reference_profile_id',
@@ -1510,10 +1778,10 @@ function renderCommandGroups(groups) {
1510
1778
  for (const group of groups) {
1511
1779
  if (group.command) {
1512
1780
  lines.push(...renderCommandEntry(group.command, '', group.name));
1513
- lines.push('');
1514
- continue;
1515
1781
  }
1516
- lines.push(group.name);
1782
+ else {
1783
+ lines.push(group.name);
1784
+ }
1517
1785
  for (const subcommand of group.subcommands ?? []) {
1518
1786
  lines.push(...renderCommandEntry(subcommand));
1519
1787
  }