@purveyors/cli 0.9.6 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +476 -159
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +4 -3
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/catalog.d.ts.map +1 -1
- package/dist/commands/catalog.js +55 -31
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +61 -12
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +34 -234
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/manifest.d.ts +3 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +32 -0
- package/dist/commands/manifest.js.map +1 -0
- package/dist/commands/roast.d.ts.map +1 -1
- package/dist/commands/roast.js +105 -2
- package/dist/commands/roast.js.map +1 -1
- package/dist/commands/sales.d.ts.map +1 -1
- package/dist/commands/sales.js +87 -40
- package/dist/commands/sales.js.map +1 -1
- package/dist/index.js +6 -105
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -2
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +13 -1
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +153 -10
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/interactive/watch.d.ts +37 -3
- package/dist/lib/interactive/watch.d.ts.map +1 -1
- package/dist/lib/interactive/watch.js +237 -81
- package/dist/lib/interactive/watch.js.map +1 -1
- package/dist/lib/manifest.d.ts +107 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +977 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/output.d.ts +10 -0
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +33 -18
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/roast.d.ts +2 -0
- package/dist/lib/roast.d.ts.map +1 -1
- package/dist/lib/roast.js +5 -2
- package/dist/lib/roast.js.map +1 -1
- package/dist/lib/sales.d.ts +15 -2
- package/dist/lib/sales.d.ts.map +1 -1
- package/dist/lib/sales.js +77 -15
- package/dist/lib/sales.js.map +1 -1
- package/dist/program.d.ts +4 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +135 -0
- package/dist/program.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +7 -1
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
import { EXIT_CODES } from './errors.js';
|
|
2
|
+
const legacyMachineSurfaces = {
|
|
3
|
+
humanReference: 'purvey context',
|
|
4
|
+
shellManifest: 'purvey manifest',
|
|
5
|
+
moduleImport: '@purveyors/cli/manifest',
|
|
6
|
+
notes: [],
|
|
7
|
+
};
|
|
8
|
+
const docs = [
|
|
9
|
+
{ label: 'CLI docs', url: 'https://purveyors.io/docs/cli/overview' },
|
|
10
|
+
{ label: 'API docs', url: 'https://purveyors.io/docs/api/overview' },
|
|
11
|
+
{ label: 'Repository', url: 'https://github.com/reedwhetstone/purveyors-cli' },
|
|
12
|
+
{ label: 'npm package', url: 'https://www.npmjs.com/package/@purveyors/cli' },
|
|
13
|
+
];
|
|
14
|
+
const roles = [
|
|
15
|
+
{ role: 'viewer', description: 'valid authenticated session; required for all catalog commands' },
|
|
16
|
+
{ role: 'member', description: 'required for inventory, roast, sales, and tasting commands' },
|
|
17
|
+
];
|
|
18
|
+
const globalOptions = [
|
|
19
|
+
{ flags: '--json', description: 'Output compact JSON explicitly' },
|
|
20
|
+
{ flags: '--pretty', description: 'Pretty-print JSON output with colors' },
|
|
21
|
+
{ flags: '--csv', description: 'Output array results as CSV where supported' },
|
|
22
|
+
{ flags: '--help', description: 'Show help for any command' },
|
|
23
|
+
{ flags: '--version', description: 'Show version number' },
|
|
24
|
+
];
|
|
25
|
+
const outputModes = [
|
|
26
|
+
{ mode: 'json', description: 'compact JSON, suitable for agents and scripts' },
|
|
27
|
+
{ mode: 'pretty', description: 'indented JSON with colors for humans' },
|
|
28
|
+
{ mode: 'csv', description: 'CSV for array-shaped results on supporting commands' },
|
|
29
|
+
];
|
|
30
|
+
const exitCodes = [
|
|
31
|
+
{ exitCode: EXIT_CODES.OK, code: 'OK', description: 'success' },
|
|
32
|
+
{
|
|
33
|
+
exitCode: EXIT_CODES.GENERAL_ERROR,
|
|
34
|
+
code: 'GENERAL_ERROR',
|
|
35
|
+
description: 'unexpected or unclassified error',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
exitCode: EXIT_CODES.INVALID_ARGUMENT,
|
|
39
|
+
code: 'INVALID_ARGUMENT',
|
|
40
|
+
description: 'invalid argument or bad input',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
exitCode: EXIT_CODES.AUTH_ERROR,
|
|
44
|
+
code: 'AUTH_ERROR',
|
|
45
|
+
description: 'auth error, not logged in, expired session, or wrong role',
|
|
46
|
+
},
|
|
47
|
+
{ exitCode: EXIT_CODES.NOT_FOUND, code: 'NOT_FOUND', description: 'resource not found' },
|
|
48
|
+
{
|
|
49
|
+
exitCode: EXIT_CODES.DEPENDENCY_CONFLICT,
|
|
50
|
+
code: 'DEPENDENCY_CONFLICT',
|
|
51
|
+
description: 'dependency conflict, for example delete without --force',
|
|
52
|
+
},
|
|
53
|
+
{ exitCode: EXIT_CODES.CONFIG_ERROR, code: 'CONFIG_ERROR', description: 'local config error' },
|
|
54
|
+
];
|
|
55
|
+
const idTypes = [
|
|
56
|
+
{
|
|
57
|
+
name: 'catalog_id',
|
|
58
|
+
source: 'coffee_catalog row',
|
|
59
|
+
usedBy: [
|
|
60
|
+
'catalog get',
|
|
61
|
+
'inventory add --catalog-id',
|
|
62
|
+
'tasting get <catalog_id>',
|
|
63
|
+
'roast list --catalog-id',
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'inventory_id',
|
|
68
|
+
source: 'green_coffee_inv row',
|
|
69
|
+
usedBy: [
|
|
70
|
+
'inventory get/update/delete',
|
|
71
|
+
'roast --coffee-id',
|
|
72
|
+
'roast list --coffee-id',
|
|
73
|
+
'tasting rate [inventory_id]',
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'roast_id',
|
|
78
|
+
source: 'roast_data row',
|
|
79
|
+
usedBy: ['roast get/delete', 'roast list --roast-id', 'sales --roast-id'],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'sale_id',
|
|
83
|
+
source: 'coffee_sales row',
|
|
84
|
+
usedBy: ['sales update/delete'],
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const commandGroups = [
|
|
88
|
+
{
|
|
89
|
+
name: 'auth',
|
|
90
|
+
summary: 'Manage authentication with purveyors.io',
|
|
91
|
+
auth: 'none',
|
|
92
|
+
subcommands: [
|
|
93
|
+
{
|
|
94
|
+
name: 'login',
|
|
95
|
+
summary: 'Log in to purveyors.io',
|
|
96
|
+
auth: 'none',
|
|
97
|
+
options: [
|
|
98
|
+
{ flags: '--headless', description: 'Print OAuth URL and accept pasted callback URL' },
|
|
99
|
+
],
|
|
100
|
+
examples: ['purvey auth login', 'purvey auth login --headless'],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'status',
|
|
104
|
+
summary: 'Show current login status and role',
|
|
105
|
+
auth: 'none',
|
|
106
|
+
options: [{ flags: '--pretty' }, { flags: '--csv' }],
|
|
107
|
+
examples: [
|
|
108
|
+
'purvey auth status',
|
|
109
|
+
'purvey auth status --pretty',
|
|
110
|
+
'purvey auth status --json',
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'logout',
|
|
115
|
+
summary: 'Clear stored credentials',
|
|
116
|
+
auth: 'none',
|
|
117
|
+
examples: ['purvey auth logout'],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'catalog',
|
|
123
|
+
summary: 'Browse the coffee catalog',
|
|
124
|
+
auth: 'viewer',
|
|
125
|
+
subcommands: [
|
|
126
|
+
{
|
|
127
|
+
name: 'search',
|
|
128
|
+
summary: 'Search coffees by origin, process, price, flavor, and catalog metadata',
|
|
129
|
+
auth: 'viewer',
|
|
130
|
+
options: [
|
|
131
|
+
{ flags: '--origin <origin>' },
|
|
132
|
+
{ flags: '--process <method>' },
|
|
133
|
+
{ flags: '--price-min <n>' },
|
|
134
|
+
{ flags: '--price-max <n>' },
|
|
135
|
+
{ flags: '--flavor <keywords>' },
|
|
136
|
+
{ flags: '--name <text>' },
|
|
137
|
+
{ flags: '--supplier <name>' },
|
|
138
|
+
{ flags: '--ids <n,n,...>' },
|
|
139
|
+
{ flags: '--variety <text>' },
|
|
140
|
+
{ flags: '--drying-method <text>' },
|
|
141
|
+
{ flags: '--stocked-days <n>' },
|
|
142
|
+
{ flags: '--stocked' },
|
|
143
|
+
{ flags: '--sort <field>' },
|
|
144
|
+
{ flags: '--offset <n>', defaultValue: 0 },
|
|
145
|
+
{ flags: '--limit <n>', defaultValue: 10 },
|
|
146
|
+
],
|
|
147
|
+
notes: [
|
|
148
|
+
'All filters are optional. Without flags, returns up to --limit results.',
|
|
149
|
+
'--ids fetches specific catalog items by ID, ignoring --limit and --offset.',
|
|
150
|
+
'--offset + --limit enables pagination through large result sets.',
|
|
151
|
+
],
|
|
152
|
+
examples: [
|
|
153
|
+
'purvey catalog search --origin "Ethiopia" --process "natural" --pretty',
|
|
154
|
+
'purvey catalog search --variety "gesha" --stocked --pretty',
|
|
155
|
+
'purvey catalog search --drying-method "sun" --origin "Ethiopia" --pretty',
|
|
156
|
+
'purvey catalog search --stocked-days 30 --pretty',
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'get',
|
|
161
|
+
summary: 'Get details for a specific coffee by ID',
|
|
162
|
+
auth: 'viewer',
|
|
163
|
+
arguments: [
|
|
164
|
+
{
|
|
165
|
+
name: 'catalog_id',
|
|
166
|
+
cliToken: 'id',
|
|
167
|
+
description: 'coffee_catalog.catalog_id',
|
|
168
|
+
required: true,
|
|
169
|
+
idType: 'catalog_id',
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
examples: ['purvey catalog get 128 --pretty'],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'stats',
|
|
176
|
+
summary: 'Aggregate statistics for the catalog',
|
|
177
|
+
auth: 'viewer',
|
|
178
|
+
examples: ['purvey catalog stats --pretty'],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'similar',
|
|
182
|
+
summary: 'Find similar coffees by catalog ID',
|
|
183
|
+
auth: 'viewer',
|
|
184
|
+
arguments: [
|
|
185
|
+
{
|
|
186
|
+
name: 'catalog_id',
|
|
187
|
+
cliToken: 'id',
|
|
188
|
+
description: 'coffee_catalog.catalog_id',
|
|
189
|
+
required: true,
|
|
190
|
+
idType: 'catalog_id',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
options: [
|
|
194
|
+
{ flags: '--threshold <0-1>', defaultValue: 0.7 },
|
|
195
|
+
{ flags: '--limit <n>', defaultValue: 10 },
|
|
196
|
+
{ flags: '--stocked-only' },
|
|
197
|
+
],
|
|
198
|
+
examples: ['purvey catalog similar 1182 --threshold 0.85 --stocked-only --json'],
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'inventory',
|
|
204
|
+
summary: 'Manage your green coffee inventory',
|
|
205
|
+
auth: 'member',
|
|
206
|
+
subcommands: [
|
|
207
|
+
{
|
|
208
|
+
name: 'list',
|
|
209
|
+
summary: 'List your green coffee inventory with catalog details',
|
|
210
|
+
auth: 'member',
|
|
211
|
+
options: [
|
|
212
|
+
{ flags: '--stocked' },
|
|
213
|
+
{ flags: '--catalog-id <id>' },
|
|
214
|
+
{ flags: '--purchase-date-start <YYYY-MM-DD>' },
|
|
215
|
+
{ flags: '--purchase-date-end <YYYY-MM-DD>' },
|
|
216
|
+
{ flags: '--origin <country>' },
|
|
217
|
+
{ flags: '--limit <n>', defaultValue: 20 },
|
|
218
|
+
{ flags: '--offset <n>', defaultValue: 0 },
|
|
219
|
+
],
|
|
220
|
+
notes: [
|
|
221
|
+
'Returns green_coffee_inv rows joined with catalog details.',
|
|
222
|
+
'The returned id field is the inventory ID, distinct from catalog_id.',
|
|
223
|
+
'--offset + --limit enables pagination through large result sets.',
|
|
224
|
+
],
|
|
225
|
+
examples: [
|
|
226
|
+
'purvey inventory list --stocked --pretty',
|
|
227
|
+
'purvey inventory list --limit 20 --offset 20',
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'get',
|
|
232
|
+
summary: 'Get a single inventory item',
|
|
233
|
+
auth: 'member',
|
|
234
|
+
arguments: [
|
|
235
|
+
{
|
|
236
|
+
name: 'inventory_id',
|
|
237
|
+
cliToken: 'id',
|
|
238
|
+
description: 'green_coffee_inv.id',
|
|
239
|
+
required: true,
|
|
240
|
+
idType: 'inventory_id',
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
examples: ['purvey inventory get 7 --pretty'],
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'add',
|
|
247
|
+
summary: 'Add a bean to your inventory',
|
|
248
|
+
auth: 'member',
|
|
249
|
+
options: [
|
|
250
|
+
{ flags: '--catalog-id <id>', requiredInFlagMode: true },
|
|
251
|
+
{ flags: '--qty <lbs>', requiredInFlagMode: true },
|
|
252
|
+
{ flags: '--cost <dollars>' },
|
|
253
|
+
{ flags: '--tax-ship <dollars>' },
|
|
254
|
+
{ flags: '--notes <text>' },
|
|
255
|
+
{ flags: '--purchase-date <YYYY-MM-DD>' },
|
|
256
|
+
{ flags: '--form' },
|
|
257
|
+
],
|
|
258
|
+
examples: ['purvey inventory add --catalog-id 128 --qty 10 --cost 8.50 --pretty'],
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'update',
|
|
262
|
+
summary: 'Update an inventory item',
|
|
263
|
+
auth: 'member',
|
|
264
|
+
arguments: [
|
|
265
|
+
{
|
|
266
|
+
name: 'inventory_id',
|
|
267
|
+
cliToken: 'id',
|
|
268
|
+
description: 'green_coffee_inv.id',
|
|
269
|
+
required: true,
|
|
270
|
+
idType: 'inventory_id',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
options: [
|
|
274
|
+
{ flags: '--qty <lbs>' },
|
|
275
|
+
{ flags: '--cost <dollars>' },
|
|
276
|
+
{ flags: '--tax-ship <dollars>' },
|
|
277
|
+
{ flags: '--notes <text>' },
|
|
278
|
+
{ flags: '--stocked <true|false>' },
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'delete',
|
|
283
|
+
summary: 'Delete an inventory item',
|
|
284
|
+
auth: 'member',
|
|
285
|
+
arguments: [
|
|
286
|
+
{
|
|
287
|
+
name: 'inventory_id',
|
|
288
|
+
cliToken: 'id',
|
|
289
|
+
description: 'green_coffee_inv.id',
|
|
290
|
+
required: true,
|
|
291
|
+
idType: 'inventory_id',
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
options: [
|
|
295
|
+
{ flags: '--yes' },
|
|
296
|
+
{
|
|
297
|
+
flags: '--force',
|
|
298
|
+
notes: [
|
|
299
|
+
'Cascade delete dependent roast profiles and sales records before deleting the item.',
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'roast',
|
|
308
|
+
summary: 'Browse and manage your roast profiles',
|
|
309
|
+
auth: 'member',
|
|
310
|
+
subcommands: [
|
|
311
|
+
{
|
|
312
|
+
name: 'list',
|
|
313
|
+
summary: 'List your roast profiles, sorted by date',
|
|
314
|
+
auth: 'member',
|
|
315
|
+
options: [
|
|
316
|
+
{ flags: '--coffee-id <id>' },
|
|
317
|
+
{ flags: '--roast-id <id>' },
|
|
318
|
+
{ flags: '--batch-name <text>' },
|
|
319
|
+
{ flags: '--coffee-name <text>' },
|
|
320
|
+
{ flags: '--date-start <YYYY-MM-DD>' },
|
|
321
|
+
{ flags: '--date-end <YYYY-MM-DD>' },
|
|
322
|
+
{ flags: '--stocked' },
|
|
323
|
+
{ flags: '--catalog-id <id>' },
|
|
324
|
+
{ flags: '--limit <n>', defaultValue: 20 },
|
|
325
|
+
{ flags: '--offset <n>', defaultValue: 0 },
|
|
326
|
+
],
|
|
327
|
+
notes: [
|
|
328
|
+
'--coffee-id expects inventory_id, not catalog_id.',
|
|
329
|
+
'--catalog-id filters by coffee_catalog.catalog_id.',
|
|
330
|
+
'--date-start and --date-end accept YYYY-MM-DD format.',
|
|
331
|
+
'--offset + --limit enables pagination through large result sets.',
|
|
332
|
+
],
|
|
333
|
+
examples: ['purvey roast list --date-start 2026-03-01 --date-end 2026-03-31'],
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'get',
|
|
337
|
+
summary: 'Get a single roast profile',
|
|
338
|
+
auth: 'member',
|
|
339
|
+
arguments: [
|
|
340
|
+
{
|
|
341
|
+
name: 'roast_id',
|
|
342
|
+
cliToken: 'id',
|
|
343
|
+
description: 'roast_data.roast_id',
|
|
344
|
+
required: true,
|
|
345
|
+
idType: 'roast_id',
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
options: [{ flags: '--include-temps' }, { flags: '--include-events' }],
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'create',
|
|
352
|
+
summary: 'Create a new roast profile',
|
|
353
|
+
auth: 'member',
|
|
354
|
+
options: [
|
|
355
|
+
{ flags: '--coffee-id <id>', requiredInFlagMode: true },
|
|
356
|
+
{ flags: '--batch-name <name>' },
|
|
357
|
+
{ flags: '--oz-in <oz>' },
|
|
358
|
+
{ flags: '--oz-out <oz>' },
|
|
359
|
+
{ flags: '--roast-date <YYYY-MM-DD>' },
|
|
360
|
+
{ flags: '--notes <text>' },
|
|
361
|
+
{ flags: '--form' },
|
|
362
|
+
],
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: 'update',
|
|
366
|
+
summary: 'Update a roast profile',
|
|
367
|
+
auth: 'member',
|
|
368
|
+
arguments: [
|
|
369
|
+
{
|
|
370
|
+
name: 'roast_id',
|
|
371
|
+
cliToken: 'id',
|
|
372
|
+
description: 'roast_data.roast_id',
|
|
373
|
+
required: true,
|
|
374
|
+
idType: 'roast_id',
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
options: [
|
|
378
|
+
{ flags: '--notes <text>' },
|
|
379
|
+
{ flags: '--oz-out <oz>' },
|
|
380
|
+
{ flags: '--batch-name <name>' },
|
|
381
|
+
{ flags: '--targets <text>' },
|
|
382
|
+
],
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: 'delete',
|
|
386
|
+
summary: 'Delete a roast profile',
|
|
387
|
+
auth: 'member',
|
|
388
|
+
arguments: [
|
|
389
|
+
{
|
|
390
|
+
name: 'roast_id',
|
|
391
|
+
cliToken: 'id',
|
|
392
|
+
description: 'roast_data.roast_id',
|
|
393
|
+
required: true,
|
|
394
|
+
idType: 'roast_id',
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
options: [{ flags: '--yes' }],
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'import',
|
|
401
|
+
summary: 'Import an Artisan .alog roast file',
|
|
402
|
+
auth: 'member',
|
|
403
|
+
arguments: [{ name: 'file', description: 'Path to Artisan .alog file', required: false }],
|
|
404
|
+
options: [
|
|
405
|
+
{ flags: '--coffee-id <id>', requiredInFlagMode: true },
|
|
406
|
+
{ flags: '--batch-name <name>' },
|
|
407
|
+
{ flags: '--oz-in <oz>' },
|
|
408
|
+
{ flags: '--roast-notes <text>' },
|
|
409
|
+
{ flags: '--roast-targets <text>' },
|
|
410
|
+
{ flags: '--form' },
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'watch',
|
|
415
|
+
summary: 'Watch a directory for new Artisan .alog files',
|
|
416
|
+
auth: 'member',
|
|
417
|
+
arguments: [{ name: 'directory', description: 'Directory to watch', required: false }],
|
|
418
|
+
options: [
|
|
419
|
+
{ flags: '--coffee-id <inventory_id>' },
|
|
420
|
+
{ flags: '--batch-prefix <name>' },
|
|
421
|
+
{ flags: '--prompt-each' },
|
|
422
|
+
{ flags: '--auto-match' },
|
|
423
|
+
{ flags: '--commit-mode <batch|individual>', defaultValue: 'batch' },
|
|
424
|
+
{ flags: '--oz-in <oz>' },
|
|
425
|
+
{ flags: '--roast-notes <text>' },
|
|
426
|
+
{ flags: '--roast-targets <text>' },
|
|
427
|
+
{ flags: '--resume' },
|
|
428
|
+
{ flags: '--form' },
|
|
429
|
+
],
|
|
430
|
+
notes: [
|
|
431
|
+
'--auto-match is mutually exclusive with --coffee-id.',
|
|
432
|
+
'--commit-mode defaults to batch so new roasts are queued until the session ends.',
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
name: 'sales',
|
|
439
|
+
summary: 'Record and manage coffee sales',
|
|
440
|
+
auth: 'member',
|
|
441
|
+
subcommands: [
|
|
442
|
+
{
|
|
443
|
+
name: 'list',
|
|
444
|
+
summary: 'List your sales, sorted by sell date',
|
|
445
|
+
auth: 'member',
|
|
446
|
+
options: [
|
|
447
|
+
{ flags: '--roast-id <id>' },
|
|
448
|
+
{ flags: '--date-start <YYYY-MM-DD>' },
|
|
449
|
+
{ flags: '--date-end <YYYY-MM-DD>' },
|
|
450
|
+
{ flags: '--buyer <name>' },
|
|
451
|
+
{ flags: '--limit <n>', defaultValue: 20 },
|
|
452
|
+
{ flags: '--offset <n>', defaultValue: 0 },
|
|
453
|
+
],
|
|
454
|
+
notes: [
|
|
455
|
+
'--date-start and --date-end accept YYYY-MM-DD and compose into a range.',
|
|
456
|
+
'--offset + --limit enables pagination through large result sets.',
|
|
457
|
+
],
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
name: 'record',
|
|
461
|
+
summary: 'Record a new sale',
|
|
462
|
+
auth: 'member',
|
|
463
|
+
options: [
|
|
464
|
+
{ flags: '--roast-id <id>' },
|
|
465
|
+
{ flags: '--coffee-id <id>' },
|
|
466
|
+
{ flags: '--batch-name <name>' },
|
|
467
|
+
{ flags: '--oz <amount>', requiredInFlagMode: true },
|
|
468
|
+
{ flags: '--price <dollars>', requiredInFlagMode: true },
|
|
469
|
+
{ flags: '--buyer <name>' },
|
|
470
|
+
{ flags: '--sell-date <YYYY-MM-DD>' },
|
|
471
|
+
{ flags: '--form' },
|
|
472
|
+
],
|
|
473
|
+
notes: [
|
|
474
|
+
'Selector modes: exact --roast-id, or resolved --coffee-id + --batch-name.',
|
|
475
|
+
'Use exactly one selector mode.',
|
|
476
|
+
'If resolved mode is ambiguous, rerun with --roast-id.',
|
|
477
|
+
],
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: 'update',
|
|
481
|
+
summary: 'Update a sale record',
|
|
482
|
+
auth: 'member',
|
|
483
|
+
arguments: [
|
|
484
|
+
{
|
|
485
|
+
name: 'sale_id',
|
|
486
|
+
cliToken: 'id',
|
|
487
|
+
description: 'coffee_sales row id',
|
|
488
|
+
required: true,
|
|
489
|
+
idType: 'sale_id',
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
options: [
|
|
493
|
+
{ flags: '--oz <amount>' },
|
|
494
|
+
{ flags: '--price <dollars>' },
|
|
495
|
+
{ flags: '--buyer <name>' },
|
|
496
|
+
{ flags: '--sell-date <YYYY-MM-DD>' },
|
|
497
|
+
],
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
name: 'delete',
|
|
501
|
+
summary: 'Delete a sale record',
|
|
502
|
+
auth: 'member',
|
|
503
|
+
arguments: [
|
|
504
|
+
{
|
|
505
|
+
name: 'sale_id',
|
|
506
|
+
cliToken: 'id',
|
|
507
|
+
description: 'coffee_sales row id',
|
|
508
|
+
required: true,
|
|
509
|
+
idType: 'sale_id',
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
options: [{ flags: '--yes' }],
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
name: 'tasting',
|
|
518
|
+
summary: 'View and record tasting notes for a coffee bean',
|
|
519
|
+
auth: 'member',
|
|
520
|
+
subcommands: [
|
|
521
|
+
{
|
|
522
|
+
name: 'get',
|
|
523
|
+
summary: 'Get tasting notes for a coffee',
|
|
524
|
+
auth: 'member',
|
|
525
|
+
arguments: [
|
|
526
|
+
{
|
|
527
|
+
name: 'catalog_id',
|
|
528
|
+
cliToken: 'bean-id',
|
|
529
|
+
description: 'coffee_catalog.catalog_id, not inventory id',
|
|
530
|
+
required: true,
|
|
531
|
+
idType: 'catalog_id',
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
options: [{ flags: '--filter <user|supplier|both>', defaultValue: 'both' }],
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
name: 'rate',
|
|
538
|
+
summary: 'Rate a coffee bean with cupping scores',
|
|
539
|
+
auth: 'member',
|
|
540
|
+
arguments: [
|
|
541
|
+
{
|
|
542
|
+
name: 'inventory_id',
|
|
543
|
+
cliToken: 'bean-id',
|
|
544
|
+
description: 'green_coffee_inv.id, not catalog_id',
|
|
545
|
+
required: false,
|
|
546
|
+
idType: 'inventory_id',
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
options: [
|
|
550
|
+
{ flags: '--aroma <1-5>', requiredInFlagMode: true },
|
|
551
|
+
{ flags: '--body <1-5>', requiredInFlagMode: true },
|
|
552
|
+
{ flags: '--acidity <1-5>', requiredInFlagMode: true },
|
|
553
|
+
{ flags: '--sweetness <1-5>', requiredInFlagMode: true },
|
|
554
|
+
{ flags: '--aftertaste <1-5>', requiredInFlagMode: true },
|
|
555
|
+
{ flags: '--brew-method <method>' },
|
|
556
|
+
{ flags: '--notes <text>' },
|
|
557
|
+
{ flags: '--form' },
|
|
558
|
+
],
|
|
559
|
+
},
|
|
560
|
+
],
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
name: 'config',
|
|
564
|
+
summary: 'Manage purvey CLI settings',
|
|
565
|
+
auth: 'none',
|
|
566
|
+
subcommands: [
|
|
567
|
+
{
|
|
568
|
+
name: 'list',
|
|
569
|
+
summary: 'Show all config values',
|
|
570
|
+
auth: 'none',
|
|
571
|
+
notes: [
|
|
572
|
+
'Interactive terminals print human-readable key = value lines.',
|
|
573
|
+
'Machine mode emits the full config object as JSON.',
|
|
574
|
+
'--csv is not supported on config commands.',
|
|
575
|
+
],
|
|
576
|
+
examples: ['purvey config list', 'purvey config list --json'],
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: 'get',
|
|
580
|
+
summary: 'Get a config value',
|
|
581
|
+
auth: 'none',
|
|
582
|
+
arguments: [{ name: 'key', description: 'Config key', required: true }],
|
|
583
|
+
notes: [
|
|
584
|
+
'Interactive terminals print the raw value with no decorators.',
|
|
585
|
+
'Machine mode emits {"<key>": value|null}.',
|
|
586
|
+
'--csv is not supported on config commands.',
|
|
587
|
+
],
|
|
588
|
+
examples: ['purvey config get form-mode', 'purvey config get form-mode --json'],
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: 'set',
|
|
592
|
+
summary: 'Set a config value',
|
|
593
|
+
auth: 'none',
|
|
594
|
+
arguments: [
|
|
595
|
+
{ name: 'key', description: 'Config key', required: true },
|
|
596
|
+
{ name: 'value', description: 'Config value', required: true },
|
|
597
|
+
],
|
|
598
|
+
notes: [
|
|
599
|
+
'Interactive terminals print a human-readable success line.',
|
|
600
|
+
'Machine mode emits the updated config value as JSON.',
|
|
601
|
+
'--csv is not supported on config commands.',
|
|
602
|
+
],
|
|
603
|
+
examples: ['purvey config set form-mode true', 'purvey config set form-mode true --json'],
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: 'reset',
|
|
607
|
+
summary: 'Reset config to defaults',
|
|
608
|
+
auth: 'none',
|
|
609
|
+
notes: [
|
|
610
|
+
'Interactive terminals print a human-readable success line.',
|
|
611
|
+
'Machine mode emits {}.',
|
|
612
|
+
'--csv is not supported on config commands.',
|
|
613
|
+
],
|
|
614
|
+
examples: ['purvey config reset', 'purvey config reset --json'],
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
name: 'context',
|
|
620
|
+
summary: 'Emit dense human-readable operator reference or manifest-parity JSON',
|
|
621
|
+
auth: 'none',
|
|
622
|
+
command: {
|
|
623
|
+
name: 'context',
|
|
624
|
+
summary: 'Emit dense human-readable operator reference or manifest-parity JSON',
|
|
625
|
+
auth: 'none',
|
|
626
|
+
options: [{ flags: '--json' }, { flags: '--pretty' }],
|
|
627
|
+
notes: [
|
|
628
|
+
'Use `purvey context` for dense human-readable operator reference text.',
|
|
629
|
+
'Prefer `purvey manifest` for the stable machine-readable CLI contract.',
|
|
630
|
+
'Use `purvey context --json` only for compatibility with existing context-based callers.',
|
|
631
|
+
'Use `@purveyors/cli/manifest` for the same contract in-process.',
|
|
632
|
+
],
|
|
633
|
+
examples: ['purvey context', 'purvey context --json', 'purvey context --pretty'],
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
name: 'manifest',
|
|
638
|
+
summary: 'Emit the preferred stable machine-readable CLI manifest contract',
|
|
639
|
+
auth: 'none',
|
|
640
|
+
command: {
|
|
641
|
+
name: 'manifest',
|
|
642
|
+
summary: 'Emit the preferred stable machine-readable CLI manifest contract',
|
|
643
|
+
auth: 'none',
|
|
644
|
+
options: [{ flags: '--json' }, { flags: '--pretty' }],
|
|
645
|
+
notes: [
|
|
646
|
+
'`purvey manifest` is the preferred machine-readable entrypoint and emits compact JSON by default.',
|
|
647
|
+
'`purvey context --json` remains available for compatibility with existing context-based callers.',
|
|
648
|
+
'The same contract is available in-process via `@purveyors/cli/manifest`.',
|
|
649
|
+
],
|
|
650
|
+
examples: ['purvey manifest', 'purvey manifest --pretty'],
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
];
|
|
654
|
+
const workflows = [
|
|
655
|
+
{
|
|
656
|
+
title: 'Catalog to inventory',
|
|
657
|
+
commands: [
|
|
658
|
+
'purvey catalog search --origin "Ethiopia" --stocked --pretty',
|
|
659
|
+
'purvey inventory add --catalog-id 128 --qty 10 --cost 8.50',
|
|
660
|
+
],
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
title: 'Import a roast from Artisan',
|
|
664
|
+
commands: [
|
|
665
|
+
'purvey inventory list --stocked --pretty',
|
|
666
|
+
'purvey roast import ~/artisan/ethiopia.alog --coffee-id 7 --pretty',
|
|
667
|
+
],
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
title: 'Watch a folder for new roasts',
|
|
671
|
+
commands: [
|
|
672
|
+
'purvey roast watch ~/artisan/ --coffee-id 7',
|
|
673
|
+
'purvey roast watch ~/artisan/ --auto-match',
|
|
674
|
+
'purvey roast watch --resume',
|
|
675
|
+
],
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
title: 'Rate coffee and record a sale',
|
|
679
|
+
commands: [
|
|
680
|
+
'purvey tasting rate 7 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4',
|
|
681
|
+
'purvey sales record --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz 12 --price 22.00 --buyer "Jane Smith"',
|
|
682
|
+
],
|
|
683
|
+
},
|
|
684
|
+
];
|
|
685
|
+
const errorPatterns = [
|
|
686
|
+
{
|
|
687
|
+
title: 'Not logged in',
|
|
688
|
+
exitCodes: [EXIT_CODES.AUTH_ERROR],
|
|
689
|
+
guidance: [
|
|
690
|
+
'Run `purvey auth login` or `purvey auth login --headless`.',
|
|
691
|
+
'Run `purvey auth status` to confirm role after login.',
|
|
692
|
+
],
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
title: 'Wrong ID type',
|
|
696
|
+
exitCodes: [EXIT_CODES.INVALID_ARGUMENT, EXIT_CODES.NOT_FOUND],
|
|
697
|
+
guidance: [
|
|
698
|
+
'Verify whether the command wants catalog_id, inventory_id, roast_id, or sale_id.',
|
|
699
|
+
'See the ID MAP section.',
|
|
700
|
+
],
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
title: 'Missing required args in write commands',
|
|
704
|
+
exitCodes: [EXIT_CODES.INVALID_ARGUMENT],
|
|
705
|
+
guidance: ['Pass the required flags or use --form.'],
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
title: 'Parser mistakes like unknown options or commands',
|
|
709
|
+
exitCodes: [EXIT_CODES.INVALID_ARGUMENT],
|
|
710
|
+
guidance: [
|
|
711
|
+
'Unknown options, unknown commands, and missing required arguments use the same JSON error envelope contract in machine mode.',
|
|
712
|
+
'In an interactive terminal with no explicit output flag, those parser failures stay human-readable.',
|
|
713
|
+
],
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
title: 'Dependency conflict on delete',
|
|
717
|
+
exitCodes: [EXIT_CODES.DEPENDENCY_CONFLICT],
|
|
718
|
+
guidance: ['Add `--force` to cascade delete dependent roast profiles and sales records.'],
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
title: 'Mutually exclusive watch flags',
|
|
722
|
+
exitCodes: [EXIT_CODES.INVALID_ARGUMENT],
|
|
723
|
+
guidance: ['`roast watch` forbids using --auto-match together with --coffee-id.'],
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
title: 'Pagination only returning the first 20 results',
|
|
727
|
+
exitCodes: [],
|
|
728
|
+
guidance: [
|
|
729
|
+
'All list commands default to --limit 20.',
|
|
730
|
+
'Use --offset to page, for example `--limit 20 --offset 40` returns items 41-60.',
|
|
731
|
+
],
|
|
732
|
+
},
|
|
733
|
+
];
|
|
734
|
+
export function getCliManifest() {
|
|
735
|
+
return {
|
|
736
|
+
schemaVersion: '1',
|
|
737
|
+
binary: 'purvey',
|
|
738
|
+
packageName: '@purveyors/cli',
|
|
739
|
+
description: 'The official CLI for purveyors.io. Coffee intelligence from your terminal.',
|
|
740
|
+
nodeVersion: 'Node.js 20+',
|
|
741
|
+
importPath: '@purveyors/cli/manifest',
|
|
742
|
+
machineSurfaces: {
|
|
743
|
+
humanReference: 'purvey context',
|
|
744
|
+
shellManifest: 'purvey manifest',
|
|
745
|
+
moduleImport: '@purveyors/cli/manifest',
|
|
746
|
+
notes: [
|
|
747
|
+
'`purvey --help` is the quick-discovery surface for commands and global flags.',
|
|
748
|
+
'`purvey context` is the dense human-readable operator reference surface.',
|
|
749
|
+
'`purvey manifest` is the preferred stable machine-readable contract for shells and automation.',
|
|
750
|
+
'`purvey context --json` emits the same manifest contract for compatibility when callers already use the context entrypoint.',
|
|
751
|
+
'`@purveyors/cli/manifest` exposes the same manifest contract in-process for Node.js and agent consumers.',
|
|
752
|
+
],
|
|
753
|
+
},
|
|
754
|
+
files: {
|
|
755
|
+
credentials: '~/.config/purvey/credentials.json',
|
|
756
|
+
config: '~/.config/purvey/config.json',
|
|
757
|
+
},
|
|
758
|
+
docs,
|
|
759
|
+
roles,
|
|
760
|
+
globalOptions,
|
|
761
|
+
outputModes,
|
|
762
|
+
outputContract: {
|
|
763
|
+
stdout: 'Structured JSON by default for most commands, CSV when explicitly requested on supporting commands, and human-readable reference text for `purvey context` unless JSON is requested.',
|
|
764
|
+
stderr: 'interactive progress and prompts, plus human-readable or structured fatal errors depending on mode',
|
|
765
|
+
notes: [
|
|
766
|
+
'Most commands emit compact JSON to stdout by default.',
|
|
767
|
+
'Use --json to request compact JSON explicitly.',
|
|
768
|
+
'Use --pretty for indented JSON.',
|
|
769
|
+
'Use --csv for array-shaped results that support CSV output.',
|
|
770
|
+
'`purvey context` prints dense human-readable operator reference text unless --json or --pretty is passed.',
|
|
771
|
+
'`purvey manifest` is the preferred machine-readable contract and always emits it on stdout.',
|
|
772
|
+
'`purvey context --json` stays available for compatibility parity with existing context-based callers.',
|
|
773
|
+
'`@purveyors/cli/manifest` exposes the same machine-readable contract for in-process consumers.',
|
|
774
|
+
'`purvey config list/get/set/reset` stay human-readable in an interactive TTY, but emit JSON on stdout in machine mode and reject --csv.',
|
|
775
|
+
'In interactive use, stderr may also carry prompts, spinners, and human-readable status lines.',
|
|
776
|
+
'Parser mistakes like unknown options, unknown commands, and missing required arguments follow the same fatal-error contract as runtime command failures.',
|
|
777
|
+
'Important exception: auth status prints human-readable output in an interactive TTY unless --json, --pretty, or --csv is passed; when piped or redirected it emits structured JSON automatically.',
|
|
778
|
+
],
|
|
779
|
+
structuredErrors: {
|
|
780
|
+
channel: 'stderr',
|
|
781
|
+
guaranteedFields: ['error', 'code', 'exitCode', 'message'],
|
|
782
|
+
optionalFields: ['details'],
|
|
783
|
+
when: 'Emitted for parser and runtime command failures when the invocation is non-interactive or when --json, --pretty, or --csv selects a machine-readable output mode.',
|
|
784
|
+
exception: '`purvey auth status` keeps its status payload on stdout even when unauthenticated; this envelope documents command failures.',
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
exitCodes,
|
|
788
|
+
idTypes,
|
|
789
|
+
commandGroups,
|
|
790
|
+
workflows,
|
|
791
|
+
errorPatterns,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function renderDocs(docsLinks) {
|
|
795
|
+
return ['DOCS', '----', ...docsLinks.map((link) => `${link.label.padEnd(18, ' ')} ${link.url}`)];
|
|
796
|
+
}
|
|
797
|
+
function renderRoles(roleContracts, groups) {
|
|
798
|
+
const unauthenticatedCommands = groups
|
|
799
|
+
.filter((group) => group.auth === 'none')
|
|
800
|
+
.map((group) => group.name)
|
|
801
|
+
.sort();
|
|
802
|
+
const localOnlyCommands = groups
|
|
803
|
+
.filter((group) => group.auth === 'none' && group.name !== 'auth')
|
|
804
|
+
.map((group) => group.name)
|
|
805
|
+
.sort();
|
|
806
|
+
return [
|
|
807
|
+
'ROLES',
|
|
808
|
+
'-----',
|
|
809
|
+
`No pre-existing session required for: ${unauthenticatedCommands.join(', ')}.`,
|
|
810
|
+
`Local-only commands: ${localOnlyCommands.join(', ')}.`,
|
|
811
|
+
'Commands that talk to purveyors.io require authentication, except for the auth commands that establish or inspect a session.',
|
|
812
|
+
...roleContracts.map((role) => `${role.role.padEnd(7, ' ')} ${role.description}`),
|
|
813
|
+
'',
|
|
814
|
+
'Both roles are granted on sign-in through purveyors.io.',
|
|
815
|
+
];
|
|
816
|
+
}
|
|
817
|
+
function renderAuthSection() {
|
|
818
|
+
return [
|
|
819
|
+
'AUTH',
|
|
820
|
+
'----',
|
|
821
|
+
'Interactive login:',
|
|
822
|
+
' purvey auth login',
|
|
823
|
+
'',
|
|
824
|
+
'Headless login:',
|
|
825
|
+
' purvey auth login --headless',
|
|
826
|
+
' 1. CLI prints a Google OAuth URL',
|
|
827
|
+
' 2. User opens it in any browser and signs in',
|
|
828
|
+
' 3. User pastes the full callback URL back into the terminal',
|
|
829
|
+
'',
|
|
830
|
+
'Status:',
|
|
831
|
+
' purvey auth status',
|
|
832
|
+
' purvey auth status --pretty',
|
|
833
|
+
' purvey auth status --json',
|
|
834
|
+
'',
|
|
835
|
+
'Logout:',
|
|
836
|
+
' purvey auth logout',
|
|
837
|
+
];
|
|
838
|
+
}
|
|
839
|
+
function renderOutputContract(manifest) {
|
|
840
|
+
const { structuredErrors } = manifest.outputContract;
|
|
841
|
+
return [
|
|
842
|
+
'OUTPUT CONTRACT',
|
|
843
|
+
'---------------',
|
|
844
|
+
`stdout: ${manifest.outputContract.stdout}`,
|
|
845
|
+
`stderr: ${manifest.outputContract.stderr}`,
|
|
846
|
+
'',
|
|
847
|
+
...manifest.outputContract.notes,
|
|
848
|
+
'',
|
|
849
|
+
`Machine-mode error envelope: ${structuredErrors.channel}`,
|
|
850
|
+
` guaranteed fields: ${structuredErrors.guaranteedFields.join(', ')}`,
|
|
851
|
+
` optional fields: ${structuredErrors.optionalFields.join(', ')}`,
|
|
852
|
+
` when: ${structuredErrors.when}`,
|
|
853
|
+
...(structuredErrors.exception ? [` exception: ${structuredErrors.exception}`] : []),
|
|
854
|
+
];
|
|
855
|
+
}
|
|
856
|
+
function renderExitCodes(codes) {
|
|
857
|
+
return [
|
|
858
|
+
'EXIT CODES',
|
|
859
|
+
'----------',
|
|
860
|
+
'Check $? after a command finishes.',
|
|
861
|
+
...codes.map((code) => `${String(code.exitCode).padEnd(2, ' ')} ${code.description}`),
|
|
862
|
+
];
|
|
863
|
+
}
|
|
864
|
+
function renderIdMap(ids) {
|
|
865
|
+
const lines = ['ID MAP', '------'];
|
|
866
|
+
for (const id of ids) {
|
|
867
|
+
lines.push(`${id.name.padEnd(14, ' ')} ${id.source}; used by: ${id.usedBy.join(', ')}`);
|
|
868
|
+
}
|
|
869
|
+
lines.push('', 'Common ID mistake: tasting get takes catalog_id; tasting rate takes inventory_id.');
|
|
870
|
+
return lines;
|
|
871
|
+
}
|
|
872
|
+
function renderOptions(options = [], indent = ' ') {
|
|
873
|
+
return options.map((option) => `${indent}${option.flags}`);
|
|
874
|
+
}
|
|
875
|
+
function renderArgumentUsage(argument) {
|
|
876
|
+
const token = argument.cliToken ?? argument.name;
|
|
877
|
+
return argument.required ? `<${token}>` : `[${token}]`;
|
|
878
|
+
}
|
|
879
|
+
function renderCommandEntry(command, indent = ' ', displayName = command.name) {
|
|
880
|
+
const args = command.arguments?.map((argument) => renderArgumentUsage(argument)).join(' ') ?? '';
|
|
881
|
+
const suffix = args ? ` ${args}` : '';
|
|
882
|
+
const optionHint = command.options && command.options.length > 0 ? ' [options]' : '';
|
|
883
|
+
const lines = [`${indent}${displayName}${suffix}${optionHint}`];
|
|
884
|
+
lines.push(...renderOptions(command.options, `${indent} `));
|
|
885
|
+
if (command.notes) {
|
|
886
|
+
lines.push(...command.notes.map((note) => `${indent} ${note}`));
|
|
887
|
+
}
|
|
888
|
+
return lines;
|
|
889
|
+
}
|
|
890
|
+
function renderCommandGroups(groups) {
|
|
891
|
+
const lines = ['COMMANDS', '--------'];
|
|
892
|
+
for (const group of groups) {
|
|
893
|
+
if (group.command) {
|
|
894
|
+
lines.push(...renderCommandEntry(group.command, '', group.name));
|
|
895
|
+
lines.push('');
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
lines.push(group.name);
|
|
899
|
+
for (const subcommand of group.subcommands ?? []) {
|
|
900
|
+
lines.push(...renderCommandEntry(subcommand));
|
|
901
|
+
}
|
|
902
|
+
lines.push('');
|
|
903
|
+
}
|
|
904
|
+
return lines;
|
|
905
|
+
}
|
|
906
|
+
function renderConfigKeys() {
|
|
907
|
+
return ['Current config keys:', ' form-mode true|false'];
|
|
908
|
+
}
|
|
909
|
+
function renderWorkflows(workflowsList) {
|
|
910
|
+
const lines = ['WORKFLOWS', '---------'];
|
|
911
|
+
for (const workflow of workflowsList) {
|
|
912
|
+
lines.push(`${workflow.title}:`);
|
|
913
|
+
for (const command of workflow.commands) {
|
|
914
|
+
lines.push(` ${command}`);
|
|
915
|
+
}
|
|
916
|
+
lines.push('');
|
|
917
|
+
}
|
|
918
|
+
lines.pop();
|
|
919
|
+
return lines;
|
|
920
|
+
}
|
|
921
|
+
function renderErrorPatterns(patterns) {
|
|
922
|
+
const lines = ['ERROR PATTERNS', '--------------'];
|
|
923
|
+
for (const pattern of patterns) {
|
|
924
|
+
lines.push(`${pattern.title}${pattern.exitCodes.length > 0 ? ` (exit ${pattern.exitCodes.join('/')})` : ''}:`);
|
|
925
|
+
for (const item of pattern.guidance) {
|
|
926
|
+
lines.push(` ${item}`);
|
|
927
|
+
}
|
|
928
|
+
lines.push('');
|
|
929
|
+
}
|
|
930
|
+
lines.pop();
|
|
931
|
+
return lines;
|
|
932
|
+
}
|
|
933
|
+
function resolveMachineSurfaces(manifest) {
|
|
934
|
+
const machineSurfaces = manifest.machineSurfaces;
|
|
935
|
+
return {
|
|
936
|
+
humanReference: machineSurfaces?.humanReference ?? legacyMachineSurfaces.humanReference,
|
|
937
|
+
shellManifest: machineSurfaces?.shellManifest ?? legacyMachineSurfaces.shellManifest,
|
|
938
|
+
moduleImport: machineSurfaces?.moduleImport ?? legacyMachineSurfaces.moduleImport,
|
|
939
|
+
notes: machineSurfaces?.notes ?? legacyMachineSurfaces.notes,
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
export function renderContextText(manifest = getCliManifest()) {
|
|
943
|
+
const machineSurfaces = resolveMachineSurfaces(manifest);
|
|
944
|
+
return [
|
|
945
|
+
'PURVEY CLI - Agent Reference',
|
|
946
|
+
'============================',
|
|
947
|
+
`${manifest.description} ${manifest.nodeVersion}.`,
|
|
948
|
+
`Credentials file: ${manifest.files.credentials}`,
|
|
949
|
+
`Config file: ${manifest.files.config}`,
|
|
950
|
+
`Quick discovery: purvey --help`,
|
|
951
|
+
`Human reference: ${machineSurfaces.humanReference}`,
|
|
952
|
+
`JSON manifest: ${machineSurfaces.shellManifest}`,
|
|
953
|
+
`Module import: ${machineSurfaces.moduleImport}`,
|
|
954
|
+
'',
|
|
955
|
+
...renderDocs(manifest.docs),
|
|
956
|
+
'',
|
|
957
|
+
...renderRoles(manifest.roles, manifest.commandGroups),
|
|
958
|
+
'',
|
|
959
|
+
...renderAuthSection(),
|
|
960
|
+
'',
|
|
961
|
+
...renderOutputContract(manifest),
|
|
962
|
+
'',
|
|
963
|
+
...renderExitCodes(manifest.exitCodes),
|
|
964
|
+
'',
|
|
965
|
+
...renderIdMap(manifest.idTypes),
|
|
966
|
+
'',
|
|
967
|
+
...renderCommandGroups(manifest.commandGroups),
|
|
968
|
+
...renderConfigKeys(),
|
|
969
|
+
'',
|
|
970
|
+
...renderWorkflows(manifest.workflows),
|
|
971
|
+
'',
|
|
972
|
+
...renderErrorPatterns(manifest.errorPatterns),
|
|
973
|
+
]
|
|
974
|
+
.join('\n')
|
|
975
|
+
.trim();
|
|
976
|
+
}
|
|
977
|
+
//# sourceMappingURL=manifest.js.map
|