@meistrari/pantry-cli
v1.1.0
Published
CLI tool for chefs to interact with tela
Downloads
481
Maintainers
Keywords
Readme
pantry-cli
CLI for talking to Tela services. It handles device-flow auth once, then lets you call authenticated APIs and inspect OpenAPI, TypeDoc, and guide docs for any ingredient registered in Pantry.
Install
There are two ways to install pantry, and both can be installed directly or managed by mise. Pick whichever fits your setup.
Package manager (npm)
The npm package ships the CLI as a JavaScript bundle that runs under bun, so this route needs bun available:
bun add -g @meistrari/pantry-clinpm install -g @meistrari/pantry-clipnpm add -g @meistrari/pantry-cliOr with mise, which also manages the bun it runs under:
mise use -g npm:@meistrari/pantry-cliStandalone binary (no runtime)
A prebuilt binary for your platform — no bun or node needed. Published for darwin-arm64 and linux-x64.
With mise, straight from the GitHub releases:
mise use -g github:meistrari/pantry-cliOr with the install script:
curl -fsSL https://raw.githubusercontent.com/meistrari/pantry-cli/main/install.sh | bashIt installs to ~/.local/bin by default; override with PANTRY_INSTALL_DIR, or pin a version with PANTRY_VERSION:
PANTRY_INSTALL_DIR=/usr/local/bin PANTRY_VERSION=1.0.0 \
curl -fsSL https://raw.githubusercontent.com/meistrari/pantry-cli/main/install.sh | bashVerify
pantry --helpAgent skill
This repository includes a Vercel Skills-compatible agent skill in skills/pantry. It teaches coding agents when and how to use pantry to discover Tela ingredients, read endpoint schemas, inspect library docs, and avoid rebuilding capabilities that already exist in Tela.
Install it with Vercel Skills from this repository:
bunx skills add meistrari/pantry-clipnpx skills add meistrari/pantry-clinpx skills add meistrari/pantry-cliIt is also published to Pantry as the pantry-cli skill, in the pantry bundle, so an agent that already has the CLI can install it with:
pantry skills install pantry-cliThe skill ships an install script that bootstraps the CLI itself — via mise, then npm, then the standalone binary, whichever works first — and then tells the user to run pantry login. So the skill does not assume the CLI is already there, only that it is authenticated before the agent starts calling services. Skip the script with --ignore-scripts.
pantry decides between interactive and non-interactive login automatically: --interactive defaults to on when stdin is a TTY and off otherwise. In interactive mode login waits on stdin and opens the browser for you. In non-interactive mode (agents, CI, or an explicit --no-interactive) it instead prints the verification URL, code, and a login --device-code <code> command, then exits — the user authorizes in their browser and a follow-up login --device-code finishes the sign-in. Pass --no-interactive to force this two-step flow even from a TTY, or --interactive to force the browser-opening flow.
First run
Authenticate. Run the login command to start the device flow:
pantry loginIt prints a verification URL and code, then waits on stdin. Press Enter to open your browser.
Populate the ingredient cache.
pantryloads Tela services from Pantry instead of shipping a hardcoded service list:pantry updateThis writes
~/.pantry/ingredients.json. Re-runpantry updatewhenever an ingredient is added, removed, or changes environments. As a rule of thumb, run it every few days, or even before each use, to make sure your local ingredient cache is current.
Credentials live in ~/.pantry/credentials.<slug>.json. The default production auth target uses credentials.production.json. Force a re-login with:
pantry login --forceRemove every stored credential (all auth targets) with:
pantry login resetUpgrading from telactl
The CLI used to be called telactl and kept its config in ~/.telactl. On the first run after upgrading, pantry copies that directory to ~/.pantry, removes the old one, and says so on stderr — credentials, the ingredient cache, environment defaults, and the installed-skill registry all come across, so there is nothing to re-run.
It never overwrites: if ~/.pantry already exists, both directories are left exactly as they are. If the copy fails, ~/.telactl is left untouched and the error names it. Skills themselves are installed under ~/.agents/skills, outside either directory, so they are unaffected.
The old telactl binary is not removed. Delete it by hand once you are happy — rm "$(command -v telactl)".
One call spelling changed: Pantry itself is now reached by omitting the slug. telactl api pantry GET /ingredients becomes pantry api GET /ingredients. The old spelling is not silently accepted — a leftover pantry slug is answered with the command to run instead:
Use `pantry api GET /ingredients`.Every other ingredient is still named as before.
Common flows
Discover services
pantry api GET /ingredients
pantry api GET /ingredients query==file
pantry api GET /ingredients type==librarypantry is always available, so you can use it to discover API services, libraries, and skills before building something from scratch.
Read documentation
pantry docs endpoints <slug>
pantry docs reference <slug>
pantry docs reference <slug> <METHOD> <PATH>
pantry docs guide <slug>
pantry docs guide <slug> <path>Use endpoints to list API routes, reference for exact API or library contracts, and guide for step-by-step usage docs.
Manage agent skills
Skills are agent instructions published through Pantry as immutable, commit-addressed snapshots.
pantry can browse them and install them onto your machine.
pantry skills list -p # cached skills; ✓ marks the ones you have installed
pantry skills search openapi -p # live search against Pantry
pantry skills files add-pantry-labels -p # what is inside the snapshot
pantry skills cat add-pantry-labels # read SKILL.mdInstall one skill, several at once, or a skill together with the rest of its bundle:
pantry skills install add-pantry-labels
pantry skills install add-pantry-labels configure-openapi
pantry skills install add-pantry-labels --bundle--bundle is a flag on the skills you name, not a bundle name — it means "also install every other
skill in those skills' bundles". The last command above installs add-pantry-labels plus its
bundle-mates configure-openapi and configure-typedoc. With several slugs it expands each one's
own bundle and merges the results, installing nothing twice. It always needs at least one slug:
pantry skills install --bundle on its own is an error. If a skill has no bundle recorded,
nothing expands for it — pantry installs just that skill and says so on stderr. Unbundled skills
are never grouped with one another.
When a --bundle expansion spans two or more different bundles, the command asks first, since
that reaches well past anything you named:
Proceed with installing 5 skills from bundles "pantry" and "openapi"? [y/N]Answer n and nothing is installed and the exit code is non-zero (Aborted; nothing was
installed.). In a script or any non-interactive shell there is no prompt to answer, so the command
refuses outright:
Refusing to install 5 skills from 2 bundles without confirmation.
Re-run with --yes to install them all, or name skills from a single bundle.-y / --yes skips the question and proceeds. A single bundle never asks — you still get the
stderr notice, as before.
To install a whole bundle without naming a skill in it, name the bundle itself:
pantry skills install bundle pantry
pantry skills install bundle pantry openapiThis takes bundle names, not slugs — install bundle pantry installs add-pantry-labels,
configure-openapi, and configure-typedoc. Several bundles are merged and deduped, the resolved
set is listed on stderr before anything is installed, and --ignore-scripts, --no-symlink, and
-f / --force behave exactly as they do on install. There is no -y here and no confirmation
however many bundles you name: --bundle asks because named slugs can balloon into bundles you
never mentioned, while naming the bundles is asking for them outright. A name that matches no
skills is an error, and nothing from the other names is installed either:
No skills found in bundle "pantrry".
Nothing was installed. Check the name with `pantry skills list`.Skills are extracted to ~/.agents/skills/<slug> and symlinked into ~/.claude/skills/<slug>.
Skip the symlink with --no-symlink.
Every file is checked against the snapshot manifest's sha256 before anything is written, so a
truncated or tampered download fails without touching your disk.
install refuses to clobber anything it does not own: a target directory it did not install, or
an entry in ~/.claude/skills/<slug> that is not a symlink. -f / --force authorizes both
— it replaces the unowned install directory and replaces the non-symlink entry. Without it, the
first is a hard error and the second is only skipped, with a note on stderr.
An existing symlink at ~/.claude/skills/<slug> is replaced without asking — only a real
directory or file there is protected. That holds even when the symlink points somewhere pantry
never installed, and no --force or warning is involved.
Keep them current and remove them again:
pantry skills upgrade # every installed skill whose snapshot changed
pantry skills upgrade --check # report what is stale, install nothing
pantry skills up -p # same command, shorter (also: pantry skills u)
pantry skills upgrade add-pantry-labels --bundle # that skill and its installed bundle-mates
pantry skills uninstall add-pantry-labels
pantry skills uninstall add-pantry-labels configure-openapi
pantry skills uninstall add-pantry-labels --bundleupgrade and uninstall take several slugs too, and --bundle and -y / --yes behave exactly
as they do on install — the destructive command does not fan out across bundles more quietly
than the additive one. Declining there reports Aborted; nothing was removed. (or upgraded).
upgrade and uninstall work only on skills pantry installed — they read
~/.pantry/skills.json and refuse to touch anything else. That is also where they read the
bundle from: it is recorded at install time, so --bundle works offline here and expands to the
bundle members you actually have installed, ignoring anything published since. (install has no
such record to read, so it asks Pantry.) upgrade with no slug still means "everything
installed", which is unrelated to --bundle.
If any slug you name is not installed, uninstall refuses the whole run before deleting anything
— add-pantry-labels and configure-openapi are not installed by pantry. Nothing to remove. — so
a typo in one of four never leaves you with a partial uninstall.
Both name what they are about to touch before they touch it — Removing 2 skills from bundle
"pantry": add-pantry-labels, configure-openapi on stderr, ahead of the first deletion.
upgrade --check answers "is anything stale?" without upgrading to find out. It lists only the
skills with a newer snapshot, marked outdated rather than upgraded, and installs nothing — so
an empty list means everything is current. Since it cannot change anything, it never prompts for
a multi-bundle expansion and never fails on an empty registry; both print [] and exit 0. Naming
a slug that is not installed is still an error, because that is a typo rather than an answer.
upgrade has no --force: it will re-install over the directory it already recorded for a skill,
but it never replaces a non-symlink entry in ~/.claude/skills — clear that by hand, or re-run
install --force.
If a skill declares an install or uninstall script, the command is printed before it runs; pass
--ignore-scripts to skip it. That notice, and the script's own output, go to stderr — so
pantry skills upgrade keeps stdout as clean JSON you can pipe into jq. Only results are
written to stdout: Installed <slug>@<short-sha> to <dir> from install, `Removed from
Call an API
pantry api <METHOD> <PATH> [args...] [flags] # Pantry itself
pantry api <slug> <METHOD> <PATH> [args...] [flags] # any other ingredientPantry is the registry the CLI already reads from, so it is the ingredient api defaults to: omit the slug and the call goes there. Every other ingredient is named explicitly.
Args use HTTPie-style syntax:
| Syntax | Meaning |
| ------ | ------- |
| name=value | JSON body field as a string |
| name:=value | JSON body field as raw JSON |
| name==value | Query parameter |
| Header:value | Request header |
Examples:
# GET with query params
pantry api GET /ingredients limit==50 page==1
# POST with JSON fields
pantry api vault POST /files name=report.pdf size:=1024
# Raw body from a file
pantry api vault POST /files --body "$(cat body.json)"
# Custom header and environment override
pantry api GET /ingredients X-Debug:1 --env stagingOutput follows the terminal. At a TTY, -p / --pretty is already on — human-readable output: pretty JSON, tables, lists, ANSI-rendered markdown. Redirected or piped, it is off, and output is raw and script-friendly: minified JSON or verbatim response text. Pass --no-pretty to force the raw form at a terminal too.
The switch reads stdout, so pantry docs guide <slug> > guide.md writes plain markdown and pantry skills list | jq gets JSON, with no flag either way.
Global flags
These are accepted by every command, before or after the subcommand — pantry --pretty skills list and pantry skills list --pretty do the same thing:
| Flag | Meaning |
| --- | --- |
| --interactive / --no-interactive | Authenticate by prompting and opening a browser, or by printing instructions instead. Defaults to on when stdin is a TTY. |
| -p / --pretty / --no-pretty | Shape structured results for a human or for a machine. Defaults to on when stdout is a TTY. |
| -y / --yes | Skip confirmation prompts. |
A command that has nothing to do with a flag accepts it and ignores it — pantry login --pretty is valid and prints the same thing.
--ignore-scripts (skip a skill's declared install/uninstall script) is global to the pantry skills group rather than the whole CLI, since nothing else runs scripts. It is accepted from skills onward — pantry skills install foo --ignore-scripts, not pantry --ignore-scripts skills install foo.
--auth-env, --auth-url, and --force are deliberately not global, since silently ignoring those would change which environment you reach or what gets overwritten.
Commands
Auth
pantry login [--auth-env <slug>] [--auth-url <url>] [--force] [--device-code <code>]
pantry login reset
pantry whoami [--auth-env <slug>] [--auth-url <url>]
pantry token [--auth-env <slug>] [--auth-url <url>]
pantry token list--auth-env picks an auth-api environment by slug. --auth-url overrides it with a raw URL. login --device-code <code> resumes a device flow that a previous non-interactive login started, polling until sign-in completes. token prints the stored access token for the resolved auth environment, and token list shows stored credentials with validity checks.
Interactivity is controlled by the global --interactive / --no-interactive flag, which defaults to on when stdin is a TTY and off otherwise. When non-interactive (e.g. an agent with no TTY, or an explicit --no-interactive), a command that needs a fresh device flow prints the verification URL, code, and a login --device-code <code> command, then exits without polling. The user authorizes in their browser, then pantry login --device-code <code> resumes the flow and polls until sign-in completes. Cached and refreshable tokens are still used without prompting.
Cache and environments
pantry update
pantry env list <slug>
pantry env set <slug> <environment>update refreshes the ingredient cache from Pantry using production auth.
Docs
pantry docs endpoints <slug>
pantry docs reference <slug>
pantry docs reference <slug> <METHOD> <PATH>
pantry docs reference <library-slug> [file-path]
pantry docs guide <slug> [path]api ingredients expose api, docs endpoints, docs reference, and docs guide. library ingredients expose docs reference and docs guide.
For an api ingredient, docs reference <slug> prints the whole OpenAPI document and <METHOD> <PATH> narrows it to one endpoint — both required together. For a library, the optional argument is a path relative to the ingredient's API reference directory and defaults to globals.md, the generated index. docs guide <slug> [path] works the same way against the guides root, defaulting to the guides entry file (typically README.md).
Skills
pantry skills list [-p]
pantry skills search <query> [-p]
pantry skills files <slug> [-p]
pantry skills cat <slug> [<path>] [-p]
pantry skills install <slug...> [--bundle] [--no-symlink] [-f] [-y]
pantry skills install bundle <bundles...> [--no-symlink] [-f]
pantry skills upgrade [<slug...>] [--bundle] [--check] [-p] [-y]
pantry skills uninstall <slug...> [--bundle] [-y]| Command | What it does |
|---------|--------------|
| list | Skills from the local cache, annotated with install status |
| search | Live search against Pantry (type=skill) |
| files | The snapshot's file tree with sizes |
| cat | One file from the snapshot; defaults to SKILL.md |
| install | Download, verify, extract, and symlink one or more skills — and, with --bundle, their bundle-mates |
| upgrade | Reinstall installed skills whose snapshot sha changed (aliases up, u); no slug means all of them |
| uninstall | Remove skills pantry installed — and, with --bundle, their installed bundle-mates |
install, upgrade, and uninstall all accept several slugs in one call. uninstall refuses the whole run, before deleting anything, if any named slug is not installed.
Flags:
--bundle: a boolean modifier on the slugs you name — also act on every other skill in those skills' bundles. It takes no value and does not replace the slugs;--bundlewithout any is an error. With several slugs it expands each one's own bundle and dedupes the combined set. A skill with no bundle recorded —null, missing, or empty — contributes just itself, with a note on stderr, and is never grouped with other unbundled skills. When it does expand, the command lists the skills on stderr before acting on any of them.installreads the bundle from Pantry (the skill may not be installed yet);upgradeanduninstallread it from~/.pantry/skills.json, so they stay offline and only touch installed members.-y/--yes: proceed without confirming when a--bundleexpansion spans two or more different bundles. Without it, an interactive shell prompts (Proceed with installing 5 skills from bundles "pantry" and "openapi"?) and a non-interactive one refuses:Refusing to install 5 skills from 2 bundles without confirmation.One bundle, or no--bundle, never asks. Declining acts on nothing and exits non-zero.--check(upgradeonly): report which skills have a newer snapshot and install nothing. Only stale skills are listed, each withstatus: "outdated"rather thanupgraded, so an empty array means everything is current. Writing nothing, it skips both guards a real upgrade answers to — the--bundleconfirmation and the "nothing installed" error — and exits 0 with[]either way. A slug that is not installed is still an error.--ignore-scripts: do not run the skill's declared install/uninstall script.--no-symlink(install): do not symlink into~/.claude/skills.-f/--force(installonly): replace an install directory pantry does not own, and replace a non-symlink entry in~/.claude/skills.upgradehas no equivalent.
Diagnostics — script notices, relayed script output, skipped symlinks, and registry warnings — go to stderr; only results go to stdout, so pantry skills upgrade emits parseable JSON on stdout.
Requests
pantry api [<slug>] <METHOD> <PATH> [args...] [--env <slug>] [--auth-url <url>] [--body <json>]Omit <slug> to call Pantry itself.
Flags:
--env <slug>: override the service environment for this call.--auth-url <url>: override auth-api URL resolution.--body <string>: send a raw request body as-is, overriding field args.-p/--pretty: pretty-print response body as JSON when parseable. On by default at a TTY;--no-prettyforces the body verbatim.
Authorization: Bearer <token> and Accept: application/json are set automatically unless you override them with header args. Content-Type: application/json is set whenever a body is sent unless you pass a Content-Type: header arg.
Non-2xx responses print <status> <statusText> plus the body to stderr and exit 1.
Development
Use this section only if you are changing the CLI itself.
Install dependencies and build the binaries:
bun install
bun run buildbun run build runs the .mise/tasks/build.ts task (also invocable as mise run build). It bundles and obfuscates the CLI into dist/index.mjs — the npm bin, which runs under bun — then compiles standalone binaries per platform:
dist/pantry-darwin-arm64dist/pantry-linux-x64
It also emits dist/index.mjs.map (used by mise run trace to translate stack traces). The release workflow publishes dist/index.mjs to npm and attaches the two binaries and the sourcemap to the GitHub release; install.sh downloads the right binary from that release.
Run the CLI from source while developing:
bun cli <command>This is equivalent to:
bun run src/index.ts <command>Run tests:
bun testTech stack:
- Bun
- Cliffy
@meistrari/auth-clidevice flowopenapi-to-md
