@purveyors/cli
v0.15.1
Published
The official CLI for purveyors.io — coffee intelligence from your terminal
Maintainers
Readme
purvey
Coffee intelligence from your terminal.
purvey is the official CLI for purveyors.io. It gives coffee professionals, developers, and AI agents direct access to the Purveyors platform from the terminal: catalog search, inventory tracking, roast logging, sales records, tasting notes, and Artisan .alog import.
Use purvey --help for quick command discovery, purvey context for the dense human-readable operator reference, purvey manifest for the preferred machine-readable contract, or @purveyors/cli/manifest in-process.
At a glance
- Official binary:
purvey - Package:
@purveyors/cli - Runtime: Node.js 20+
- No pre-existing session required:
auth,config,context,manifest - Viewer role required:
catalog - Member role required:
inventory,roast,sales,tasting - Preferred machine-readable contract:
purvey manifest - Dense human-readable reference:
purvey context - Compatibility JSON alias:
purvey context --json - In-process machine contract:
@purveyors/cli/manifest
Installation
npm install -g @purveyors/cliRequirements:
- Node.js 20 or newer
Verify the install:
purvey --versionDocumentation map
| Surface | Use it for | | ------------------------------------------------ | --------------------------------------------------------- | | https://purveyors.io/docs/cli/overview | Live CLI docs | | https://purveyors.io/docs/api/overview | Live API docs | | AGENTS.md | Canonical contributor and agent guidance | | docs/CLI_STRATEGY.md | Historical architecture and shipped-surface retrospective | | https://github.com/reedwhetstone/purveyors-cli | Repository, issues, and source | | https://www.npmjs.com/package/@purveyors/cli | Package installation and release metadata |
Use the live docs on purveyors.io as the primary external reference. Use this README and AGENTS.md for repo-specific contributor detail.
Source-of-truth hierarchy
Use this hierarchy when references disagree:
src/program.ts,src/commands/*, andsrc/lib/manifest.tsdefine the shipped command surface, help text, auth requirements, output modes, ID guidance, and manifest payload.package.jsondefines the package version, Node engine, binary entrypoint, scripts, and exported subpaths.README.md,AGENTS.md, anddocs/CLI_STRATEGY.mdexplain the repo-specific contract for users, contributors, and agents.https://purveyors.io/docs/cli/overviewandhttps://purveyors.io/docs/api/overvieware the primary live product docs for external readers.
The CLI is an agent-first product surface. Treat the binary, exported functions, purvey manifest, purvey context, stdout/stderr behavior, and role-gated command boundaries as one contract.
Quick Start
# 1. Authenticate before using catalog, inventory, roast, sales, or tasting commands
purvey auth login
# For agents, CI, or remote machines, use headless flow:
# purvey auth login --headless
# 2. Confirm the session and role
purvey auth status
# 3. Search the catalog (requires viewer role)
purvey catalog search --origin "Ethiopia" --stocked --pretty
# 4. Check inventory (requires member role)
purvey inventory list --stocked --pretty
# 5. Import a roast from Artisan
purvey roast import ~/artisan/my-roast.alog --coffee-id 7 --pretty
# 6. Get the machine-readable CLI contract
purvey manifest
# 7. Get the dense readable CLI reference
purvey contextReference surfaces
Use the right reference surface for the job:
purvey manifestis the preferred stable machine-readable contract for agents, scripts, generated tooling, and parity checks.purvey contextis the dense human-readable operator reference for reviewers and interactive use.purvey context --jsonandpurvey context --prettyemit the same JSON payload aspurvey manifest, but exist mainly for compatibility with tooling that already shells out tocontext.@purveyors/cli/manifestexposes the same contract in-process for Node.js and agent runtimes.
Package exports and shared product surface
The npm package is both a binary and a shared TypeScript product surface. coffee-app and agent runtimes import CLI business functions directly, so exported subpaths are part of the supported machine contract.
| Import path | Use it for |
| -------------------------- | ---------------------------------------------- |
| @purveyors/cli | CLI entrypoint package root |
| @purveyors/cli/catalog | Catalog search, lookup, stats, similar coffees |
| @purveyors/cli/inventory | Green coffee inventory operations |
| @purveyors/cli/roast | Roast profile operations |
| @purveyors/cli/sales | Sales record operations |
| @purveyors/cli/tasting | Tasting note and rating operations |
| @purveyors/cli/lib | Shared library helpers |
| @purveyors/cli/manifest | Stable machine-readable CLI manifest |
| @purveyors/cli/artisan | Artisan .alog parsing and import utilities |
| @purveyors/cli/ai | AI helper surface used by CLI workflows |
Shell integrations should usually start with purvey manifest. In-process agent and website integrations should import the smallest relevant subpath instead of shelling out when they are already running in Node.js.
Export discipline:
- Add or remove subpaths only when the package contract intentionally changes.
- Keep
package.json,README.md,AGENTS.md,docs/CLI_STRATEGY.md,src/lib/manifest.ts, and dist parity checks aligned in the same PR. - Prefer the narrowest import path for application and agent code. For example, use
@purveyors/cli/catalogfor catalog operations instead of importing the package root. - Treat export-shape changes as product changes because coffee-app and agent runtimes import these functions directly.
Authentication and access model
No pre-existing session is required for auth, config, context, or manifest.
All remote data commands require a valid authenticated session:
catalogrequires theviewerroleinventory,roast,sales, andtastingrequire thememberrole
purvey uses Google OAuth through purveyors.io.
Interactive login:
purvey auth loginHeadless login for agents, CI, and remote machines:
purvey auth login --headless
# CLI prints a Google OAuth URL
# Open it in any browser and sign in
# Paste the full callback URL back into the terminalStatus:
purvey auth status
purvey auth status --json
purvey auth status --prettyLogout:
purvey auth logoutCredentials are stored at ~/.config/purvey/credentials.json.
Auth roles
| Role | Access |
| -------- | ------------------------------------------------------------------- |
| viewer | catalog search, catalog get, catalog stats, catalog similar |
| member | All viewer commands, plus inventory, roast, sales, tasting |
auth, config, context, and manifest remain available without a pre-existing session.
Commands that require a higher role exit with code 3 on auth failure. That includes not being signed in, an expired session, or an insufficient role.
Output contract and scripting
Most commands write compact JSON to stdout by default. Use --json if you want to request that mode explicitly.
Machine-contract rule of thumb: stdout is for successful payloads, stderr is for status or errors, and exit codes communicate the failure class. That rule is more important than making terminal output look conversational.
Pretty JSON:
purvey inventory list --prettyCSV output for array results on supporting commands:
purvey inventory list --csv > inventory.csv
purvey roast list --csv > roasts.csv
purvey sales list --csv > sales.csvPipe JSON into jq:
purvey inventory list | jq '.[].id'
purvey roast list --limit 5 | jq '.[].roast_id'
purvey auth status 2>/dev/null | jq -r '.email'Operational messages go to stderr so stdout stays script-friendly.
Fatal errors also stay on stderr, but the payload format depends on mode:
- interactive terminal with no explicit output flag: human-readable text
--json,--pretty, or--csv: JSON error envelope on stderr- piped or redirected with no explicit flag: compact JSON error envelope on stderr
That contract also applies to parser-level mistakes such as unknown options, unknown commands, and missing required arguments.
The JSON error envelope includes:
{ "error": true, "code": "INVALID_ARGUMENT", "exitCode": 2, "message": "..." }Output caveats worth knowing
purvey auth statusprints human-readable output in an interactive terminal unless you pass--json,--pretty, or--csv. When piped or redirected, it emits structured JSON on stdout even when unauthenticated.purvey config list/get/set/resetstay human-readable in an interactive terminal, but emit JSON on stdout when you pass--jsonor--pretty, or when stdout is non-interactive.--csvis not supported for config commands.purvey contextdefaults to dense human-readable reference text.--jsonand--prettymake it emit the same JSON manifest aspurvey manifest.purvey manifestalways emits the machine-readable contract on stdout.--prettyonly changes formatting.--csvaffects successful stdout output only; fatal errors still use JSON on stderr.
Exit codes
All purvey commands exit with a numeric code your scripts can check with $?.
| Code | Meaning |
| ---- | ---------------------------------------------------------------- |
| 0 | Success |
| 1 | Unexpected or unclassified error |
| 2 | Invalid argument or bad input |
| 3 | Auth error: not logged in, expired session, or insufficient role |
| 4 | Not found |
| 5 | Dependency conflict |
| 6 | Local config error |
Scripting pattern:
purvey catalog search --origin "Ethiopia" --stocked --json
if [ $? -eq 3 ]; then
purvey auth login --headless
fiCommand reference
auth
purvey auth loginpurvey auth login --headlesspurvey auth statuspurvey auth status --jsonpurvey auth status --prettypurvey auth status --csvpurvey auth logout
Examples:
purvey auth login
purvey auth login --headless
purvey auth status --pretty
purvey auth status --csv
purvey auth logoutNotes:
auth loginuses browser-based Google OAuth.auth login --headlessprints an OAuth URL and accepts a pasted callback URL.auth status --jsonis the safest mode for scripts.auth status --csvis supported for spreadsheet-style checks, but JSON remains the better integration format.
catalog
purvey catalog searchpurvey catalog get <id>purvey catalog statspurvey catalog similar <id>
catalog search filters:
--origin <text>; origin, country, continent, or region--process <method>; processing method--price-min <n>; minimum USD/lb--price-max <n>; maximum USD/lb--flavor <keywords>; comma-separated flavor terms--name <text>; partial coffee name match--supplier <name>; partial supplier match--ids <n,n,...>; fetch specific catalog IDs, ignores limit and offset--variety <text>; partial cultivar match--drying-method <text>; partial drying method match--stocked-days <n>; stocked within N days--stocked; only currently stocked coffees--sort <price|price-desc|name|origin|newest>--offset <n>; pagination offset--limit <n>; default10
catalog similar <id> options:
--threshold <0-1>; default0.70--limit <n>; default10--stocked-only
Examples:
purvey catalog search --origin "Ethiopia" --pretty
purvey catalog search --supplier "Royal Coffee" --stocked --pretty
purvey catalog search --ids "1182,1183,1200"
purvey catalog search --stocked --sort price --offset 10 --limit 10
purvey catalog similar 1182 --threshold 0.85 --stocked-only --pretty
purvey catalog stats --pretty
purvey catalog get 1182 --prettyNotes:
- Catalog commands require an authenticated
viewerrole. catalog getandcatalog similarboth takecoffee_catalog.catalog_id.catalog statsreturns aggregate catalog metrics, not your personal inventory metrics.
inventory
purvey inventory listpurvey inventory get <id>purvey inventory addpurvey inventory update <id>purvey inventory delete <id>
inventory list filters:
--stocked--catalog-id <id>--purchase-date-start <YYYY-MM-DD>--purchase-date-end <YYYY-MM-DD>--origin <country>--limit <n>; default20--offset <n>; default0
inventory add flags:
--catalog-id <id>; required in flag mode--qty <lbs>; required in flag mode--cost <dollars>--tax-ship <dollars>--notes <text>--purchase-date <YYYY-MM-DD>--form
inventory update <id> flags:
--qty <lbs>--cost <dollars>--tax-ship <dollars>--notes <text>--stocked <true|false>
inventory delete <id> options:
--force; cascade dependent roasts and sales--yes; skip confirmation prompt
Examples:
purvey inventory list --stocked --pretty
purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
purvey inventory add --form
purvey inventory update 7 --stocked false
purvey inventory delete 7 --yesNotes:
- Inventory commands require an authenticated
memberrole. - Inventory
idisgreen_coffee_inv.id, notcatalog_id. inventory deletemay require--forceif dependent roasts or sales exist.
roast
purvey roast listpurvey roast get <id>purvey roast createpurvey roast update <id>purvey roast delete <id>purvey roast import [file]purvey roast watch [directory]
roast list filters:
--coffee-id <id>; inventory item ID--roast-id <id>; exact roast profile ID--batch-name <text>--coffee-name <text>--date-start <YYYY-MM-DD>--date-end <YYYY-MM-DD>--stocked--catalog-id <id>--limit <n>; default20--offset <n>; default0
roast get <id> options:
--include-temps--include-events
roast create flags:
--coffee-id <id>; required in flag mode--batch-name <name>--oz-in <oz>--oz-out <oz>--roast-date <YYYY-MM-DD>--notes <text>--form
roast update <id> flags:
--batch-name <name>--oz-out <oz>--notes <text>--targets <text>
roast import [file] flags:
--coffee-id <id>; required unless--form--batch-name <name>--oz-in <oz>--roast-notes <text>--roast-targets <text>--form
roast watch [directory] options:
--coffee-id <id>; required unless--auto-match--batch-prefix <name>--prompt-each--auto-match--commit-mode <batch|individual>--oz-in <oz>--roast-notes <text>--roast-targets <text>--resume--form
Examples:
purvey roast list --catalog-id 128 --pretty
purvey roast get 123 --include-temps --pretty
purvey roast create --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz-in 16
purvey roast import ~/artisan/ethiopia.alog --coffee-id 7 --roast-targets "Aim for 18% development"
purvey roast watch ~/artisan/ --auto-matchNotes:
- Roast commands require an authenticated
memberrole. --coffee-iduses inventory IDs.roast watch --auto-matchis mutually exclusive with--coffee-id.roast watch --commit-modedefaults tobatch.
sales
purvey sales listpurvey sales recordpurvey sales update <id>purvey sales delete <id>
sales list filters:
--roast-id <id>--date-start <YYYY-MM-DD>--date-end <YYYY-MM-DD>--buyer <name>--limit <n>; default20--offset <n>; default0
sales record flags:
--roast-id <id>; exact selector mode--coffee-id <id>; resolved selector mode, requires--batch-name--batch-name <name>; resolved selector mode, requires--coffee-id--oz <amount>; required in flag mode--price <dollars>; required in flag mode--buyer <name>--sell-date <YYYY-MM-DD>--form
sales update <id> flags:
--oz <amount>--price <dollars>--buyer <name>--sell-date <YYYY-MM-DD>
sales delete <id> options:
--yes
Examples:
purvey sales record --roast-id 123 --oz 12 --price 22.00 --buyer "Jane Smith"
purvey sales record --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz 8 --price 16.00
purvey sales list --pretty
purvey sales update 5 --price 24.00
purvey sales delete 5 --yesNotes:
- Sales commands require an authenticated
memberrole. - Use exactly one selector mode for
sales record: exact--roast-id, or resolved--coffee-idplus--batch-name. - If resolved mode matches multiple roasts, rerun with
--roast-id. --priceis total sale price, not per-ounce price.
tasting
purvey tasting get <catalog-id>purvey tasting rate [inventory-id]
ID distinction:
tasting gettakes acatalog_idtasting ratetakes aninventory id
purvey tasting get <catalog-id> options:
--filter <user|supplier|both>; defaultboth
purvey tasting rate [inventory-id] options:
--aroma <1-5>; required in flag mode--body <1-5>; required in flag mode--acidity <1-5>; required in flag mode--sweetness <1-5>; required in flag mode--aftertaste <1-5>; required in flag mode--brew-method <method>--notes <text>--form
Examples:
purvey tasting get 128 --filter both --pretty
purvey tasting rate 7 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
purvey tasting rate --formNotes:
- Tasting commands require an authenticated
memberrole. tasting getcombines supplier notes with your own notes when available.tasting ratewrites scores back to your inventory row.
config
purvey config list(supports--json,--pretty)purvey config get <key>(supports--json,--pretty)purvey config set <key> <value>(supports--json,--pretty)purvey config reset(supports--json,--pretty)
Current config key:
form-mode: whentrue, write commands enter interactive mode when required args are missing
Examples:
purvey config set form-mode true
purvey config get form-mode --json
purvey config reset --jsonNotes:
- Config commands are local-only.
- Config file path:
~/.config/purvey/config.json --csvis not supported.
context
purvey contextpurvey context --jsonpurvey context --pretty
Notes:
- Default output is the dense human-readable operator reference text.
--jsonand--prettyemit the same machine-readable manifest aspurvey manifest.- Prefer
purvey manifestfor new machine integrations. Usepurvey context --jsonwhen you need compatibility with an existingcontext-based workflow. - Use
@purveyors/cli/manifestwhen you need that same contract in-process from Node.js or an agent runtime. --csvis not supported.
manifest
purvey manifestpurvey manifest --jsonpurvey manifest --pretty
Notes:
purvey manifestemits the preferred stable machine-readable CLI contract on stdout.purvey manifestandpurvey manifest --jsonboth emit compact JSON.purvey manifest --prettyemits indented JSON.purvey manifestandpurvey context --jsonemit the same JSON payload.- Use
purvey manifestfor new automation and treatpurvey context --jsonas a compatibility alias. --csvis not supported.
In-process manifest export
@purveyors/cli/manifest
Use this when you need the same stable contract from Node.js without shelling out to the CLI. This package subpath is exported via ./manifest in package.json and is part of the supported public machine surface for agents and scripts.
import { getCliManifest } from '@purveyors/cli/manifest';
const manifest = getCliManifest();Common workflows
Catalog to inventory to roast to sale
purvey catalog search --origin "Ethiopia" --process "natural" --stocked --pretty
purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
purvey roast import ~/artisan/guji-light.alog --coffee-id 7 --pretty
purvey tasting rate 7 --aroma 5 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
purvey sales record --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz 12 --price 22.00 --buyer "Jane Smith"Continuous Artisan watch mode
purvey roast watch ~/artisan/ --coffee-id 7
purvey roast watch ~/artisan/ --auto-match
purvey roast watch --resumeExport records for spreadsheets
purvey inventory list --csv > inventory.csv
purvey roast list --csv > roasts.csv
purvey sales list --csv > sales.csvBootstrap an agent or script
purvey manifest
purvey context
purvey auth login --headless
purvey auth status 2>/dev/null | jq .ID reference
Use the right ID for the right command.
catalog_id:coffee_catalogrows; used bycatalog get,inventory add --catalog-id,tasting get,roast list --catalog-idinventory id:green_coffee_invrows; used byinventory get/update/delete,roast --coffee-id,tasting rate,roast list --coffee-idroast_id:roast_datarows; used byroast get/delete,sales --roast-id,roast list --roast-idsales recordalso supports resolving a roast frominventory idplus--batch-name; if that selector is ambiguous, use exactroast_idsale id:coffee_salesrows; used bysales update/delete
Environment variables
PURVEYORS_SUPABASE_URL: override the Supabase project URLPURVEYORS_SUPABASE_ANON_KEY: override the Supabase anon keyPURVEYORS_BASE_URL: override the Purveyors web base URLPURVEY_DEBUG: enable verbose error output
For AI agents
Recommended bootstrap order:
purvey manifest
purvey context
purvey auth login --headlessUse purvey manifest as the authoritative machine-readable entry point. Keep purvey context for dense operator context, or use purvey context --json only when you need compatibility with an existing wrapper.
Why this CLI works well for agents:
- stable command names
- structured stdout by default
- headless auth flow
- documented exit codes and role boundaries
- dedicated machine-readable manifest command
- dedicated dense human-readable reference command
purvey context --jsonparity withpurvey manifest@purveyors/cli/manifestexport for in-process access--offsetand--limitpagination across list commands
The scripting contract is simple: stdout carries successful payloads, stderr carries status and fatal errors, and exit codes stay stable.
Troubleshooting
Error: not logged in or exit code 3
purvey auth login
# or
purvey auth login --headlessCatalog commands fail after logging in
Run purvey auth status to confirm you have a viewer or member role. If the session is stale, run purvey auth logout and log in again.
Wrong ID type passed to a command
Use the ID reference section above. catalog_id and inventory IDs are different values.
Pagination only shows the first page
All list commands default to 20 results. Use --limit and --offset.
purvey inventory list --limit 20 --offset 0
purvey inventory list --limit 20 --offset 20
purvey inventory list --limit 20 --offset 40inventory delete fails with dependency conflict
purvey inventory delete 7 --force --yesEnable verbose error output
PURVEY_DEBUG=1 purvey <command>Development
git clone https://github.com/reedwhetstone/purveyors-cli
cd purveyors-cli
pnpm install
npm run build
npm run verify:contract
npm run verify:dist
npm run verify:prepublish
npm run check
npm run lint
npm testnpm run verify:prepublish is the release guardrail. It rebuilds first, re-runs the targeted manifest and output-contract suites, checks the compiled dist/ artifact, verifies the npm pack --dry-run publish surface, and smoke-tests package.json, README.md, purvey manifest, purvey context --json, and @purveyors/cli/manifest.
Key files:
src/index.ts: executable entrypointsrc/program.ts: top-level CLI registration and global optionssrc/commands/: command definitions and help textsrc/lib/: business logic and Supabase integrationsrc/commands/context.ts: dense human-readable agent referencesrc/commands/manifest.ts: machine-readable CLI manifest commandsrc/lib/manifest.ts: shared manifest contract, package export list, command metadata, ID guidance, and context rendererpackage.json: package metadata and export surface, including./manifesttests/dist-contract.test.ts: compiled artifact parity guardrailsAGENTS.md: canonical contributor guide
Live documentation:
License
Sustainable Use License. See LICENSE.md.
Copyright 2026 Reed Whetstone / purveyors.io
