@binaryandthread/cli
v0.3.2
Published
bt — the Binary & Thread terminal store. Buy heavyweight pieces with the ceremony of a production deploy. A dumb, well-tested client over the Store API; cards never touch the terminal.
Readme
@binaryandthread/cli, bt
Buy a Binary & Thread heavyweight piece from your terminal, with the ceremony of a production deploy. A small, well-tested client over the Store API, the card never touches the terminal.
btis a dumb client. Every dangerous operation (payment, fulfilment) lives in the backend. Checkout hands you a URL; you pay on the storefront's Stripe page in your own browser.
Install
Zero-config, it ships pointed at the live store (the publishable key is public by design):
# recommended: install globally for a persistent `bt` on your PATH
npm i -g @binaryandthread/cli
bt browse
# or run without installing (no persistent command):
npx @binaryandthread/cli browseA bare bt may collide with another binary on your PATH. If it does, this package also installs
btn as an alias, btn browse works identically. (npx @binaryandthread/cli … is always the
unambiguous form.)
The only package is the scoped
@binaryandthread/cli(signed, 2FA-published).npx bnt,npx bt,npx btn, andnpx binaryandthreadare different, unscoped packages owned by strangers — never run them. Always install the full scoped name.
Commands
| command | what it does |
| --- | --- |
| bt browse | the catalog as a numbered table (bt stage <n>); --pick opts into the ↑/↓ picker; --json for scripts |
| bt search <q> | filter the catalog |
| bt view <n\|slug> | one piece, specs + mockup (--colour black renders the black row) |
| bt stage <n\|slug> | add to your cart (--size, --colour) |
| bt blame <n\|slug> | the design's lore + sources (git blame, for motifs) |
| bt cart / bt unstage <ref> | show / remove a line |
| bt checkout | mint a checkout, hand off to the browser to pay, then poll |
| bt order <id> / bt log | track an order / your order history (when logged in) |
| bt login / bt whoami / bt logout | magic-link auth → JWT in your OS keychain |
| bt region | show your ship-to country + currency; bt region <cc> to set it (validated), bt region --list for every country we ship to |
| bt drops | terminal-visible drops |
| bt forget | wipe your local session + config (keychain token, cart, email) |
Config (all optional, env overrides the baked-in prod defaults)
| var | notes |
| --- | --- |
| BT_API_URL | Store API base URL (default: production; set for local dev / staging) |
| BT_PUBLISHABLE_KEY | the publishable key, public by design (a secret sk_ key is rejected) |
| NO_COLOR / --no-color | plain output, no ANSI / inline images |
| --json | machine-readable output for scripts / CI |
| --region <cc> | ship-to country for this command (ISO-2), overriding your saved region |
Region & currency. We ship internationally, so prices + shipping are shown in your ship-to
country's currency. Set it once with bt region <cc> (or bt config set region <cc>), override it
per-command with --region <cc>, and see the full list with bt region --list. Setting an
unshippable country is refused, and the money commands (stage / checkout) fail closed rather than
charge you in a substituted currency.
Your auth token is never stored in config, it lives in the OS keychain (bt login).
Local config (cart id, email, region, defaults) lives at ~/.config/bt/config.json.
Scripting: --json output & exit codes
Every command takes --json for stable, machine-readable output. bt checkout --json (or --yes)
skips the interactive confirm so it never hangs in CI. Field shapes (stable, safe to depend on):
| command | --json shape |
| --- | --- |
| bt browse / bt search | [{ id, handle, title, price, currency, colours[], channel_stage }] |
| bt view <handle> | { id, handle, title, price, currency, colours[], sizes[] } |
| bt cart | { cart_id, items: [{ id, title, colour, qty, total }], total, total_excludes_shipping } |
| bt checkout | { status: "placed"\|"timeout"\|"expired", order_id?, display_id?, total?, currency?, checkout_url? } |
| bt region | { region, currency, matched, home } · --list → { regions: [{ currency, countries[] }] } |
| bt whoami | { authenticated, tier, commits, standing_pct, token_expires_at, token_expired } |
| bt order <id> | { display_id, status, fulfillment_status?, tracking? } |
Errors (--json) come back as { "error": { "code", "type", "message" } } and the process
exits with a distinct code so CI/agents can branch:
| exit | type | meaning |
| --- | --- | --- |
| 0 | — | success |
| 65 | client_error / stale_cart | bad request / re-stage the cart |
| 69 | not_found | no such product/order |
| 75 | rate_limited / upstream | temporary, retry later (a 429 is auto-retried honoring Retry-After) |
| 77 | auth | not logged in / forbidden, run bt login |
| 1 | error | anything else |
Rate limits: the client auto-retries 429 responses, honoring the server's Retry-After
(capped at 10s) before falling back to jittered exponential backoff. A persistent 429 surfaces as
exit 75 / type: "rate_limited".
The MCP server mirrors these as structuredContent on every tool result (e.g. cart_id,
session_id, order_id, products[]) so an agent can chain tools without parsing prose.
