@purveyors/cli 0.8.2 → 0.9.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.
@@ -1,291 +1,224 @@
1
1
  import { Command } from 'commander';
2
- // ─── Agent Context Reference ──────────────────────────────────────────────────
3
- //
4
- // This text is optimized for token efficiency: dense, structured, no fluff.
5
- // An agent reading this once should be able to use every CLI command correctly.
2
+ // This text is intentionally dense. An agent should be able to read it once,
3
+ // then use the CLI correctly without hunting through help output.
6
4
  const CONTEXT_TEXT = `
7
- PURVEY CLI Agent Reference v0.7
8
- ==================================
9
- CLI for purveyors.io coffee marketplace. Node >=20 required.
10
- Credentials: ~/.config/purvey/credentials.json (auto-refreshed, do not edit)
5
+ PURVEY CLI - Agent Reference
6
+ ============================
7
+ Official CLI for purveyors.io. Node.js 20+.
8
+ Credentials file: ~/.config/purvey/credentials.json
9
+ Config file: ~/.config/purvey/config.json
11
10
 
12
11
  ROLES
13
12
  -----
14
- viewer catalog commands only (no login required for catalog search/get/stats)
15
- member — all commands; required for inventory, roast, sales, tasting
13
+ viewer valid authenticated session; required for catalog commands
14
+ member required for inventory, roast, sales, and tasting commands
16
15
 
17
- AUTHENTICATION
18
- --------------
19
- Browser login (interactive):
16
+ AUTH
17
+ ----
18
+ Interactive login:
20
19
  purvey auth login
21
20
 
22
- Headless login (agents/CI — no browser):
21
+ Headless login:
23
22
  purvey auth login --headless
24
- prints Google OAuth URL
25
- user opens URL, signs in, copies callback URL
26
- paste callback URL back at prompt
27
- Token auto-refreshes; no action needed between sessions.
23
+ 1. CLI prints a Google OAuth URL
24
+ 2. User opens it in any browser and signs in
25
+ 3. User pastes the full callback URL back into the terminal
28
26
 
29
- Check status:
30
- purvey auth status # human-readable in terminal; compact JSON when piped
31
- purvey auth status --pretty # indented/colorized JSON (human reading)
32
- purvey auth status | jq '.email' # compact JSON on stdout
27
+ Status:
28
+ purvey auth status
29
+ purvey auth status --pretty
33
30
 
34
31
  Logout:
35
32
  purvey auth logout
36
33
 
37
- OUTPUT FORMATS
34
+ OUTPUT
35
+ ------
36
+ Most commands emit compact JSON to stdout by default.
37
+ Use --json to request compact JSON explicitly.
38
+ Use --pretty for indented JSON.
39
+ Use --csv for array-shaped results that support CSV output.
40
+ User feedback goes to stderr.
41
+
42
+ Important exception:
43
+ - auth status prints human-readable output in an interactive TTY unless --json, --pretty, or --csv is passed; otherwise it emits structured output
44
+
45
+ ID MAP
46
+ ------
47
+ catalog_id coffee_catalog row; used by catalog get, inventory add --catalog-id, tasting get, roast list --catalog-id
48
+ inventory_id green_coffee_inv row; used by inventory get/update/delete, roast --coffee-id, roast list --coffee-id, tasting rate
49
+ roast_id roast_data row; used by roast get/delete, roast list --roast-id, sales --roast-id
50
+ sale_id coffee_sales row; used by sales update/delete
51
+
52
+ COMMANDS
53
+ --------
54
+ auth
55
+ login [--headless]
56
+ status
57
+ logout
58
+
59
+ catalog
60
+ search [options]
61
+ --origin <text>
62
+ --process <method>
63
+ --price-min <n>
64
+ --price-max <n>
65
+ --flavor <keywords>
66
+ --name <text>
67
+ --supplier <name>
68
+ --ids <n,n,...>
69
+ --variety <text>
70
+ --drying-method <text>
71
+ --stocked-days <n>
72
+ --stocked
73
+ --sort <price|price-desc|name|origin|newest>
74
+ --offset <n>
75
+ --limit <n>
76
+ get <catalog_id>
77
+ stats
78
+ similar <catalog_id>
79
+ --threshold <0-1> default 0.70
80
+ --limit <n> default 10
81
+ --stocked-only
82
+
83
+ inventory (member)
84
+ list [--stocked] [--limit <n>]
85
+ get <inventory_id>
86
+ add
87
+ --catalog-id <id> required in flag mode
88
+ --qty <lbs> required in flag mode
89
+ --cost <dollars>
90
+ --tax-ship <dollars>
91
+ --notes <text>
92
+ --purchase-date <YYYY-MM-DD>
93
+ --form
94
+ update <inventory_id>
95
+ --qty <lbs>
96
+ --cost <dollars>
97
+ --tax-ship <dollars>
98
+ --notes <text>
99
+ --stocked <true|false>
100
+ delete <inventory_id> [--yes]
101
+
102
+ roast (member)
103
+ list
104
+ --coffee-id <inventory_id>
105
+ --roast-id <roast_id>
106
+ --batch-name <text>
107
+ --date-start <YYYY-MM-DD>
108
+ --date-end <YYYY-MM-DD>
109
+ --stocked
110
+ --catalog-id <catalog_id>
111
+ --limit <n>
112
+ get <roast_id>
113
+ --include-temps
114
+ --include-events
115
+ create
116
+ --coffee-id <inventory_id> required in flag mode
117
+ --batch-name <name>
118
+ --oz-in <oz>
119
+ --oz-out <oz>
120
+ --roast-date <YYYY-MM-DD>
121
+ --notes <text>
122
+ --form
123
+ update <roast_id>
124
+ --notes <text>
125
+ --oz-out <oz>
126
+ --batch-name <name>
127
+ --targets <text>
128
+ delete <roast_id> [--yes]
129
+ import [file]
130
+ --coffee-id <inventory_id> required in flag mode
131
+ --batch-name <name>
132
+ --oz-in <oz>
133
+ --roast-notes <text>
134
+ --form
135
+ watch [directory]
136
+ --coffee-id <inventory_id> required unless --auto-match
137
+ --batch-prefix <name>
138
+ --prompt-each
139
+ --auto-match mutually exclusive with --coffee-id
140
+ --resume
141
+ --form
142
+
143
+ sales (member)
144
+ list [--limit <n>]
145
+ record
146
+ --roast-id <roast_id> required in flag mode
147
+ --oz <amount> required in flag mode
148
+ --price <dollars> required in flag mode
149
+ --buyer <name>
150
+ --sell-date <YYYY-MM-DD>
151
+ --form
152
+ update <sale_id>
153
+ --oz <amount>
154
+ --price <dollars>
155
+ --buyer <name>
156
+ --sell-date <YYYY-MM-DD>
157
+ delete <sale_id> [--yes]
158
+
159
+ tasting (member)
160
+ get <catalog_id>
161
+ --filter <user|supplier|both> default both
162
+ rate [inventory_id]
163
+ --aroma <1-5> required in flag mode
164
+ --body <1-5> required in flag mode
165
+ --acidity <1-5> required in flag mode
166
+ --sweetness <1-5> required in flag mode
167
+ --aftertaste <1-5> required in flag mode
168
+ --brew-method <method>
169
+ --notes <text>
170
+ --form
171
+
172
+ config
173
+ list
174
+ get <key>
175
+ set <key> <value>
176
+ reset
177
+
178
+ Current config keys:
179
+ form-mode true|false
180
+
181
+ WORKFLOWS
182
+ ---------
183
+ Catalog -> inventory:
184
+ purvey catalog search --origin "Ethiopia" --stocked --pretty
185
+ purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
186
+
187
+ Import a roast from Artisan:
188
+ purvey inventory list --stocked --pretty
189
+ purvey roast import ~/artisan/ethiopia.alog --coffee-id 7 --pretty
190
+
191
+ Watch a folder for new roasts:
192
+ purvey roast watch ~/artisan/ --coffee-id 7
193
+ purvey roast watch ~/artisan/ --auto-match
194
+ purvey roast watch --resume
195
+
196
+ Rate coffee and record a sale:
197
+ purvey tasting rate 7 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
198
+ purvey sales record --roast-id 123 --oz 12 --price 22.00 --buyer "Jane Smith"
199
+
200
+ ERROR PATTERNS
38
201
  --------------
39
- Default: compact single-line JSON (machine-readable, pipeable to jq)
40
- --pretty: indented + colorized JSON (human-readable)
41
- --csv: CSV rows (pipeable to spreadsheets)
42
- User feedback (spinners, success/error) → stderr only; stdout is always clean data.
43
-
44
- DATA MODEL
45
- ----------
46
- coffee_catalog (catalog_id)
47
- └─ green_coffee_inv (id, user, catalog_id) ← your inventory
48
- ├─ roast_data (roast_id, coffee_id) ← your roast profiles
49
- │ ├─ roast_temperatures ← temperature curve
50
- │ └─ roast_events ← FC, drop, etc.
51
- ├─ coffee_sales (id, roast_id) ← your sales
52
- └─ cupping notes stored on green_coffee_inv (aroma/body/acidity/sweetness/aftertaste)
53
-
54
- CATALOG COMMANDS (no auth required)
55
- -------------------------------------
56
- Search the public catalog:
57
- purvey catalog search [options]
58
- Options (all optional):
59
- --origin <text> country/region (e.g. "Ethiopia", "Colombia")
60
- --process <method> natural|washed|honey
61
- --price-min <n> min USD/lb (filters on price_per_lb)
62
- --price-max <n> max USD/lb (filters on price_per_lb)
63
- --flavor <keywords> comma-separated (e.g. "blueberry,citrus")
64
- --stocked only currently stocked
65
- --sort <field> price|price-desc|name|origin|newest
66
- --offset <n> skip N results for pagination (default: 0)
67
- --limit <n> max results (default: 10)
68
- Returns: array of catalog items with id, name, origin, process, flavor_notes, price_per_lb, cost_lb, stocked
69
-
70
- Get one catalog item by ID:
71
- purvey catalog get <catalog_id>
72
- Returns: single catalog item object
73
-
74
- Catalog statistics:
75
- purvey catalog stats
76
- Returns: aggregate stats (count, avg_price, origins, processes, etc.)
77
-
78
- INVENTORY COMMANDS (member role required)
79
- ------------------------------------------
80
- List your green coffee inventory:
81
- purvey inventory list [options]
82
- Options:
83
- --stocked only stocked beans
84
- --limit <n> max results (default: 20)
85
- Returns: array with id, catalog_id, qty, cost, stocked, purchase_date, plus joined catalog fields
86
-
87
- Get one inventory item:
88
- purvey inventory get <id>
89
- Returns: single inventory item
90
-
91
- Add bean to inventory (REQUIRED: --catalog-id, --qty):
92
- purvey inventory add --catalog-id <id> --qty <lbs> [options]
93
- Options:
94
- --catalog-id <id> [REQUIRED] coffee_catalog id
95
- --qty <lbs> [REQUIRED] pounds purchased
96
- --cost <dollars> cost per lb
97
- --tax-ship <dollars> tax + shipping cost
98
- --notes <text> notes
99
- --purchase-date <YYYY-MM-DD> defaults to today
100
- --form interactive wizard
101
- Returns: created inventory item
102
-
103
- Update inventory item:
104
- purvey inventory update <id> [options]
105
- Options (at least one required):
106
- --qty <lbs> updated quantity
107
- --cost <dollars> updated cost
108
- --tax-ship <dollars> updated tax/shipping
109
- --notes <text> updated notes
110
- --stocked <true|false> mark as stocked/unstocked
111
- Returns: updated inventory item
112
-
113
- Delete inventory item:
114
- purvey inventory delete <id> [-y]
115
- -y skips confirmation prompt
116
-
117
- ROAST COMMANDS (member role required)
118
- ---------------------------------------
119
- List roast profiles:
120
- purvey roast list [options]
121
- Options:
122
- --coffee-id <id> filter by inventory item id
123
- --limit <n> max results (default: 20)
124
- Returns: array of roast profiles with roast_id, batch_name, roast_date, oz_in, oz_out
125
-
126
- Get roast profile:
127
- purvey roast get <roast_id> [options]
128
- Options:
129
- --include-temps include temperature curve data
130
- --include-events include roast events (FC, drop, etc.)
131
- Returns: roast profile object
132
-
133
- Create roast profile (REQUIRED: --coffee-id):
134
- purvey roast create --coffee-id <id> [options]
135
- Options:
136
- --coffee-id <id> [REQUIRED] green_coffee_inv id
137
- --batch-name <name> defaults to "CoffeeName YYYY-MM-DD"
138
- --oz-in <oz> green weight in ounces
139
- --oz-out <oz> roasted weight in ounces
140
- --roast-date <YYYY-MM-DD> defaults to today
141
- --notes <text> roast notes
142
- --form interactive wizard
143
- Returns: created roast profile
144
-
145
- Delete roast profile:
146
- purvey roast delete <roast_id> [-y]
147
-
148
- Update roast profile:
149
- purvey roast update <roast_id> [options]
150
- Options:
151
- --notes <text> updated roast notes
152
- --oz-out <oz> updated roasted weight (recalculates weight_loss_percent)
153
- --batch-name <name> updated batch name
154
- --targets <text> updated roast targets (planning/goals)
155
- At least one flag required.
202
+ Not logged in:
203
+ run purvey auth login or purvey auth login --headless
156
204
 
157
- Import Artisan .alog file (REQUIRED: <file>, --coffee-id):
158
- purvey roast import <file.alog> --coffee-id <id> [options]
159
- Options:
160
- --coffee-id <id> [REQUIRED] green_coffee_inv id
161
- --batch-name <name> auto-generated if omitted
162
- --oz-in <oz> extracted from .alog if omitted
163
- --roast-notes <text> additional notes
164
- --form interactive wizard
165
- Returns: roast profile + milestone_events + control_events counts
166
-
167
- Watch directory for new .alog files (REQUIRED: <directory>):
168
- purvey roast watch <directory> [options]
169
- Options:
170
- --coffee-id <id> [REQUIRED unless --auto-match] green_coffee_inv id
171
- --batch-prefix <name> prefix for batch names
172
- --auto-match AI matches bean per file (requires member role; mutually exclusive with --coffee-id)
173
- --prompt-each prompt for bean on each new file
174
- --resume resume previous watch session
175
- --form interactive wizard
176
- Runs continuously (Ctrl+C to stop). Persists session for --resume.
177
-
178
- SALES COMMANDS (member role required)
179
- ---------------------------------------
180
- List sales:
181
- purvey sales list [--limit <n>]
182
- Returns: array with id, roast_id, oz, price, buyer, sell_date
183
-
184
- Record sale (REQUIRED: --roast-id, --oz, --price):
185
- purvey sales record --roast-id <id> --oz <amount> --price <dollars> [options]
186
- Options:
187
- --roast-id <id> [REQUIRED] roast_data id
188
- --oz <amount> [REQUIRED] ounces sold
189
- --price <dollars> [REQUIRED] sale price
190
- --buyer <name> buyer name/identifier
191
- --sell-date <YYYY-MM-DD> defaults to today
192
- --form interactive wizard
193
- Returns: created sale record
194
-
195
- Update sale:
196
- purvey sales update <id> [--oz <n>] [--price <n>] [--buyer <name>] [--sell-date <date>]
197
-
198
- Delete sale:
199
- purvey sales delete <id> [-y]
200
-
201
- TASTING COMMANDS (member role required)
202
- -----------------------------------------
203
- Get tasting notes for a catalog item:
204
- purvey tasting get <catalog_id> [--filter user|supplier|both]
205
- --filter both (default): returns {supplier: {...}, user: {...}}
206
- --filter user: only your cupping notes from green_coffee_inv
207
- --filter supplier: only the supplier's flavor notes from coffee_catalog
208
-
209
- Rate a bean (REQUIRED: <inventory_id>, all score flags):
210
- purvey tasting rate <inventory_id> --aroma <1-5> --body <1-5> --acidity <1-5> --sweetness <1-5> --aftertaste <1-5> [options]
211
- Options:
212
- --aroma <1-5> [REQUIRED in flag mode] integer 1-5
213
- --body <1-5> [REQUIRED in flag mode] integer 1-5
214
- --acidity <1-5> [REQUIRED in flag mode] integer 1-5
215
- --sweetness <1-5> [REQUIRED in flag mode] integer 1-5
216
- --aftertaste <1-5> [REQUIRED in flag mode] integer 1-5
217
- --brew-method pour_over|french_press|espresso|etc
218
- --notes <text> tasting notes
219
- --form interactive wizard
220
- Note: <inventory_id> is green_coffee_inv.id, NOT catalog_id.
221
- Scores are stored on the green_coffee_inv row.
222
-
223
- CONFIG COMMANDS
224
- ---------------
225
- purvey config list show all config
226
- purvey config get form-mode get a value
227
- purvey config set form-mode true|false set a value
228
- purvey config reset clear all config
229
-
230
- Config keys:
231
- form-mode true|false — when true, write commands auto-enter interactive
232
- wizard if required args are missing (default: false)
233
-
234
- COMMON WORKFLOWS
235
- ----------------
236
- 1. Import a roast and rate it:
237
- purvey inventory list --stocked --pretty # find your bean id
238
- purvey roast import roast.alog --coffee-id 42 # import
239
- purvey tasting rate 42 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
240
-
241
- 2. Full workflow: buy → roast → sell:
242
- purvey catalog search --origin "Ethiopia" --stocked --pretty
243
- purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
244
- purvey roast create --coffee-id 7 --oz-in 16 --batch-name "Ethiopia Guji Light"
245
- purvey sales record --roast-id 123 --oz 12 --price 22.00 --buyer "Jane"
246
-
247
- 3. Watch directory and auto-import roasts:
248
- purvey roast watch ~/artisan/ --coffee-id 7
249
- # or with AI bean matching:
250
- purvey roast watch ~/artisan/ --auto-match
251
-
252
- 4. Find a coffee and check tasting notes:
253
- purvey catalog search --flavor "blueberry,floral" --pretty
254
- purvey tasting get 128 --filter both --pretty
255
-
256
- ERROR HANDLING
257
- --------------
258
- Exit 0: success
259
- Exit 1: error (auth failure, not found, validation error, API error)
260
- Errors go to stderr as: Error: <message>
205
+ Wrong ID type:
206
+ verify whether the command wants catalog_id, inventory_id, roast_id, or sale_id
261
207
 
262
- Common errors:
263
- "Not logged in" → run: purvey auth login [--headless]
264
- "Not found" → check the ID; must be yours (RLS enforced)
265
- "INVALID_ARGUMENT" → check flags; use --form for interactive mode
266
- "--auto-match and --coffee-id are mutually exclusive" → use one or the other
208
+ Missing required args in write commands:
209
+ pass the required flags or use --form
267
210
 
268
- ID TYPES (avoid confusion)
269
- ---------------------------
270
- catalog_id → coffee_catalog table; used with: catalog get, inventory add --catalog-id, tasting get
271
- inventory_id → green_coffee_inv table (your beans); used with: inventory get/update/delete, roast --coffee-id, tasting rate
272
- roast_id → roast_data table; used with: roast get/delete, sales --roast-id
273
- sale_id → coffee_sales table; used with: sales update/delete
211
+ Mutually exclusive watch flags:
212
+ roast watch forbids using --auto-match together with --coffee-id
274
213
  `.trim();
275
- // ─── Command builder ──────────────────────────────────────────────────────────
276
- /**
277
- * `purvey context`
278
- * Outputs a comprehensive CLI reference for AI agent onboarding.
279
- * Optimized for token efficiency — dense, structured, no fluff.
280
- */
281
214
  export function buildContextCommand() {
282
215
  return new Command('context')
283
216
  .description('Output full CLI reference for AI agent onboarding')
284
217
  .addHelpText('after', `
285
218
  Examples:
286
- purvey context # print full reference to stdout
287
- purvey context | head -50 # preview first section
288
- purvey context > cli-ref.txt # save to file
219
+ purvey context
220
+ purvey context | head -50
221
+ purvey context > cli-reference.txt
289
222
  `)
290
223
  .action(() => {
291
224
  console.log(CONTEXT_TEXT);
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,iFAAiF;AACjF,EAAE;AACF,4EAA4E;AAC5E,gFAAgF;AAEhF,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4QpB,CAAC,IAAI,EAAE,CAAC;AAET,iFAAiF;AAEjF;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CAAC,mDAAmD,CAAC;SAChE,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,6EAA6E;AAC7E,kEAAkE;AAElE,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiNpB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CAAC,mDAAmD,CAAC;SAChE,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEhB,MAAM,iBAAiB,CAAC;AAOzB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAIhE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAmuB3C"}
1
+ {"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEhB,MAAM,iBAAiB,CAAC;AAOzB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAIhE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAgyB3C"}
@@ -22,24 +22,70 @@ export function buildRoastCommand() {
22
22
  .command('list')
23
23
  .description('List your roast profiles, sorted by date (newest first)')
24
24
  .option('--coffee-id <id>', 'Filter by green_coffee_inv ID')
25
+ .option('--roast-id <id>', 'Filter by roast profile ID')
26
+ .option('--batch-name <text>', 'Filter by batch name (partial match, case-insensitive)')
27
+ .option('--date-start <YYYY-MM-DD>', 'Only show roasts on or after this date')
28
+ .option('--date-end <YYYY-MM-DD>', 'Only show roasts on or before this date')
29
+ .option('--stocked', 'Only show roasts for currently stocked beans')
30
+ .option('--catalog-id <id>', 'Filter by coffee_catalog ID')
25
31
  .option('--limit <n>', 'Maximum results to return', '20')
26
32
  .addHelpText('after', `
27
33
  Examples:
28
34
  purvey roast list --pretty
29
35
  purvey roast list --coffee-id 7 --pretty
36
+ purvey roast list --roast-id 123 --pretty
37
+ purvey roast list --batch-name "Ethiopia Guji" --pretty
38
+ purvey roast list --date-start 2026-03-01 --date-end 2026-03-31
39
+ purvey roast list --stocked --limit 10
40
+ purvey roast list --catalog-id 128 --pretty
30
41
  purvey roast list --limit 5 | jq '.[].roast_id'
31
42
  purvey roast list --csv > roasts.csv
32
43
 
33
44
  Notes:
34
45
  --coffee-id filters by inventory item (green_coffee_inv.id), not catalog_id.
46
+ --roast-id filters by the exact roast profile ID while preserving list output shape.
47
+ --catalog-id filters by coffee_catalog ID (cross-reference from catalog search).
48
+ --batch-name accepts partial matches (case-insensitive).
49
+ --date-start and --date-end accept YYYY-MM-DD format; use together for a range.
50
+ --stocked only returns roasts for beans currently marked as stocked in inventory.
35
51
  Returns roast_id, batch_name, roast_date, oz_in, oz_out, and bean details.
36
52
  Requires authentication (member role).
37
53
  `)
38
54
  .action(withErrorHandling(async (opts, cmd) => {
39
55
  const globalOpts = cmd.optsWithGlobals();
40
56
  const { supabase, userId } = await requireAuth('member');
57
+ // Parse --date-start and --date-end format
58
+ const dateStart = opts.dateStart;
59
+ const dateEnd = opts.dateEnd;
60
+ if (dateStart && !/^\d{4}-\d{2}-\d{2}$/.test(dateStart)) {
61
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --date-start: "${dateStart}". Must be YYYY-MM-DD format.`);
62
+ }
63
+ if (dateEnd && !/^\d{4}-\d{2}-\d{2}$/.test(dateEnd)) {
64
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --date-end: "${dateEnd}". Must be YYYY-MM-DD format.`);
65
+ }
66
+ // Parse exact-id filters
67
+ let roastId;
68
+ if (opts.roastId !== undefined) {
69
+ roastId = parseInt(opts.roastId, 10);
70
+ if (isNaN(roastId) || roastId <= 0) {
71
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --roast-id: "${opts.roastId}". Must be a positive integer.`);
72
+ }
73
+ }
74
+ let catalogId;
75
+ if (opts.catalogId !== undefined) {
76
+ catalogId = parseInt(opts.catalogId, 10);
77
+ if (isNaN(catalogId) || catalogId <= 0) {
78
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --catalog-id: "${opts.catalogId}". Must be a positive integer.`);
79
+ }
80
+ }
41
81
  const data = await listRoasts(supabase, userId, {
42
82
  coffee_id: opts.coffeeId !== undefined ? parseInt(opts.coffeeId, 10) : undefined,
83
+ roast_id: roastId,
84
+ batch_name: opts.batchName,
85
+ date_start: dateStart,
86
+ date_end: dateEnd,
87
+ stocked_only: opts.stocked === true ? true : undefined,
88
+ catalog_id: catalogId,
43
89
  limit: Math.max(1, parseInt(opts.limit, 10)),
44
90
  });
45
91
  if (data.length === 0) {