@binaryandthread/cli 0.3.2 → 0.4.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.
package/README.md CHANGED
@@ -33,8 +33,8 @@ unambiguous form.)
33
33
  | --- | --- |
34
34
  | `bt browse` | the catalog as a numbered table (`bt stage <n>`); `--pick` opts into the ↑/↓ picker; `--json` for scripts |
35
35
  | `bt search <q>` | filter the catalog |
36
- | `bt view <n\|slug>` | one piece, specs + mockup (`--colour black` renders the black row) |
37
- | `bt stage <n\|slug>` | add to your cart (`--size`, `--colour`) |
36
+ | `bt view <n\|slug>` | one piece, specs + mockup (`--colour black` renders the black row; `--fit oversize` shows the other fit of a two-fit design) |
37
+ | `bt stage <n\|slug>` | add to your cart (`--size`, `--colour`, `--fit regular\|oversize`; a design sold in both fits needs `--fit`, or `bt config set defaultFit …`) |
38
38
  | `bt blame <n\|slug>` | the design's lore + sources (git blame, for motifs) |
39
39
  | `bt cart` / `bt unstage <ref>` | show / remove a line |
40
40
  | `bt checkout` | mint a checkout, hand off to the browser to pay, then poll |
@@ -70,8 +70,8 @@ skips the interactive confirm so it never hangs in CI. Field shapes (stable, saf
70
70
 
71
71
  | command | `--json` shape |
72
72
  | --- | --- |
73
- | `bt browse` / `bt search` | `[{ id, handle, title, price, currency, colours[], channel_stage }]` |
74
- | `bt view <handle>` | `{ id, handle, title, price, currency, colours[], sizes[] }` |
73
+ | `bt browse` / `bt search` | `[{ id, handle, title, price, price_from, currency, colours[], fits[], channel_stage }]` (`price` is the cheapest fit when `price_from`) |
74
+ | `bt view <handle>` | `{ id, handle, title, fits[], fit, colours[], unavailable_colours[], specs, variants: [{ id, fit, colour, size, price, currency }] }` |
75
75
  | `bt cart` | `{ cart_id, items: [{ id, title, colour, qty, total }], total, total_excludes_shipping }` |
76
76
  | `bt checkout` | `{ status: "placed"\|"timeout"\|"expired", order_id?, display_id?, total?, currency?, checkout_url? }` |
77
77
  | `bt region` | `{ region, currency, matched, home }` · `--list` → `{ regions: [{ currency, countries[] }] }` |
package/dist/bin.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  forgetConfig,
6
6
  loadConfig,
7
7
  saveConfig
8
- } from "./chunk-RGFMXWD5.js";
8
+ } from "./chunk-T7MFWTB7.js";
9
9
  import {
10
10
  makePrompts
11
11
  } from "./chunk-AAN4VWP3.js";
@@ -27,7 +27,6 @@ import {
27
27
  empty,
28
28
  fail,
29
29
  failResult,
30
- firstPrice,
31
30
  fmtPrice,
32
31
  formatCartLine,
33
32
  formatFatal,
@@ -36,8 +35,10 @@ import {
36
35
  isCartTaxInclusive,
37
36
  lineColour,
38
37
  ok,
38
+ priceLine,
39
39
  printResult,
40
40
  productColours,
41
+ productFits,
41
42
  productLore,
42
43
  redact,
43
44
  regionLine,
@@ -53,7 +54,7 @@ import {
53
54
  versionLine,
54
55
  warn,
55
56
  working
56
- } from "./chunk-BFMH7VCE.js";
57
+ } from "./chunk-X3RWS3AG.js";
57
58
  import {
58
59
  __require
59
60
  } from "./chunk-7OCVIDC7.js";
@@ -102,7 +103,7 @@ function makeLogger(debug, write = (s) => process.stderr.write(s)) {
102
103
  }
103
104
 
104
105
  // src/version.ts
105
- var VERSION = "0.3.2";
106
+ var VERSION = "0.4.0";
106
107
 
107
108
  // src/eggs.ts
108
109
  function teapot() {
@@ -418,15 +419,18 @@ function truncate(s, w) {
418
419
  }
419
420
  function layoutRows(rows, style) {
420
421
  const cells = rows.map(({ p, stage }, i) => {
421
- const { amount, currency } = firstPrice(p);
422
+ const { amount, currency, from } = priceLine(p);
422
423
  const { title, handle } = displayName(p);
424
+ const fits = productFits(p);
425
+ const fitsMarker = fits.length > 1 ? fits.join(" \xB7 ") : "";
426
+ const colours = colourMarker(productColours(p));
423
427
  return {
424
428
  ref: `[${i + 1}]`,
425
429
  title,
426
- price: fmtPrice(amount, currency),
430
+ price: `${from ? "from " : ""}${fmtPrice(amount, currency)}`,
427
431
  handle,
428
432
  stage,
429
- colours: colourMarker(productColours(p))
433
+ colours: [colours, fitsMarker].filter(Boolean).join(" ")
430
434
  };
431
435
  });
432
436
  const refW = Math.max(...cells.map((c) => c.ref.length));
@@ -491,11 +495,14 @@ async function runBrowse(client, config, args) {
491
495
  if (drops.length > 0) {
492
496
  lines.push(" on next, before the web \u2192 bt drops");
493
497
  }
494
- const taxIncl = rows.length > 0 ? firstPrice(rows[0].p).taxInclusive : null;
498
+ const taxIncl = rows.length > 0 ? priceLine(rows[0].p).taxInclusive : null;
495
499
  if (taxIncl === true) {
496
500
  lines.push(dim(style, " prices incl. VAT"));
497
501
  }
498
502
  lines.push(" view one \u2192 bt view <n|slug> \xB7 stage one \u2192 bt stage <n|slug> --size L");
503
+ if (rows.some(({ p }) => productFits(p).length > 1)) {
504
+ lines.push(dim(style, " two fits? add --fit regular|oversize (or bt config set defaultFit \u2026)"));
505
+ }
499
506
  }
500
507
  return {
501
508
  lines,
@@ -503,9 +510,11 @@ async function runBrowse(client, config, args) {
503
510
  id: p.id,
504
511
  handle: p.handle,
505
512
  title: p.title,
506
- price: firstPrice(p).amount,
507
- currency: firstPrice(p).currency,
513
+ price: priceLine(p).amount,
514
+ price_from: priceLine(p).from,
515
+ currency: priceLine(p).currency,
508
516
  colours: productColours(p),
517
+ fits: productFits(p),
509
518
  channel_stage: p.metadata?.["channel_stage"] ?? "main"
510
519
  })) : void 0
511
520
  };
@@ -514,7 +523,7 @@ function registerBrowse(program, makeClient2, loadConfig2) {
514
523
  program.command("browse").alias("ls").description("list what's in stock on a branch.").option("--branch <branch>", "main (live) or next (terminal-only drops)").option("--pick", "opt into the arrow-key picker (default is the numbered table \u2014 use bt stage <n>)").option("--plain", "force the static table (the default; kept for back-compat)").action(async (opts) => {
515
524
  const { json, brand } = resolveOutput(program);
516
525
  if (opts.pick && !json && brand.color && process.stdout.isTTY && process.stdin.isTTY) {
517
- const { runInteractiveBrowse } = await import("./interactive-BOS6VMRJ.js");
526
+ const { runInteractiveBrowse } = await import("./interactive-VBATIHRD.js");
518
527
  const handled = await runInteractiveBrowse(makeClient2(), loadConfig2(), { branch: opts.branch, brand });
519
528
  if (handled) return;
520
529
  }
@@ -677,13 +686,14 @@ async function runSearch(client, config, args) {
677
686
  } else {
678
687
  lines.push(header(style, `bt search "${args.query}"`));
679
688
  for (const p of filtered) {
680
- const { amount, currency } = firstPrice(p);
689
+ const { amount, currency, from } = priceLine(p);
681
690
  const { title, handle } = displayName(p);
682
691
  const stage = p.metadata?.["channel_stage"] ?? "main";
683
- const colours = colourMarker(productColours(p));
692
+ const fits = productFits(p);
693
+ const colours = [colourMarker(productColours(p)), fits.length > 1 ? fits.join(" \xB7 ") : ""].filter(Boolean).join(" ");
684
694
  const row = [
685
695
  title.padEnd(22),
686
- cyan(style, fmtPrice(amount, currency).padEnd(11)),
696
+ cyan(style, `${from ? "from " : ""}${fmtPrice(amount, currency)}`.padEnd(11)),
687
697
  dim(style, handle.padEnd(34)),
688
698
  stageMarker(stage)
689
699
  ].join(" ").trimEnd();
@@ -698,9 +708,11 @@ async function runSearch(client, config, args) {
698
708
  id: p.id,
699
709
  handle: p.handle,
700
710
  title: p.title,
701
- price: firstPrice(p).amount,
702
- currency: firstPrice(p).currency,
703
- colours: productColours(p)
711
+ price: priceLine(p).amount,
712
+ price_from: priceLine(p).from,
713
+ currency: priceLine(p).currency,
714
+ colours: productColours(p),
715
+ fits: productFits(p)
704
716
  })) : void 0
705
717
  };
706
718
  }
@@ -1017,7 +1029,7 @@ function registerCheckout(program, makeClient2, loadConfig2) {
1017
1029
  const { makePrompts: makePrompts2 } = await import("./prompts-DDVV5RE6.js");
1018
1030
  const { openCheckout } = await import("./browser-WCNC4AWS.js");
1019
1031
  const { pollUntilPlaced } = await import("./poll-II3TVJLK.js");
1020
- const { saveConfig: saveConfig2 } = await import("./config-WV4DTQXU.js");
1032
+ const { saveConfig: saveConfig2 } = await import("./config-L4X7G563.js");
1021
1033
  const client = makeClient2();
1022
1034
  const print = json ? () => {
1023
1035
  } : (line) => process.stdout.write(line + "\n");
@@ -1724,7 +1736,7 @@ function registerDrop(program, exec) {
1724
1736
  }
1725
1737
 
1726
1738
  // src/commands/init.ts
1727
- var HAPPY_PATH = ["bt browse", "bt stage <handle> --size L", "bt checkout"];
1739
+ var HAPPY_PATH = ["bt browse", "bt stage <handle> --size L # add --fit regular|oversize for a two-fit design", "bt checkout"];
1728
1740
  var DOCS_URL = "https://binaryandthread.com/docs";
1729
1741
  function runInit(args) {
1730
1742
  const style = brandOf(args);
@@ -1735,7 +1747,7 @@ function runInit(args) {
1735
1747
  "",
1736
1748
  " the happy path:",
1737
1749
  " bt browse # the catalog, numbered",
1738
- " bt stage <n|slug> --size L # add to your cart (a browse number, or a slug)",
1750
+ " bt stage <n|slug> --size L # add to your cart (a browse number, or a slug; --fit for a two-fit design)",
1739
1751
  " bt checkout # pay in your browser \u2014 cards never touch the terminal (10% applied)",
1740
1752
  "",
1741
1753
  " ships worldwide \u2014 set your country so prices show your currency:",
@@ -1764,6 +1776,7 @@ async function runBuy(client, config, args, deps) {
1764
1776
  handle: args.handle,
1765
1777
  size: args.size,
1766
1778
  colour: args.colour,
1779
+ fit: args.fit,
1767
1780
  json: args.json,
1768
1781
  brand: args.brand
1769
1782
  });
@@ -1787,16 +1800,16 @@ async function runBuy(client, config, args, deps) {
1787
1800
  };
1788
1801
  }
1789
1802
  function registerBuy(program, makeClient2, loadConfig2) {
1790
- program.command("buy").argument("<handle>", "the design: a slug, a bt browse number (3), or a fuzzy hint (termnl)").option("--size <size>", "garment size").option("--colour <colour>", "colour, e.g. white / black").description("stage + checkout in one \u2014 reserve, then open browser checkout. cards never touch the terminal.").action(async (handle, opts) => {
1803
+ program.command("buy").argument("<handle>", "the design: a slug, a bt browse number (3), or a fuzzy hint (termnl)").option("--size <size>", "garment size").option("--colour <colour>", "colour, e.g. white / black").option("--fit <fit>", "regular or oversize (required when a design comes in both)").description("stage + checkout in one \u2014 reserve, then open browser checkout. cards never touch the terminal.").action(async (handle, opts) => {
1791
1804
  const { json, brand } = resolveOutput(program);
1792
1805
  const { makePrompts: makePrompts2 } = await import("./prompts-DDVV5RE6.js");
1793
1806
  const { openCheckout } = await import("./browser-WCNC4AWS.js");
1794
1807
  const { pollUntilPlaced } = await import("./poll-II3TVJLK.js");
1795
- const { saveConfig: saveConfig2 } = await import("./config-WV4DTQXU.js");
1808
+ const { saveConfig: saveConfig2 } = await import("./config-L4X7G563.js");
1796
1809
  const client = makeClient2();
1797
1810
  const print = json ? () => {
1798
1811
  } : (line) => process.stdout.write(line + "\n");
1799
- const res = await runBuy(client, loadConfig2(), { handle, size: opts.size, colour: opts.colour, json, brand }, {
1812
+ const res = await runBuy(client, loadConfig2(), { handle, size: opts.size, colour: opts.colour, fit: opts.fit, json, brand }, {
1800
1813
  prompts: makePrompts2(),
1801
1814
  open: (url) => openCheckout(url),
1802
1815
  // Same heartbeat as bt checkout — bt buy's browser-hop wait is the identical 20-min poll.
@@ -1817,7 +1830,7 @@ function registerBuy(program, makeClient2, loadConfig2) {
1817
1830
  }
1818
1831
 
1819
1832
  // src/commands/config.ts
1820
- var SETTABLE = ["email", "region", "defaultSize", "defaultColour", "defaultBranch"];
1833
+ var SETTABLE = ["email", "region", "defaultSize", "defaultColour", "defaultFit", "defaultBranch"];
1821
1834
  function runConfig(args, deps) {
1822
1835
  const cfg = deps.load();
1823
1836
  const sub = (args.sub ?? "get").toLowerCase();
@@ -1852,7 +1865,7 @@ function runConfig(args, deps) {
1852
1865
  function registerConfig(program) {
1853
1866
  program.command("config").argument("[sub]", "get | set", "get").argument("[key]", "config key").argument("[value]", "value (for set)").description("view or set local prefs (email, region, defaults). the auth token is never stored here.").action(async (sub, key, value) => {
1854
1867
  const { json } = resolveOutput(program);
1855
- const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await import("./config-WV4DTQXU.js");
1868
+ const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await import("./config-L4X7G563.js");
1856
1869
  const res = runConfig({ sub, key, value, json }, { load: loadConfig2, save: saveConfig2 });
1857
1870
  printResult(res, json);
1858
1871
  });