gearbox-code 0.2.4 → 0.2.5

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +391 -221
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -140309,8 +140309,139 @@ function StatusBar({
140309
140309
  }, undefined, true, undefined, this);
140310
140310
  }
140311
140311
 
140312
- // src/ui/components/CommandPalette.tsx
140312
+ // src/ui/components/StatusStrip.tsx
140313
140313
  var jsx_dev_runtime5 = __toESM(require_jsx_dev_runtime(), 1);
140314
+ function bar(leftPct, cells = 12) {
140315
+ const filled = Math.max(0, Math.min(cells, Math.round(leftPct / 100 * cells)));
140316
+ return "█".repeat(filled) + "░".repeat(cells - filled);
140317
+ }
140318
+ function fmtTok2(n) {
140319
+ return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
140320
+ }
140321
+ function StatusStrip({
140322
+ ctxPct,
140323
+ tokens,
140324
+ contextWindow,
140325
+ cost,
140326
+ sub,
140327
+ api: api2,
140328
+ width
140329
+ }) {
140330
+ const Row2 = ({ label, children }) => /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140331
+ children: [
140332
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140333
+ color: color.faint,
140334
+ children: [
140335
+ label.padEnd(8),
140336
+ " "
140337
+ ]
140338
+ }, undefined, true, undefined, this),
140339
+ children
140340
+ ]
140341
+ }, undefined, true, undefined, this);
140342
+ return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
140343
+ width,
140344
+ flexDirection: "column",
140345
+ paddingX: 1,
140346
+ marginTop: 1,
140347
+ children: [
140348
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
140349
+ justifyContent: "space-between",
140350
+ children: [
140351
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140352
+ color: color.accent,
140353
+ bold: true,
140354
+ children: "usage"
140355
+ }, undefined, false, undefined, this),
140356
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140357
+ color: color.faint,
140358
+ children: "/cost to hide"
140359
+ }, undefined, false, undefined, this)
140360
+ ]
140361
+ }, undefined, true, undefined, this),
140362
+ ctxPct != null ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Row2, {
140363
+ label: "context",
140364
+ children: [
140365
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140366
+ color: color.text,
140367
+ children: [
140368
+ 100 - ctxPct,
140369
+ "% left"
140370
+ ]
140371
+ }, undefined, true, undefined, this),
140372
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140373
+ color: color.faint,
140374
+ children: [
140375
+ " · ",
140376
+ fmtTok2(tokens),
140377
+ contextWindow ? ` / ${fmtTok2(contextWindow)}` : ""
140378
+ ]
140379
+ }, undefined, true, undefined, this)
140380
+ ]
140381
+ }, undefined, true, undefined, this) : null,
140382
+ sub?.limits?.map((l) => /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Row2, {
140383
+ label: l.label,
140384
+ children: [
140385
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140386
+ color: l.pct >= 90 ? color.err : color.accentDim,
140387
+ children: bar(100 - l.pct)
140388
+ }, undefined, false, undefined, this),
140389
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140390
+ color: l.pct >= 90 ? color.err : color.text,
140391
+ children: [
140392
+ " ",
140393
+ 100 - l.pct,
140394
+ "% left"
140395
+ ]
140396
+ }, undefined, true, undefined, this),
140397
+ l.resetsIn ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140398
+ color: color.faint,
140399
+ children: [
140400
+ " · ",
140401
+ l.resetsIn
140402
+ ]
140403
+ }, undefined, true, undefined, this) : null
140404
+ ]
140405
+ }, l.label, true, undefined, this)),
140406
+ sub && !sub.limits?.length ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Row2, {
140407
+ label: "limits",
140408
+ children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140409
+ color: color.faint,
140410
+ children: sub.limitNote ?? "not reported yet"
140411
+ }, undefined, false, undefined, this)
140412
+ }, undefined, false, undefined, this) : null,
140413
+ api2?.spend ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Row2, {
140414
+ label: api2.name.slice(0, 8),
140415
+ children: [
140416
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140417
+ color: api2.spendPos ? color.ok : color.faint,
140418
+ children: api2.spend
140419
+ }, undefined, false, undefined, this),
140420
+ api2.balanceLeft ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140421
+ color: color.faint,
140422
+ children: [
140423
+ " · ",
140424
+ api2.balanceLeft
140425
+ ]
140426
+ }, undefined, true, undefined, this) : null
140427
+ ]
140428
+ }, undefined, true, undefined, this) : null,
140429
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Row2, {
140430
+ label: "session",
140431
+ children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140432
+ color: cost >= 0.005 ? color.text : color.faint,
140433
+ children: [
140434
+ "$",
140435
+ cost.toFixed(2)
140436
+ ]
140437
+ }, undefined, true, undefined, this)
140438
+ }, undefined, false, undefined, this)
140439
+ ]
140440
+ }, undefined, true, undefined, this);
140441
+ }
140442
+
140443
+ // src/ui/components/CommandPalette.tsx
140444
+ var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
140314
140445
  function windowed(items, selected, limit) {
140315
140446
  const count = Math.max(1, limit);
140316
140447
  const safeSelected = Math.max(0, Math.min(selected, Math.max(items.length - 1, 0)));
@@ -140328,13 +140459,13 @@ function CommandPalette({ draft, selected = 0, limit = 5, rows, width = 80 }) {
140328
140459
  const rowWidth = Math.max(20, width - 2);
140329
140460
  if (rows?.length) {
140330
140461
  const shown2 = windowed(rows, selected, limit);
140331
- return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
140462
+ return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
140332
140463
  flexDirection: "column",
140333
140464
  paddingX: 1,
140334
140465
  marginTop: 1,
140335
140466
  children: shown2.rows.map((r2, i2) => {
140336
140467
  const active = shown2.start + i2 === selected;
140337
- return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140468
+ return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
140338
140469
  color: active ? color.text : color.dim,
140339
140470
  bold: active,
140340
140471
  backgroundColor: active ? color.accentBg : undefined,
@@ -140347,13 +140478,13 @@ function CommandPalette({ draft, selected = 0, limit = 5, rows, width = 80 }) {
140347
140478
  if (matches2.length === 0)
140348
140479
  return null;
140349
140480
  const shown = windowed(matches2, selected, limit);
140350
- return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
140481
+ return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
140351
140482
  flexDirection: "column",
140352
140483
  paddingX: 1,
140353
140484
  marginTop: 1,
140354
140485
  children: shown.rows.map((c, i2) => {
140355
140486
  const active = shown.start + i2 === selected;
140356
- return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
140487
+ return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
140357
140488
  color: active ? color.text : color.dim,
140358
140489
  bold: active,
140359
140490
  backgroundColor: active ? color.accentBg : undefined,
@@ -140364,7 +140495,7 @@ function CommandPalette({ draft, selected = 0, limit = 5, rows, width = 80 }) {
140364
140495
  }
140365
140496
 
140366
140497
  // src/ui/components/FilePalette.tsx
140367
- var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
140498
+ var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
140368
140499
  function windowed2(items, selected, limit) {
140369
140500
  const count = Math.max(1, limit);
140370
140501
  const safeSelected = Math.max(0, Math.min(selected, Math.max(items.length - 1, 0)));
@@ -140377,12 +140508,12 @@ function FilePalette({ matches: matches2, selected = 0, limit = 5, width = 80 })
140377
140508
  const rowWidth = Math.max(20, width - 2);
140378
140509
  if (shown.rows.length === 0)
140379
140510
  return null;
140380
- return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
140511
+ return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140381
140512
  flexDirection: "column",
140382
140513
  paddingX: 1,
140383
140514
  marginTop: 1,
140384
140515
  children: [
140385
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
140516
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140386
140517
  color: color.faint,
140387
140518
  children: "@ files · tab to complete"
140388
140519
  }, undefined, false, undefined, this),
@@ -140390,7 +140521,7 @@ function FilePalette({ matches: matches2, selected = 0, limit = 5, width = 80 })
140390
140521
  const active = shown.start + i2 === selected;
140391
140522
  const raw = `${active ? `${glyph.on} ` : " "}${f3}`;
140392
140523
  const text = raw.length > rowWidth ? raw.slice(0, Math.max(0, rowWidth - 1)) + "…" : raw.padEnd(rowWidth);
140393
- return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
140524
+ return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140394
140525
  color: active ? color.text : color.faint,
140395
140526
  bold: active,
140396
140527
  backgroundColor: active ? color.accentBg : undefined,
@@ -140669,7 +140800,7 @@ function applyMouse(s2, click) {
140669
140800
  }
140670
140801
 
140671
140802
  // src/ui/components/Composer.tsx
140672
- var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
140803
+ var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
140673
140804
  function Composer({
140674
140805
  value,
140675
140806
  cursor,
@@ -140701,15 +140832,15 @@ function Composer({
140701
140832
  const cursorHere = line === curLine;
140702
140833
  if (!hasSel) {
140703
140834
  if (!cursorHere)
140704
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140835
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140705
140836
  backgroundColor: color.panelBg,
140706
140837
  children: ln
140707
140838
  }, undefined, false, undefined, this);
140708
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140839
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140709
140840
  backgroundColor: color.panelBg,
140710
140841
  children: [
140711
140842
  ln.slice(0, curCol),
140712
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140843
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140713
140844
  inverse: true,
140714
140845
  backgroundColor: color.panelBg,
140715
140846
  children: ln[curCol] ?? " "
@@ -140718,11 +140849,11 @@ function Composer({
140718
140849
  ]
140719
140850
  }, undefined, true, undefined, this);
140720
140851
  }
140721
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140852
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140722
140853
  backgroundColor: color.panelBg,
140723
140854
  children: [
140724
140855
  ln.slice(0, selStart),
140725
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140856
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140726
140857
  inverse: true,
140727
140858
  children: ln.slice(selStart, selEnd)
140728
140859
  }, undefined, false, undefined, this),
@@ -140730,34 +140861,34 @@ function Composer({
140730
140861
  ]
140731
140862
  }, undefined, true, undefined, this);
140732
140863
  };
140733
- return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140864
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
140734
140865
  flexDirection: "column",
140735
140866
  width,
140736
140867
  marginTop: 1,
140737
140868
  children: [
140738
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140869
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
140739
140870
  paddingX: 1,
140740
- children: vim !== "off" ? /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(jsx_dev_runtime7.Fragment, {
140871
+ children: vim !== "off" ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(jsx_dev_runtime8.Fragment, {
140741
140872
  children: [
140742
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140873
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140743
140874
  color: vim === "normal" ? color.accent : color.dim,
140744
140875
  bold: true,
140745
140876
  children: vim === "normal" ? " NORMAL " : " INSERT "
140746
140877
  }, undefined, false, undefined, this),
140747
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140878
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140748
140879
  color: color.faint,
140749
140880
  children: glyph.rule.repeat(Math.max(width - 11, 4))
140750
140881
  }, undefined, false, undefined, this)
140751
140882
  ]
140752
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140883
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140753
140884
  color: color.faint,
140754
140885
  children: glyph.rule.repeat(Math.max(width - 2, 8))
140755
140886
  }, undefined, false, undefined, this)
140756
140887
  }, undefined, false, undefined, this),
140757
- busy ? /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140888
+ busy ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
140758
140889
  paddingX: 1,
140759
140890
  children: [
140760
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140891
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140761
140892
  color: color.faint,
140762
140893
  bold: true,
140763
140894
  backgroundColor: color.panelBg,
@@ -140766,16 +140897,16 @@ function Composer({
140766
140897
  " "
140767
140898
  ]
140768
140899
  }, undefined, true, undefined, this),
140769
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140900
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140770
140901
  color: color.faint,
140771
140902
  backgroundColor: color.panelBg,
140772
140903
  children: value || "…"
140773
140904
  }, undefined, false, undefined, this)
140774
140905
  ]
140775
- }, undefined, true, undefined, this) : value === "" ? /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140906
+ }, undefined, true, undefined, this) : value === "" ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
140776
140907
  paddingX: 1,
140777
140908
  children: [
140778
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140909
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140779
140910
  color: color.accent,
140780
140911
  bold: true,
140781
140912
  backgroundColor: color.panelBg,
@@ -140784,23 +140915,23 @@ function Composer({
140784
140915
  " "
140785
140916
  ]
140786
140917
  }, undefined, true, undefined, this),
140787
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140918
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140788
140919
  inverse: true,
140789
140920
  backgroundColor: color.panelBg,
140790
140921
  children: " "
140791
140922
  }, undefined, false, undefined, this),
140792
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140923
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140793
140924
  color: color.faint,
140794
140925
  backgroundColor: color.panelBg,
140795
140926
  children: suggestion ?? placeholder
140796
140927
  }, undefined, false, undefined, this)
140797
140928
  ]
140798
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140929
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
140799
140930
  flexDirection: "column",
140800
140931
  paddingX: 1,
140801
- children: lines.map((ln, i2) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
140932
+ children: lines.map((ln, i2) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
140802
140933
  children: [
140803
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
140934
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
140804
140935
  color: color.accent,
140805
140936
  bold: true,
140806
140937
  backgroundColor: color.panelBg,
@@ -141686,7 +141817,7 @@ function renderGhost(cfg) {
141686
141817
  }
141687
141818
 
141688
141819
  // src/ui/components/Mascot.tsx
141689
- var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
141820
+ var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
141690
141821
  var SKINS = ["base", "mint", "pink", "golden", "shades"];
141691
141822
  var SKIN_CFG = {
141692
141823
  base: { palette: "default", face: "happy" },
@@ -141708,35 +141839,35 @@ function SpriteRow({ row }) {
141708
141839
  j++;
141709
141840
  const n = j - i2;
141710
141841
  if (t2 && b)
141711
- spans.push(/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141842
+ spans.push(/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141712
141843
  color: t2,
141713
141844
  backgroundColor: b,
141714
141845
  children: "▀".repeat(n)
141715
141846
  }, i2, false, undefined, this));
141716
141847
  else if (t2)
141717
- spans.push(/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141848
+ spans.push(/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141718
141849
  color: t2,
141719
141850
  children: "▀".repeat(n)
141720
141851
  }, i2, false, undefined, this));
141721
141852
  else if (b)
141722
- spans.push(/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141853
+ spans.push(/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141723
141854
  color: b,
141724
141855
  children: "▄".repeat(n)
141725
141856
  }, i2, false, undefined, this));
141726
141857
  else
141727
- spans.push(/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141858
+ spans.push(/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141728
141859
  children: " ".repeat(n)
141729
141860
  }, i2, false, undefined, this));
141730
141861
  i2 = j;
141731
141862
  }
141732
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141863
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141733
141864
  children: spans
141734
141865
  }, undefined, false, undefined, this);
141735
141866
  }
141736
141867
  function Sprite({ data }) {
141737
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141868
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141738
141869
  flexDirection: "column",
141739
- children: data.map((row, i2) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(SpriteRow, {
141870
+ children: data.map((row, i2) => /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(SpriteRow, {
141740
141871
  row
141741
141872
  }, i2, false, undefined, this))
141742
141873
  }, undefined, false, undefined, this);
@@ -141764,9 +141895,9 @@ function AnimatedGhost({ cfg, scale, anim }) {
141764
141895
  frameCfg.overlay = { kind: anim.overlay, frame: slow };
141765
141896
  const data = import_react23.useMemo(() => renderGhost(frameCfg), [JSON.stringify(frameCfg)]);
141766
141897
  const shake = anim.shake ? tick % 2 : 0;
141767
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141898
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141768
141899
  marginLeft: shake,
141769
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Sprite, {
141900
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Sprite, {
141770
141901
  data
141771
141902
  }, undefined, false, undefined, this)
141772
141903
  }, undefined, false, undefined, this);
@@ -141775,9 +141906,9 @@ function KittyGhost({ variant, size: size2 }) {
141775
141906
  const data = GHOSTS[variant][size2];
141776
141907
  const id = idColor(imageId(variant, size2));
141777
141908
  const lines = placeholderRows(data[0]?.length ?? 0, data.length);
141778
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141909
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141779
141910
  flexDirection: "column",
141780
- children: lines.map((l, i2) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141911
+ children: lines.map((l, i2) => /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141781
141912
  color: id,
141782
141913
  children: l
141783
141914
  }, i2, false, undefined, this))
@@ -141785,29 +141916,29 @@ function KittyGhost({ variant, size: size2 }) {
141785
141916
  }
141786
141917
  function MascotSplash({ skin = "base", size: size2 = "big" }) {
141787
141918
  const kitty = getImageMode() === "kitty";
141788
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141919
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141789
141920
  flexDirection: "column",
141790
141921
  alignItems: "center",
141791
141922
  marginTop: 1,
141792
141923
  children: [
141793
- size2 !== "none" ? kitty ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(KittyGhost, {
141924
+ size2 !== "none" ? kitty ? /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(KittyGhost, {
141794
141925
  variant: skin,
141795
141926
  size: size2
141796
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(AnimatedGhost, {
141927
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(AnimatedGhost, {
141797
141928
  cfg: skinToCfg(skin),
141798
141929
  scale: size2 === "big" ? 2 : 1,
141799
141930
  anim: { blink: true }
141800
141931
  }, undefined, false, undefined, this) : null,
141801
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141932
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141802
141933
  marginTop: size2 === "none" ? 0 : 1,
141803
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141934
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141804
141935
  color: color.accent,
141805
141936
  bold: true,
141806
141937
  children: "gearbox"
141807
141938
  }, undefined, false, undefined, this)
141808
141939
  }, undefined, false, undefined, this),
141809
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
141810
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
141940
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141941
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141811
141942
  color: color.dim,
141812
141943
  children: "one ghost · every model"
141813
141944
  }, undefined, false, undefined, this)
@@ -141817,9 +141948,9 @@ function MascotSplash({ skin = "base", size: size2 = "big" }) {
141817
141948
  }
141818
141949
 
141819
141950
  // src/ui/components/PermissionPrompt.tsx
141820
- var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
141951
+ var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
141821
141952
  function PermissionPrompt({ req, width }) {
141822
- return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141953
+ return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
141823
141954
  flexDirection: "column",
141824
141955
  width,
141825
141956
  marginTop: 1,
@@ -141827,9 +141958,9 @@ function PermissionPrompt({ req, width }) {
141827
141958
  borderStyle: "round",
141828
141959
  borderColor: color.accent,
141829
141960
  children: [
141830
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141961
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
141831
141962
  children: [
141832
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141963
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141833
141964
  color: color.accent,
141834
141965
  bold: true,
141835
141966
  children: [
@@ -141837,7 +141968,7 @@ function PermissionPrompt({ req, width }) {
141837
141968
  " permission"
141838
141969
  ]
141839
141970
  }, undefined, true, undefined, this),
141840
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141971
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141841
141972
  color: color.faint,
141842
141973
  children: [
141843
141974
  " ",
@@ -141846,39 +141977,39 @@ function PermissionPrompt({ req, width }) {
141846
141977
  }, undefined, true, undefined, this)
141847
141978
  ]
141848
141979
  }, undefined, true, undefined, this),
141849
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141980
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
141850
141981
  marginTop: 1,
141851
- children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141982
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
141852
141983
  flexGrow: 1,
141853
- children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141984
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141854
141985
  color: color.text,
141855
141986
  wrap: "truncate-end",
141856
141987
  children: req.detail
141857
141988
  }, undefined, false, undefined, this)
141858
141989
  }, undefined, false, undefined, this)
141859
141990
  }, undefined, false, undefined, this),
141860
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
141991
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
141861
141992
  marginTop: 1,
141862
141993
  children: [
141863
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141994
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141864
141995
  color: color.accent,
141865
141996
  bold: true,
141866
141997
  children: "1"
141867
141998
  }, undefined, false, undefined, this),
141868
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
141999
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141869
142000
  color: color.dim,
141870
142001
  children: " once"
141871
142002
  }, undefined, false, undefined, this),
141872
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142003
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141873
142004
  color: color.faint,
141874
142005
  children: " "
141875
142006
  }, undefined, false, undefined, this),
141876
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142007
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141877
142008
  color: color.accent,
141878
142009
  bold: true,
141879
142010
  children: "2"
141880
142011
  }, undefined, false, undefined, this),
141881
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142012
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141882
142013
  color: color.dim,
141883
142014
  children: [
141884
142015
  " always (",
@@ -141886,33 +142017,33 @@ function PermissionPrompt({ req, width }) {
141886
142017
  ")"
141887
142018
  ]
141888
142019
  }, undefined, true, undefined, this),
141889
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142020
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141890
142021
  color: color.faint,
141891
142022
  children: " "
141892
142023
  }, undefined, false, undefined, this),
141893
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142024
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141894
142025
  color: color.accent,
141895
142026
  bold: true,
141896
142027
  children: "a"
141897
142028
  }, undefined, false, undefined, this),
141898
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142029
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141899
142030
  color: color.dim,
141900
142031
  children: " all · yolo"
141901
142032
  }, undefined, false, undefined, this),
141902
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142033
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141903
142034
  color: color.faint,
141904
142035
  children: " "
141905
142036
  }, undefined, false, undefined, this),
141906
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142037
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141907
142038
  color: color.err,
141908
142039
  bold: true,
141909
142040
  children: "3"
141910
142041
  }, undefined, false, undefined, this),
141911
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142042
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141912
142043
  color: color.dim,
141913
142044
  children: " deny"
141914
142045
  }, undefined, false, undefined, this),
141915
- /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
142046
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
141916
142047
  color: color.faint,
141917
142048
  children: " · esc"
141918
142049
  }, undefined, false, undefined, this)
@@ -141923,7 +142054,7 @@ function PermissionPrompt({ req, width }) {
141923
142054
  }
141924
142055
 
141925
142056
  // src/ui/components/Working.tsx
141926
- var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
142057
+ var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
141927
142058
  var THINK_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
141928
142059
  var STREAM_FRAMES = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▂"];
141929
142060
  var TOOL_FRAMES = ["◐", "◓", "◑", "◒"];
@@ -141957,16 +142088,16 @@ function Working({
141957
142088
  const spinner = linger ? "●" : spinFrame(state);
141958
142089
  const f3 = Math.floor(Date.now() / 360);
141959
142090
  const dots = ["", ".", "..", "..."][f3 % 4];
141960
- return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
142091
+ return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
141961
142092
  width,
141962
142093
  paddingX: 1,
141963
142094
  marginTop: 1,
141964
142095
  justifyContent: "space-between",
141965
142096
  children: [
141966
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142097
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141967
142098
  color: labelColor,
141968
142099
  children: [
141969
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142100
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141970
142101
  color: dotColor,
141971
142102
  children: [
141972
142103
  spinner,
@@ -141974,22 +142105,22 @@ function Working({
141974
142105
  ]
141975
142106
  }, undefined, true, undefined, this),
141976
142107
  label,
141977
- !linger ? /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142108
+ !linger ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141978
142109
  color: color.accentDim,
141979
142110
  children: dots
141980
142111
  }, undefined, false, undefined, this) : null
141981
142112
  ]
141982
142113
  }, undefined, true, undefined, this),
141983
- !linger ? /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142114
+ !linger ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141984
142115
  children: [
141985
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142116
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141986
142117
  color: color.accentDim,
141987
142118
  children: [
141988
142119
  elapsed,
141989
142120
  "s"
141990
142121
  ]
141991
142122
  }, undefined, true, undefined, this),
141992
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142123
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141993
142124
  color: color.faint,
141994
142125
  children: [
141995
142126
  tps >= 5 ? ` · ~${tps} tok/s` : "",
@@ -141997,7 +142128,7 @@ function Working({
141997
142128
  ]
141998
142129
  }, undefined, true, undefined, this)
141999
142130
  ]
142000
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
142131
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142001
142132
  color: color.faint,
142002
142133
  children: " "
142003
142134
  }, undefined, false, undefined, this)
@@ -142006,7 +142137,7 @@ function Working({
142006
142137
  }
142007
142138
 
142008
142139
  // src/ui/components/Viewport.tsx
142009
- var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
142140
+ var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
142010
142141
  function normalized(sel) {
142011
142142
  if (!sel)
142012
142143
  return null;
@@ -142024,7 +142155,7 @@ function selectedRangeForLine(sel, absLine) {
142024
142155
  }
142025
142156
  function LineRow({ line, absLine, selection, lineWidth }) {
142026
142157
  if (line.length === 0) {
142027
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142158
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142028
142159
  backgroundColor: color.navy,
142029
142160
  children: " ".repeat(lineWidth)
142030
142161
  }, undefined, false, undefined, this);
@@ -142033,7 +142164,7 @@ function LineRow({ line, absLine, selection, lineWidth }) {
142033
142164
  let pos = 0;
142034
142165
  const lineLen = line.reduce((n, s2) => n + s2.text.length, 0);
142035
142166
  const trailing = Math.max(0, lineWidth - lineLen);
142036
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142167
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142037
142168
  children: [
142038
142169
  line.flatMap((s2, j) => {
142039
142170
  const start = pos;
@@ -142041,7 +142172,7 @@ function LineRow({ line, absLine, selection, lineWidth }) {
142041
142172
  pos = end;
142042
142173
  if (!range2 || end <= range2[0] || start >= range2[1]) {
142043
142174
  return [
142044
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142175
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142045
142176
  color: s2.color,
142046
142177
  bold: s2.bold,
142047
142178
  italic: s2.italic,
@@ -142054,7 +142185,7 @@ function LineRow({ line, absLine, selection, lineWidth }) {
142054
142185
  const a = Math.max(range2[0] - start, 0);
142055
142186
  const b = Math.min(range2[1] - start, s2.text.length);
142056
142187
  return [
142057
- s2.text.slice(0, a) ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142188
+ s2.text.slice(0, a) ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142058
142189
  color: s2.color,
142059
142190
  bold: s2.bold,
142060
142191
  italic: s2.italic,
@@ -142062,11 +142193,11 @@ function LineRow({ line, absLine, selection, lineWidth }) {
142062
142193
  backgroundColor: s2.bg ?? color.navy,
142063
142194
  children: s2.text.slice(0, a)
142064
142195
  }, `${j}-a`, false, undefined, this) : null,
142065
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142196
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142066
142197
  inverse: true,
142067
142198
  children: s2.text.slice(a, b)
142068
142199
  }, `${j}-b`, false, undefined, this),
142069
- s2.text.slice(b) ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142200
+ s2.text.slice(b) ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142070
142201
  color: s2.color,
142071
142202
  bold: s2.bold,
142072
142203
  italic: s2.italic,
@@ -142076,7 +142207,7 @@ function LineRow({ line, absLine, selection, lineWidth }) {
142076
142207
  }, `${j}-c`, false, undefined, this) : null
142077
142208
  ].filter(Boolean);
142078
142209
  }),
142079
- trailing > 0 ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142210
+ trailing > 0 ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142080
142211
  backgroundColor: color.navy,
142081
142212
  children: " ".repeat(trailing)
142082
142213
  }, undefined, false, undefined, this) : null
@@ -142093,25 +142224,25 @@ function Viewport({ lines, scrollTop, height, width, selection }) {
142093
142224
  const thumb = hasBar ? Math.max(1, Math.round(height / total * height)) : 0;
142094
142225
  const maxTop = Math.max(1, total - height);
142095
142226
  const thumbStart = hasBar ? Math.min(height - thumb, Math.round(scrollTop / maxTop * (height - thumb))) : 0;
142096
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
142227
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
142097
142228
  width,
142098
142229
  children: [
142099
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
142230
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
142100
142231
  flexDirection: "column",
142101
142232
  width: width - 1,
142102
- children: padded.map((l, i2) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(LineRow, {
142233
+ children: padded.map((l, i2) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(LineRow, {
142103
142234
  line: l,
142104
142235
  absLine: scrollTop + i2,
142105
142236
  selection,
142106
142237
  lineWidth: width - 1
142107
142238
  }, i2, false, undefined, this))
142108
142239
  }, undefined, false, undefined, this),
142109
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
142240
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
142110
142241
  flexDirection: "column",
142111
142242
  width: 1,
142112
142243
  children: Array.from({ length: height }, (_, i2) => {
142113
142244
  const on = hasBar && i2 >= thumbStart && i2 < thumbStart + thumb;
142114
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
142245
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
142115
142246
  color: on ? color.accentDim : color.faint,
142116
142247
  children: on ? "┃" : hasBar ? "│" : " "
142117
142248
  }, i2, false, undefined, this);
@@ -142366,8 +142497,8 @@ function blockLines(tok, width) {
142366
142497
  }
142367
142498
  case "blockquote": {
142368
142499
  const inner = (tok.tokens ?? []).flatMap((t2) => blockLines(t2, Math.max(width - 2, 1)));
142369
- const bar = { text: glyph.userBar + " ", color: color.accentDim };
142370
- return inner.map((l) => [bar, ...l]);
142500
+ const bar2 = { text: glyph.userBar + " ", color: color.accentDim };
142501
+ return inner.map((l) => [bar2, ...l]);
142371
142502
  }
142372
142503
  case "hr":
142373
142504
  return [[{ text: glyph.rule.repeat(Math.min(width, 24)), color: color.faint }]];
@@ -143187,14 +143318,14 @@ class RoutingSelector {
143187
143318
  }
143188
143319
  prepare(task) {
143189
143320
  const kind = task.kind ?? classify(task.prompt);
143190
- const bar = BAR[kind];
143321
+ const bar2 = BAR[kind];
143191
143322
  const required2 = task.requires ?? [];
143192
143323
  const ctx = buildRoutingContext(ctx_now());
143193
143324
  const estInputTokens = task.estTokens || NOMINAL_INPUT_TOKENS;
143194
143325
  const all = this.enumerate(ctx);
143195
143326
  if (all.length === 0) {
143196
143327
  const m2 = pickDefaultModel(this.fallbackId);
143197
- return { kind, bar, required: required2, ctx, pool: [], clears: [], estInputTokens, fallback: m2 ?? undefined };
143328
+ return { kind, bar: bar2, required: required2, ctx, pool: [], clears: [], estInputTokens, fallback: m2 ?? undefined };
143198
143329
  }
143199
143330
  const capable = required2.length ? all.filter((c) => supportsRequirements(c.spec, required2)) : all;
143200
143331
  if (capable.length === 0) {
@@ -143205,8 +143336,8 @@ class RoutingSelector {
143205
143336
  const fits = need > 0 ? capable.filter((c) => c.spec.contextWindow >= need) : capable;
143206
143337
  let pool = fits.length ? fits : capable;
143207
143338
  pool = applyGlobalPreference(pool);
143208
- const clears = pool.filter((c) => qualityOf(c) >= bar);
143209
- return { kind, bar, required: required2, ctx, pool, clears, estInputTokens };
143339
+ const clears = pool.filter((c) => qualityOf(c) >= bar2);
143340
+ return { kind, bar: bar2, required: required2, ctx, pool, clears, estInputTokens };
143210
143341
  }
143211
143342
  preferredIn(kind, candidates) {
143212
143343
  const pref = preferenceFor(kind);
@@ -143406,7 +143537,7 @@ function backspaceFilter(panel) {
143406
143537
  }
143407
143538
 
143408
143539
  // src/ui/components/Panel.tsx
143409
- var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
143540
+ var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
143410
143541
  function accountStateColor3(status) {
143411
143542
  if (status === "active")
143412
143543
  return color.ok;
@@ -143431,9 +143562,9 @@ function Panel({
143431
143562
  const lines = staticLines ?? itemsToLines(panel.items, innerW);
143432
143563
  const maxScroll = Math.max(0, lines.length - bodyH);
143433
143564
  const scroll = Math.min(panel.scroll, maxScroll);
143434
- body = /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143565
+ body = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143435
143566
  paddingX: 1,
143436
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Viewport, {
143567
+ children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Viewport, {
143437
143568
  lines,
143438
143569
  scrollTop: scroll,
143439
143570
  height: bodyH,
@@ -143447,48 +143578,48 @@ function Panel({
143447
143578
  const start = windowStart(idx, rows.length, bodyH);
143448
143579
  const slice2 = rows.slice(start, start + bodyH);
143449
143580
  const labelPad = accounts?.labelPad ?? 0;
143450
- body = /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143581
+ body = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143451
143582
  flexDirection: "column",
143452
143583
  paddingX: 1,
143453
- children: rows.length === 0 ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143584
+ children: rows.length === 0 ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143454
143585
  color: color.faint,
143455
143586
  children: "no accounts yet · /account add to add one"
143456
143587
  }, undefined, false, undefined, this) : slice2.map((r2, i2) => {
143457
143588
  const sel = start + i2 === idx;
143458
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143589
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143459
143590
  backgroundColor: sel ? color.accentBg : undefined,
143460
143591
  children: [
143461
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143592
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143462
143593
  color: sel ? color.accent : color.faint,
143463
143594
  children: sel ? "▶ " : " "
143464
143595
  }, undefined, false, undefined, this),
143465
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143596
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143466
143597
  color: color.text,
143467
143598
  bold: r2.active,
143468
143599
  children: r2.name.padEnd(labelPad)
143469
143600
  }, undefined, false, undefined, this),
143470
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143601
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143471
143602
  color: color.faint,
143472
143603
  children: [
143473
143604
  " ",
143474
143605
  r2.type
143475
143606
  ]
143476
143607
  }, undefined, true, undefined, this),
143477
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143608
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143478
143609
  color: accountStateColor3(r2.status),
143479
143610
  children: [
143480
143611
  " ",
143481
143612
  r2.status
143482
143613
  ]
143483
143614
  }, undefined, true, undefined, this),
143484
- r2.detail ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143615
+ r2.detail ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143485
143616
  color: color.faint,
143486
143617
  children: [
143487
143618
  " · ",
143488
143619
  r2.detail
143489
143620
  ]
143490
143621
  }, undefined, true, undefined, this) : null,
143491
- r2.type === "subscription" && !(r2.detail && r2.detail.includes("@")) ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143622
+ r2.type === "subscription" && !(r2.detail && r2.detail.includes("@")) ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143492
143623
  color: color.accentDim,
143493
143624
  children: [
143494
143625
  " · /account login ",
@@ -143496,7 +143627,7 @@ function Panel({
143496
143627
  " to identify"
143497
143628
  ]
143498
143629
  }, undefined, true, undefined, this) : null,
143499
- r2.active ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143630
+ r2.active ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143500
143631
  color: color.ok,
143501
143632
  children: [
143502
143633
  " ",
@@ -143514,10 +143645,10 @@ function Panel({
143514
143645
  const idx = clampIndex(panel.index, rows.length);
143515
143646
  const start = windowStart(idx, rows.length, bodyH);
143516
143647
  const slice2 = rows.slice(start, start + bodyH);
143517
- body = /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143648
+ body = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143518
143649
  flexDirection: "column",
143519
143650
  paddingX: 1,
143520
- children: rows.length === 0 ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143651
+ children: rows.length === 0 ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143521
143652
  color: color.faint,
143522
143653
  children: [
143523
143654
  "no models match “",
@@ -143527,23 +143658,23 @@ function Panel({
143527
143658
  }, undefined, true, undefined, this) : slice2.map((r2, i2) => {
143528
143659
  const sel = start + i2 === idx;
143529
143660
  const pinned = r2.id === currentModelId;
143530
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143661
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143531
143662
  backgroundColor: sel ? color.accentBg : undefined,
143532
143663
  children: [
143533
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143664
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143534
143665
  color: sel ? color.accent : color.faint,
143535
143666
  children: sel ? "▶ " : " "
143536
143667
  }, undefined, false, undefined, this),
143537
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143668
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143538
143669
  color: pinned ? color.ok : color.text,
143539
143670
  bold: pinned,
143540
143671
  children: r2.label.padEnd(22)
143541
143672
  }, undefined, false, undefined, this),
143542
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143673
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143543
143674
  color: color.faint,
143544
143675
  children: r2.provider
143545
143676
  }, undefined, false, undefined, this),
143546
- pinned ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143677
+ pinned ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143547
143678
  color: color.ok,
143548
143679
  children: [
143549
143680
  " ",
@@ -143557,37 +143688,37 @@ function Panel({
143557
143688
  }, undefined, false, undefined, this);
143558
143689
  hint = `filter: ${panel.filter || "(type to filter)"} · ↑↓ · ⏎ pin · esc close`;
143559
143690
  }
143560
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143691
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143561
143692
  flexDirection: "column",
143562
143693
  width,
143563
143694
  height,
143564
143695
  children: [
143565
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143696
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143566
143697
  width,
143567
143698
  paddingX: 1,
143568
143699
  justifyContent: "space-between",
143569
143700
  children: [
143570
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143701
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143571
143702
  color: color.accent,
143572
143703
  bold: true,
143573
143704
  children: panel.title
143574
143705
  }, undefined, false, undefined, this),
143575
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143706
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143576
143707
  color: color.faint,
143577
143708
  children: "esc to close"
143578
143709
  }, undefined, false, undefined, this)
143579
143710
  ]
143580
143711
  }, undefined, true, undefined, this),
143581
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143712
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143582
143713
  flexDirection: "column",
143583
143714
  width,
143584
143715
  height: bodyH,
143585
143716
  children: body
143586
143717
  }, undefined, false, undefined, this),
143587
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
143718
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
143588
143719
  width,
143589
143720
  paddingX: 1,
143590
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
143721
+ children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
143591
143722
  color: color.faint,
143592
143723
  children: hint
143593
143724
  }, undefined, false, undefined, this)
@@ -145518,7 +145649,7 @@ bun run typecheck
145518
145649
  },
145519
145650
  {
145520
145651
  file: "CLAUDE.md",
145521
- text: "# Gearbox — project guide\n\nGearbox is a multi-provider coding harness for the terminal: a beautiful, simple terminal agent that reads/writes code and runs commands, talking to any provider (Anthropic, OpenAI, Google, DeepSeek) through one clean loop.\n\n**The point of the project:** intelligent per-task *model routing* — automatically picking the right model for each task across every provider and account you pay for. Basic routing is live (`RoutingSelector` — classify → quality bar → cheapest winner); the richer engine (shadow-eval, credit/limit penalties, confidence display) layers on top of the same seam. See `DESIGN.md` for the full vision and `experiments/FINDINGS.md` for the validation behind it.\n\n## The one rule that matters\n\n**Keep the routing seam clean.** The agent must never hardcode a model. It asks a `ModelSelector` for the model to use. `RoutingSelector` is the live default (classify task → filter by quality bar → cheapest winner); `FixedSelector` is used only when a model is explicitly pinned (`--model` flag or `/model <name>`). Concretely:\n\n- `src/model/selector.ts` — the seam. `select(task) => ModelChoice` (now carrying an optional `Backend` so the runner can dispatch in-loop vs a subscription seat). Do not bypass it.\n- `src/model/router.ts` — `RoutingSelector` is **account-aware**: it scores `(model, account)` PAIRS, not just models. Candidates = in-loop registry models × the accounts that serve them + flat-rate subscription **seats** (`providers.subscriptionSeats()`, a seat mirrors a canonical model but runs via the vendor binary). Flow: classify → quality bar → context fit → global/`/prefer` preference filter → score → return `{model, reason, backend}`. A seat is ~free until its rate limit, so it wins by default and fails over to metered API as the window fills. `SubscriptionPinSelector`/`FixedSelector` are hard pins (explicit `/account use` or `/model`) that beat auto-routing.\n- `src/model/scoring.ts` — the PURE scorer: `score = costEst + scarcity + switchPenalty + limitPenalty + apiThrottlePenalty − planBonus`, argmin tie-broken deterministically. No I/O; fixture-tested. Every account-state term degrades safely — and where a provider exposes nothing, we ESTIMATE rather than go blind: live API rate-limit headers (`src/model/rate-headers.ts`, parsed from each response → `apiThrottle`, gentle near-empty penalty) and a self-declared budget − tracked spend (`/budget`, an estimated balance feeding scarcity). A missing signal with no estimate is still neutral (no errors per provider).\n- `src/model/routing-context.ts` — `buildRoutingContext()`: the per-turn account-state snapshot (balance where exposed, subscription rate headroom = min over the 5h/weekly windows) read from disk-cached `usage.json`. No network on the hot path; balances refreshed in the background (App effect).\n- `src/model/profiles.ts` — the data corpus: quality, cost, latency, tokenizer calibration, **and the per-model effort vocabulary** (`efforts`) per the provider research. Routing reads this; effort is clamped/omitted against the chosen model's set, never sent unsupported.\n- `src/providers.ts` — maps a provider+model id to an AI SDK model instance. Already multi-provider. Adding a model is data, not code.\n- Every model call captures token usage (`src/agent/run.ts`) so the cost engine has data. Do not drop usage.\n- The UI consumes a normalized `AgentEvent` stream (`src/agent/events.ts`), never the AI SDK's raw types. This decouples the UI from the provider layer and from routing.\n\nIf you find yourself writing `anthropic('claude-...')` anywhere outside `providers.ts`, stop — route it through the selector.\n\n## Layout\n\n```\nsrc/\n cli.tsx entry point; renders the Ink app; picks RoutingSelector by default\n config.ts minimal config (default model, provider from env)\n providers.ts provider+model id -> AI SDK model (multi-provider; contextWindow per model)\n commands.ts slash-command metadata + pure helpers (fuzzy model match, /help, model list)\n tools.ts read / write / edit / list / search / glob / run_shell (AI SDK tools)\n model/\n selector.ts THE ROUTING SEAM — ModelSelector + ModelChoice.backend + FixedSelector (pinned model)\n router.ts RoutingSelector (account-aware): scores (model, account) pairs incl. subscription seats; SubscriptionPinSelector\n scoring.ts PURE scorer: cost + scarcity + limit − plan bonus; deterministic, fixture-tested\n routing-context.ts per-turn account-state snapshot (balance + subscription rate headroom) from usage.json\n cooldown.ts reactive-failover support: classify a failure + park an exhausted account so the router routes around it\n profiles.ts model corpus: quality (SWE-bench), cost ($/Mtok), latency, tokenizer calibration, per-model effort vocab\n tokens.ts calibrated token counting (js-tiktoken × per-model calibration factor)\n preferences.ts persist /prefer kind model choices to ~/.gearbox/routing-preferences.json\n reasoning.ts reasoning/thinking config helpers\n context/\n builder.ts context engine: system + memory + repo map + retrieved files + curated history\n retrieve.ts BM25 lexical retrieval — top-K relevant files for a prompt (no model call)\n repomap.ts repo structure summary for the system prompt\n memory.ts project memory (GEARBOX.md / CLAUDE.md loaded into context)\n compact.ts context compaction (/compact)\n accounts/\n types.ts Account + AuthMethod types (API key, AWS, Azure, Vertex, CLI, OpenAI-compat)\n store.ts accounts.json persistence (~/.gearbox/accounts.json)\n catalog.ts provider catalog (known providers, env vars, labels)\n detect.ts auto-detect env creds + cloud credentials\n onboard.ts interactive add/test account flows; addByPastedKey routes through sniff.ts\n sniff.ts pure credential sniffer: paste anything (key / AWS block / SA JSON / Azure URL / gateway key) → {kind, provider, fields, missing}\n resolve.ts credential resolution (Account → ResolvedCreds) + rank(model) → ordered failover pool (cross-provider, health-sorted)\n discover.ts per-account model discovery (Azure deployments / Foundry / gateway /models) → account.models; catalog defaultModels are seeds, not callable ids\n health.ts account health: classifyError (provider error → state), checkHealth (cached probe, timeout-bounded), recordHealth; no background polling\n usage.ts per-account spend ledger + rate-limit snapshots + balance tracking\n balance.ts provider balance fetch helpers\n help/\n ask.ts /ask corpus: bundled docs + generated command reference, system prompt, meta-question auto-detect\n agent/\n events.ts AgentEvent — normalized stream the UI consumes\n run.ts real agent loop (AI SDK streamText -> AgentEvent), abort-aware; runCompletion = tool-less grounded answer (used by /ask); returns a structured failure (for failover)\n failover.ts runWithFailover: run a turn over the ranked account pool; on a credential failure before output, advance to the next; clear exhaustion errors\n cli-backend.ts claude/codex CLI subprocess backend (for Pro/Max subscriptions)\n mock.ts scripted demo stream (runs with no API key; used by tests)\n ui/\n theme.ts colors + glyphs (the look)\n input.ts pure key→action reducer for the composer (tested)\n history.ts pure ↑/↓ prompt-history nav (tested)\n net.ts background online probe; status bar shows ⚠ offline when down\n useTerminalSize.ts reactive width on resize (everything reflows)\n git.ts current branch for the status line\n App.tsx the Ink app: state, useInput dispatch, commands, turns\n components/ Banner, Transcript, Composer, CommandPalette, StatusBar, PermissionPrompt, Panel\n panel.ts dismissable command-panel model + pure helpers (clamp/window/filter); tested\ntest/ pure-logic + render tests (ink-testing-library); no keys\nDESIGN.md full product vision (routing, requirements, UX)\nexperiments/ prototypes that validated the architecture\n```\n\nThe composer is custom (Ink `useInput` + `src/ui/input.ts`), not a third-party widget — full control over the cursor, ↑/↓ history, and esc-to-interrupt, with no focus/remount fragility. **Multi-line**: ⌃J (or shift/alt+⏎) inserts a newline, ⏎ submits; ↑/↓ move between lines and fall through to history at the top/bottom line; bracketed paste (enabled in `cli.tsx`) inserts multi-line text literally (CR normalized, paste markers stripped) instead of submitting per line. `caretPos()` is the shared line/col helper. **Readline editing** (all pure in `input.ts`, tested): ⌃U/⌃K kill to line start/end, ⌃W / ⌥⌫ kill word, ⌃D forward-delete, ⌥/⌃ + ←→ word-jump, ⌃A/⌃E line home/end. Keys: ⏎ send · ⌃J newline · ↑↓ line/history · ← → cursor · ⌥←→ word · tab complete @file · **shift+tab cycles mode (normal · auto-accept · plan)** · ⌃Y copy last reply · esc interrupt · ⌃c quit. `/keys` shows the cheatsheet.\n\n**Modes & effort.** Three input modes cycled by shift+tab (`App.tsx` `cycleMode`): **normal** (asks before writes/edits/shell), **auto-accept** (file writes/edits apply without asking — the permission broker auto-resolves `write`/`edit`; shell still gated; diffs still render), **plan** (read-only). Plus **yolo** (auto-approve everything) via `/yolo`. **Effort tiers** (`/effort fast|balanced|max`, or `setEffort`) pin the model through the routing seam (fast→haiku, balanced/max→sonnet) — the active mode + `⚡effort` show as badges in the `StatusBar`. **Click pickers** (fullscreen only): clicking the **model** or **effort** label in the status bar opens a floating picker above it (↑↓ select · ⏎ apply · esc close), reusing the same `/model`/`/effort` command path. The slash commands remain the keyboard path. The fragile row+column hit-test lives in pure, tested `statusBarHit`/`statusBarLayout` (`StatusBar.tsx`); `App.tsx` only supplies live layout (composer line count, `PALETTE_ROWS`, the rendered model/effort/mode) and toggles `quickPicker` state. Inline mode has no mouse grab, so the labels stay informational there. **Copy**: ⌃Y / `/copy` copies the last reply via OSC 52 (`src/ui/clipboard.ts`, works over SSH); `/export [file]` writes the transcript to Markdown. **Terminal integration** (`src/ui/terminal.ts`): the tab title (OSC 2) reflects working/idle, and a long turn (>8s) rings the bell + fires a desktop notification (macOS) so you can step away.\n\n**More UX affordances.** **Type-ahead**: prompts submitted while busy are queued (`queueRef`, shown as chips) and sent when the turn ends. **⌃C** interrupts a turn → clears the composer → \"press again to quit\" (`cli.tsx` renders with `exitOnCtrlC:false`). **Large pastes** collapse to a `[Pasted N lines]` chip (`pasteStoreRef`), expanded back on submit. **Fuzzy** `@file`/`/command` pickers (`src/ui/fuzzy.ts` — substring-first, then subsequence scored by boundary+contiguity; tested). **Cost**: live `$` estimate in the status bar from per-turn model+tokens (`estimateCost` + per-model pricing in `providers.ts`). **Syntax highlighting** for code blocks (`src/ui/highlight.ts` — lightweight per-line tokenizer → Ink spans, NEVER raw ANSI; used by both `lines.ts` `clipSpans` and `Markdown.tsx`). `?` on an empty composer shows the cheatsheet (`KEYS_HELP`).\n\n**Sessions** (`src/session.ts`): conversations persist per-project under `~/.gearbox/sessions/<slug>/` (`GEARBOX_HOME` overrides). Each record holds provider-neutral `messages` + the UI `items` + **per-turn `{model, usage, at}`** (routing/cost data — the record is deliberately not single-model). `gearbox --continue`/`-c` resumes the latest; `/resume [n]` lists/loads in-app; `/clear` starts a fresh session. Prompt history persists across runs (`history.json`). Saving is best-effort (never crashes the app); skipped in demo mode.\n\nFeatures: full markdown via **marked** (parse, `marked.lexer`) + **Ink** (render) in `Markdown.tsx` — headings, bold/italic/inline-code, tables, ordered+nested lists, blockquotes, code blocks. NO foreign ANSI in Ink (cli-highlight/marked-terminal were tried and removed — they corrupt Ink's width/wrapping; render marked's token tree as Ink elements instead). Markdown gets a `width` prop (threaded App→Transcript→Markdown) for table/rule sizing. Colored diffs under edits (`src/diff.ts`, edit/write tools return `{summary,diff}`), plan mode (read-only tools + plan prompt; `/plan` or shift+tab), `!cmd` runs a shell command directly (`src/shell.ts`), `@file` mentions (fuzzy picker `src/ui/mention.ts`+`files.ts`; expanded into the model message on send), live \"working · Ns\" timer.\n\n**Boo (the mascot).** A pixel ghost, now **parametric** (`src/ui/ghost/engine.ts`, ported from a Claude Design handoff). A 20×20 pixel sprite composited from composable layers — body (palette) + face (eyes/mouth) + accessory + persona + a frame-driven overlay (tears/dots/confetti/Z's/sparkle/hearts) — then FOLDED into half-block cells (`▀`/`▄`, top px → `t`/glyph color, bottom px → `b`/bg). `renderGhost(cfg)` is the source of truth for the **default blocks path**; it's pure + memoized. The data: 13 faces (`FACES`), 9 palettes (`PALETTES`), 6 accessories, 9 personas (personas/accessories ported but not yet surfaced in the live UI). Ink `color`/`backgroundColor` props only, NEVER raw ANSI (corrupts Ink's width math). PNG paths are **opt-in** via `GEARBOX_GHOST`:\n\n- `GEARBOX_GHOST=kitty` — real PNG via kitty graphics Unicode placeholders (`U+10EEEE`, fg encodes image id, diacritics encode row/col; PNGs transmitted once in `cli.tsx`). NOTE: the placeholder protocol is young and mis-rendered (squished) in Ghostty during testing — kept opt-in until that's solved.\n- `GEARBOX_GHOST=iterm` — OSC 1337 splash banner (iTerm2/WezTerm).\n\n`detectImageMode()` returns `blocks` unless `GEARBOX_GHOST` opts in. Baked PNGs live in `src/ui/mascot-png.ts`; `bun run scripts/ghost-preview.ts` previews the parametric engine (splash + all faces + the in-flow state crops). **Boo is animated but deliberately calm** on the blocks path (`AnimatedGhost` in `Mascot.tsx`): one shared, unhurried 240ms tick (leaf-local `useTick`, never lifted to App root); talk + overlays advance at half that (~480ms). There is NO idle bob/float and NO splash sparkle — motion is a quiet sign of life, not fidgeting (the splash just blinks every ~6s; in-flow only the state-meaningful overlay/talk moves). `GEARBOX_NO_MOTION=1` freezes to frame 0. `/ghost [mood]` cycles the skin (`skinToCfg` maps it to a cfg; `shades` is the cool face + shades accessory).\n\n**Layout: fullscreen by default; inline is opt-in.** **Fullscreen is the default** (alt-screen frame + virtualized scroll region + scrollbar + mouse wheel scroll); `--inline`, `GEARBOX_INLINE=1`, or `/config inline on` (pref `fullscreen: false`) opts into inline mode. `GEARBOX_FULLSCREEN=1` or `--fullscreen` forces fullscreen explicitly. The decision lives in `cli.tsx` (`wantsFullscreen`). Grabbing the mouse for wheel-scroll is exactly what disables native terminal selection, so in fullscreen mode text selection requires the terminal's modifier (e.g. Option-drag in Ghostty). **Inline mode** (the plain `Transcript` component): no alt-screen, no mouse grab — native click-drag selection / scrollback / copy all work with no modifier. The transcript is a **virtualized line buffer**: `src/ui/lines.ts` (`itemsToLines`) flattens items into styled `Line`s (markdown→lines, wrapping, diffs) — INVARIANT: every line ≤ width (tested), so nothing overflows. **Streaming perf**: flattening the markdown-heavy `assistant`/`user` items is super-linear with their length, so `staticItemLines` memoizes per item in a `WeakMap` keyed by object reference (unchanged items keep identity across renders, so only the changing tail re-parses — history is free; running tools are not cached since their spinner animates). On the producer side, assistant **text deltas are coalesced** on a ~45ms flush timer in `App.tsx`'s `onEvent` (mirroring the tool-stream coalescer), so streaming re-renders at ~22fps instead of per-token — both together stop the auto-scroll jitter that grew with reply length. `finishAssistant`/the turn `finally` flush any buffered text before marking done or on interrupt. In fullscreen, `App` renders only the visible window via `Viewport` (`src/ui/components/Viewport.tsx`) at a computed `transcriptHeight = rows − header − footer` (footer over-estimated so the frame never exceeds the screen; alt-screen clips, so under-filling is safe). Fullscreen scroll: mouse wheel (SGR mouse reporting enabled in `cli.tsx`; parsed off raw stdin in `App` since Ink doesn't model mouse — buttons 64/65) and PgUp/PgDn; new output re-pins to the bottom (`atBottomRef`); a scrollbar sits on the right. (In fullscreen, mouse reporting means text selection needs the terminal's modifier, e.g. Option-drag in Ghostty — which is why inline is now the default.) The virtualized buffer replaced an earlier flex/overflow fullscreen that corrupted on tall output. Chrome spans full width; prose wraps ≤100 cols. The plain `Transcript` component is the inline-fallback renderer. `scripts/gen-mascot.ts` still bakes the PNGs + baked sprites (`mascot-sprite.ts` `GHOSTS`) — but those now feed **only the opt-in kitty/iTerm image path** (`image.ts`); the default blocks path renders the parametric engine instead. The splash scales to the terminal (big=2×/mini=1×/none by rows×cols, in `App.tsx`). The inline/working presence is the compact **state ghost** (see below) — a native-resolution head crop so Boo never dominates the transcript.\n\nCommands are grouped in `/help` (models · conversation · accounts · save · modes · settings · other) and `src/commands.ts` carries plain-language descriptions: /model [name] (fuzzy — \"haiku\"; `/model auto` routes, `/model all` lists every provider) /effort [fast|balanced|max] /prefer [kind model] (remember a confirmed routing preference for a task type) /clear /resume /retry /compact /context /memory /ask &lt;q&gt; (answer questions about Gearbox itself from its bundled docs via a cheap routed model; plain meta-questions auto-route here with a visible affordance) /account (unified: list/add/login/use/rm/refresh — `/accounts` and `/login` are hidden aliases; `/account refresh` re-discovers each account's real callable models) /cost /copy /export [file] /plan /yolo /theme /config (theme·vim·notify·inline; `/vim` is a hidden alias) /init /keys /help /exit. **Hidden** (work but not listed): /accounts /login /vim /ghost. **Removed:** /cwd (the working dir now shows in `/context`). `formatModelList` shows usable models first and collapses no-key providers to a one-line count.\n\n**Command panel (fullscreen only).** Big info-dump commands open a dismissable, Esc-closable overlay instead of dumping into the transcript (`Panel.tsx` + pure `panel.ts`, wired in `App.tsx`): `/help` `/keys` `/context` `/cost` `/memory` are scrollable static dumps (reuse `itemsToLines` + `Viewport`); `/account` and `/model` are interactive lists (↑↓ select · ⏎ acts — they just dispatch the equivalent `/account <n>` / `/model <id>` command and close), and `/model` has type-to-filter (127 Foundry models). The panel replaces the transcript Viewport region while open and takes precedence over `welcome`; the key handler is a branch in `useInput` placed after ⌃C so Esc closes the panel rather than interrupting a turn. Short confirmations (`model → haiku`, `remembered`, `✓ added`, errors) stay inline. Inline mode keeps the old inline printing (no alt-screen to overlay). `openInfoPanel` returns false inline so callers fall back to `push`.\n\n**Accounts: reliability by design.** Every subscription, API key, and cloud credential is meant to work all the time. Switching is **by name only** (a stable unique `slug` per account, e.g. `claude-work`; positional numbers are gone — removing an account never repoints another). Each account carries a cached **health** state (`✓ ready · ⚠ expired · ✗ invalid · ⏳ limited · — unknown`) refreshed at natural touchpoints (boot sweep, opening `/account`, on switch, on live failure) — never by background polling; probes are timeout-bounded. A turn runs through a **failover pool**: `resolve.rank(model)` returns every account that can serve the model's family (cross-provider — Claude can fall Anthropic key → Bedrock → Vertex), health-sorted; `agent/failover.ts` tries them best-first and, on a credential-class failure **before any output** (expired/invalid/no-credit/rate-limited), transparently advances to the next and tells the user which account ran. A real (network/model) error or any failure after output streamed does NOT churn the pool. When the pool is exhausted, one consolidated error names each account, why it failed, and the one command to fix it. Expired subscriptions get one-step re-login: `/account login <name>` (and CLI failures name that exact command). `/account add <paste>` runs the credential **sniffer** (`accounts/sniff.ts`) — paste an API key, an AWS access key or credentials block, a Vertex service-account JSON, an Azure endpoint, or a Vercel gateway key, and it identifies the provider, fills the gaps interactively, and live-tests it.\n\n**Permission gate:** `write_file`/`edit_file`/`run_shell` block on a confirm before mutating. Broker: `src/permission.ts` (`requestPermission` in the tools; `setPermissionHandler` installed by `App`; no handler → allow, so tests/headless are unchanged). Decisions: **once** (1), **always** (2, grants that kind for the session), **all/yolo** (a, auto-approves everything until toggled), **deny** (3/esc). YOLO is also toggled by `/yolo` or started with `--yolo`; a `⚡ yolo` badge shows in the status. The `!` prefix is user-initiated so it is NOT gated. Search/nav tools: `search` (ripgrep, Bun-walk fallback) and `glob` (`Bun.Glob`), both read-only (also in plan mode). The working indicator IS Boo now (`components/Working.tsx`): a compact head-crop ghost whose face follows the agent state — thinking (dots) → streaming (talk) → tool (loading dots) → a clean-finish celebrate (party hat + confetti) → error (crying with falling tears). `App.tsx` derives `mascotState` from the `onEvent` stream; the success/error beat **lingers ~1.5s** after the turn (`linger` state — the working line gates on `busy || linger`, since it would otherwise unmount the instant `busy` goes false). Crops are per-state (`stateView`): head (rows 4–14), head+dots (2–14), head+hat (0–14) so overlays outside the head still read. This deliberately supersedes the earlier \"Boo stays on the welcome splash only / in-flow movement reads as noise\" decision — the compact, state-bearing ghost is the point of the design port.\n\n## Conventions\n\n- Runtime: **Bun**. TypeScript + TSX. Run with `bun run src/cli.tsx`.\n- UI: **Ink** (React for terminals) + **@inkjs/ui**. Keep it calm and beautiful: restrained palette (one accent), generous spacing, consistent glyphs. The look lives in `src/ui/theme.ts` — change colors/glyphs there, not inline.\n- Open + free: MIT, no paid dependencies, no hosted backend, no telemetry. The only cost is the user's own model calls on their own keys.\n- Tools must be safe by default: confirm or sandbox anything destructive; never `rm -rf` or write outside the workspace without intent.\n\n## Run it\n\n```bash\nbun install\n# set at least one key:\nexport ANTHROPIC_API_KEY=... # or OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY / DEEPSEEK_API_KEY\nbun run src/cli.tsx # or: bun start\n```\n\nWith no key it launches in demo mode (a scripted transcript) so the UI still runs.\n\n## Test\n\n```bash\nbun test # render tests + agent-loop tests; no API key needed\nbun run typecheck # tsc --noEmit\n```"
145652
+ text: "# Gearbox — project guide\n\nGearbox is a multi-provider coding harness for the terminal: a beautiful, simple terminal agent that reads/writes code and runs commands, talking to any provider (Anthropic, OpenAI, Google, DeepSeek) through one clean loop.\n\n**The point of the project:** intelligent per-task *model routing* — automatically picking the right model for each task across every provider and account you pay for. Basic routing is live (`RoutingSelector` — classify → quality bar → cheapest winner); the richer engine (shadow-eval, credit/limit penalties, confidence display) layers on top of the same seam. See `DESIGN.md` for the full vision and `experiments/FINDINGS.md` for the validation behind it.\n\n## The one rule that matters\n\n**Keep the routing seam clean.** The agent must never hardcode a model. It asks a `ModelSelector` for the model to use. `RoutingSelector` is the live default (classify task → filter by quality bar → cheapest winner); `FixedSelector` is used only when a model is explicitly pinned (`--model` flag or `/model <name>`). Concretely:\n\n- `src/model/selector.ts` — the seam. `select(task) => ModelChoice` (now carrying an optional `Backend` so the runner can dispatch in-loop vs a subscription seat). Do not bypass it.\n- `src/model/router.ts` — `RoutingSelector` is **account-aware**: it scores `(model, account)` PAIRS, not just models. Candidates = in-loop registry models × the accounts that serve them + flat-rate subscription **seats** (`providers.subscriptionSeats()`, a seat mirrors a canonical model but runs via the vendor binary). Flow: classify → quality bar → context fit → global/`/prefer` preference filter → score → return `{model, reason, backend}`. A seat is ~free until its rate limit, so it wins by default and fails over to metered API as the window fills. `SubscriptionPinSelector`/`FixedSelector` are hard pins (explicit `/account use` or `/model`) that beat auto-routing.\n- `src/model/scoring.ts` — the PURE scorer: `score = costEst + scarcity + switchPenalty + limitPenalty + apiThrottlePenalty − planBonus`, argmin tie-broken deterministically. No I/O; fixture-tested. Every account-state term degrades safely — and where a provider exposes nothing, we ESTIMATE rather than go blind: live API rate-limit headers (`src/model/rate-headers.ts`, parsed from each response → `apiThrottle`, gentle near-empty penalty) and a self-declared budget − tracked spend (`/budget`, an estimated balance feeding scarcity). A missing signal with no estimate is still neutral (no errors per provider).\n- `src/model/routing-context.ts` — `buildRoutingContext()`: the per-turn account-state snapshot (balance where exposed, subscription rate headroom = min over the 5h/weekly windows) read from disk-cached `usage.json`. No network on the hot path; balances refreshed in the background (App effect).\n- `src/model/profiles.ts` — the data corpus: quality, cost, latency, tokenizer calibration, **and the per-model effort vocabulary** (`efforts`) per the provider research. Routing reads this; effort is clamped/omitted against the chosen model's set, never sent unsupported.\n- `src/providers.ts` — maps a provider+model id to an AI SDK model instance. Already multi-provider. Adding a model is data, not code.\n- Every model call captures token usage (`src/agent/run.ts`) so the cost engine has data. Do not drop usage.\n- The UI consumes a normalized `AgentEvent` stream (`src/agent/events.ts`), never the AI SDK's raw types. This decouples the UI from the provider layer and from routing.\n\nIf you find yourself writing `anthropic('claude-...')` anywhere outside `providers.ts`, stop — route it through the selector.\n\n## Layout\n\n```\nsrc/\n cli.tsx entry point; renders the Ink app; picks RoutingSelector by default\n config.ts minimal config (default model, provider from env)\n providers.ts provider+model id -> AI SDK model (multi-provider; contextWindow per model)\n commands.ts slash-command metadata + pure helpers (fuzzy model match, /help, model list)\n tools.ts read / write / edit / list / search / glob / run_shell (AI SDK tools)\n model/\n selector.ts THE ROUTING SEAM — ModelSelector + ModelChoice.backend + FixedSelector (pinned model)\n router.ts RoutingSelector (account-aware): scores (model, account) pairs incl. subscription seats; SubscriptionPinSelector\n scoring.ts PURE scorer: cost + scarcity + limit − plan bonus; deterministic, fixture-tested\n routing-context.ts per-turn account-state snapshot (balance + subscription rate headroom) from usage.json\n cooldown.ts reactive-failover support: classify a failure + park an exhausted account so the router routes around it\n profiles.ts model corpus: quality (SWE-bench), cost ($/Mtok), latency, tokenizer calibration, per-model effort vocab\n tokens.ts calibrated token counting (js-tiktoken × per-model calibration factor)\n preferences.ts persist /prefer kind model choices to ~/.gearbox/routing-preferences.json\n reasoning.ts reasoning/thinking config helpers\n context/\n builder.ts context engine: system + memory + repo map + retrieved files + curated history\n retrieve.ts BM25 lexical retrieval — top-K relevant files for a prompt (no model call)\n repomap.ts repo structure summary for the system prompt\n memory.ts project memory (GEARBOX.md / CLAUDE.md loaded into context)\n compact.ts context compaction (/compact)\n accounts/\n types.ts Account + AuthMethod types (API key, AWS, Azure, Vertex, CLI, OpenAI-compat)\n store.ts accounts.json persistence (~/.gearbox/accounts.json)\n catalog.ts provider catalog (known providers, env vars, labels)\n detect.ts auto-detect env creds + cloud credentials\n onboard.ts interactive add/test account flows; addByPastedKey routes through sniff.ts\n sniff.ts pure credential sniffer: paste anything (key / AWS block / SA JSON / Azure URL / gateway key) → {kind, provider, fields, missing}\n resolve.ts credential resolution (Account → ResolvedCreds) + rank(model) → ordered failover pool (cross-provider, health-sorted)\n discover.ts per-account model discovery (Azure deployments / Foundry / gateway /models) → account.models; catalog defaultModels are seeds, not callable ids\n health.ts account health: classifyError (provider error → state), checkHealth (cached probe, timeout-bounded), recordHealth; no background polling\n usage.ts per-account spend ledger + rate-limit snapshots + balance tracking\n balance.ts provider balance fetch helpers\n help/\n ask.ts /ask corpus: bundled docs + generated command reference, system prompt, meta-question auto-detect\n agent/\n events.ts AgentEvent — normalized stream the UI consumes\n run.ts real agent loop (AI SDK streamText -> AgentEvent), abort-aware; runCompletion = tool-less grounded answer (used by /ask); returns a structured failure (for failover)\n failover.ts runWithFailover: run a turn over the ranked account pool; on a credential failure before output, advance to the next; clear exhaustion errors\n cli-backend.ts claude/codex CLI subprocess backend (for Pro/Max subscriptions)\n mock.ts scripted demo stream (runs with no API key; used by tests)\n ui/\n theme.ts colors + glyphs (the look)\n input.ts pure key→action reducer for the composer (tested)\n history.ts pure ↑/↓ prompt-history nav (tested)\n net.ts background online probe; status bar shows ⚠ offline when down\n useTerminalSize.ts reactive width on resize (everything reflows)\n git.ts current branch for the status line\n App.tsx the Ink app: state, useInput dispatch, commands, turns\n components/ Banner, Transcript, Composer, CommandPalette, StatusBar, PermissionPrompt, Panel\n panel.ts dismissable command-panel model + pure helpers (clamp/window/filter); tested\ntest/ pure-logic + render tests (ink-testing-library); no keys\nDESIGN.md full product vision (routing, requirements, UX)\nexperiments/ prototypes that validated the architecture\n```\n\nThe composer is custom (Ink `useInput` + `src/ui/input.ts`), not a third-party widget — full control over the cursor, ↑/↓ history, and esc-to-interrupt, with no focus/remount fragility. **Multi-line**: ⌃J (or shift/alt+⏎) inserts a newline, ⏎ submits; ↑/↓ move between lines and fall through to history at the top/bottom line; bracketed paste (enabled in `cli.tsx`) inserts multi-line text literally (CR normalized, paste markers stripped) instead of submitting per line. `caretPos()` is the shared line/col helper. **Readline editing** (all pure in `input.ts`, tested): ⌃U/⌃K kill to line start/end, ⌃W / ⌥⌫ kill word, ⌃D forward-delete, ⌥/⌃ + ←→ word-jump, ⌃A/⌃E line home/end. Keys: ⏎ send · ⌃J newline · ↑↓ line/history · ← → cursor · ⌥←→ word · tab complete @file · **shift+tab cycles mode (normal · auto-accept · plan)** · ⌃Y copy last reply · esc interrupt · ⌃c quit. `/keys` shows the cheatsheet.\n\n**Modes & effort.** Three input modes cycled by shift+tab (`App.tsx` `cycleMode`): **normal** (asks before writes/edits/shell), **auto-accept** (file writes/edits apply without asking — the permission broker auto-resolves `write`/`edit`; shell still gated; diffs still render), **plan** (read-only). Plus **yolo** (auto-approve everything) via `/yolo`. **Effort tiers** (`/effort fast|balanced|max`, or `setEffort`) pin the model through the routing seam (fast→haiku, balanced/max→sonnet) — the active mode + `⚡effort` show as badges in the `StatusBar`. **Click pickers** (fullscreen only): clicking the **model** or **effort** label in the status bar opens a floating picker above it (↑↓ select · ⏎ apply · esc close), reusing the same `/model`/`/effort` command path. The slash commands remain the keyboard path. The fragile row+column hit-test lives in pure, tested `statusBarHit`/`statusBarLayout` (`StatusBar.tsx`); `App.tsx` only supplies live layout (composer line count, `PALETTE_ROWS`, the rendered model/effort/mode) and toggles `quickPicker` state. Inline mode has no mouse grab, so the labels stay informational there. **Copy**: ⌃Y / `/copy` copies the last reply via OSC 52 (`src/ui/clipboard.ts`, works over SSH); `/export [file]` writes the transcript to Markdown. **Terminal integration** (`src/ui/terminal.ts`): the tab title (OSC 2) reflects working/idle, and a long turn (>8s) rings the bell + fires a desktop notification (macOS) so you can step away.\n\n**More UX affordances.** **Type-ahead**: prompts submitted while busy are queued (`queueRef`, shown as chips) and sent when the turn ends. **⌃C** interrupts a turn → clears the composer → \"press again to quit\" (`cli.tsx` renders with `exitOnCtrlC:false`). **Large pastes** collapse to a `[Pasted N lines]` chip (`pasteStoreRef`), expanded back on submit. **Fuzzy** `@file`/`/command` pickers (`src/ui/fuzzy.ts` — substring-first, then subsequence scored by boundary+contiguity; tested). **Cost**: live `$` estimate in the status bar from per-turn model+tokens (`estimateCost` + per-model pricing in `providers.ts`). **Syntax highlighting** for code blocks (`src/ui/highlight.ts` — lightweight per-line tokenizer → Ink spans, NEVER raw ANSI; used by both `lines.ts` `clipSpans` and `Markdown.tsx`). `?` on an empty composer shows the cheatsheet (`KEYS_HELP`).\n\n**Sessions** (`src/session.ts`): conversations persist per-project under `~/.gearbox/sessions/<slug>/` (`GEARBOX_HOME` overrides). Each record holds provider-neutral `messages` + the UI `items` + **per-turn `{model, usage, at}`** (routing/cost data — the record is deliberately not single-model). `gearbox --continue`/`-c` resumes the latest; `/resume [n]` lists/loads in-app; `/clear` starts a fresh session. Prompt history persists across runs (`history.json`). Saving is best-effort (never crashes the app); skipped in demo mode.\n\nFeatures: full markdown via **marked** (parse, `marked.lexer`) + **Ink** (render) in `Markdown.tsx` — headings, bold/italic/inline-code, tables, ordered+nested lists, blockquotes, code blocks. NO foreign ANSI in Ink (cli-highlight/marked-terminal were tried and removed — they corrupt Ink's width/wrapping; render marked's token tree as Ink elements instead). Markdown gets a `width` prop (threaded App→Transcript→Markdown) for table/rule sizing. Colored diffs under edits (`src/diff.ts`, edit/write tools return `{summary,diff}`), plan mode (read-only tools + plan prompt; `/plan` or shift+tab), `!cmd` runs a shell command directly (`src/shell.ts`), `@file` mentions (fuzzy picker `src/ui/mention.ts`+`files.ts`; expanded into the model message on send), live \"working · Ns\" timer.\n\n**Boo (the mascot).** A pixel ghost, now **parametric** (`src/ui/ghost/engine.ts`, ported from a Claude Design handoff). A 20×20 pixel sprite composited from composable layers — body (palette) + face (eyes/mouth) + accessory + persona + a frame-driven overlay (tears/dots/confetti/Z's/sparkle/hearts) — then FOLDED into half-block cells (`▀`/`▄`, top px → `t`/glyph color, bottom px → `b`/bg). `renderGhost(cfg)` is the source of truth for the **default blocks path**; it's pure + memoized. The data: 13 faces (`FACES`), 9 palettes (`PALETTES`), 6 accessories, 9 personas (personas/accessories ported but not yet surfaced in the live UI). Ink `color`/`backgroundColor` props only, NEVER raw ANSI (corrupts Ink's width math). PNG paths are **opt-in** via `GEARBOX_GHOST`:\n\n- `GEARBOX_GHOST=kitty` — real PNG via kitty graphics Unicode placeholders (`U+10EEEE`, fg encodes image id, diacritics encode row/col; PNGs transmitted once in `cli.tsx`). NOTE: the placeholder protocol is young and mis-rendered (squished) in Ghostty during testing — kept opt-in until that's solved.\n- `GEARBOX_GHOST=iterm` — OSC 1337 splash banner (iTerm2/WezTerm).\n\n`detectImageMode()` returns `blocks` unless `GEARBOX_GHOST` opts in. Baked PNGs live in `src/ui/mascot-png.ts`; `bun run scripts/ghost-preview.ts` previews the parametric engine (splash + all faces + the in-flow state crops). **Boo is animated but deliberately calm** on the blocks path (`AnimatedGhost` in `Mascot.tsx`): one shared, unhurried 240ms tick (leaf-local `useTick`, never lifted to App root); talk + overlays advance at half that (~480ms). There is NO idle bob/float and NO splash sparkle — motion is a quiet sign of life, not fidgeting (the splash just blinks every ~6s; in-flow only the state-meaningful overlay/talk moves). `GEARBOX_NO_MOTION=1` freezes to frame 0. `/ghost [mood]` cycles the skin (`skinToCfg` maps it to a cfg; `shades` is the cool face + shades accessory).\n\n**Layout: fullscreen by default; inline is opt-in.** **Fullscreen is the default** (alt-screen frame + virtualized scroll region + scrollbar + mouse wheel scroll); `--inline`, `GEARBOX_INLINE=1`, or `/config inline on` (pref `fullscreen: false`) opts into inline mode. `GEARBOX_FULLSCREEN=1` or `--fullscreen` forces fullscreen explicitly. The decision lives in `cli.tsx` (`wantsFullscreen`). Grabbing the mouse for wheel-scroll is exactly what disables native terminal selection, so in fullscreen mode text selection requires the terminal's modifier (e.g. Option-drag in Ghostty). **Inline mode** (the plain `Transcript` component): no alt-screen, no mouse grab — native click-drag selection / scrollback / copy all work with no modifier. The transcript is a **virtualized line buffer**: `src/ui/lines.ts` (`itemsToLines`) flattens items into styled `Line`s (markdown→lines, wrapping, diffs) — INVARIANT: every line ≤ width (tested), so nothing overflows. **Streaming perf**: flattening the markdown-heavy `assistant`/`user` items is super-linear with their length, so `staticItemLines` memoizes per item in a `WeakMap` keyed by object reference (unchanged items keep identity across renders, so only the changing tail re-parses — history is free; running tools are not cached since their spinner animates). On the producer side, assistant **text deltas are coalesced** on a ~45ms flush timer in `App.tsx`'s `onEvent` (mirroring the tool-stream coalescer), so streaming re-renders at ~22fps instead of per-token — both together stop the auto-scroll jitter that grew with reply length. `finishAssistant`/the turn `finally` flush any buffered text before marking done or on interrupt. In fullscreen, `App` renders only the visible window via `Viewport` (`src/ui/components/Viewport.tsx`) at a computed `transcriptHeight = rows − header − footer` (footer over-estimated so the frame never exceeds the screen; alt-screen clips, so under-filling is safe). Fullscreen scroll: mouse wheel (SGR mouse reporting enabled in `cli.tsx`; parsed off raw stdin in `App` since Ink doesn't model mouse — buttons 64/65) and PgUp/PgDn; new output re-pins to the bottom (`atBottomRef`); a scrollbar sits on the right. (In fullscreen, mouse reporting means text selection needs the terminal's modifier, e.g. Option-drag in Ghostty — which is why inline is now the default.) The virtualized buffer replaced an earlier flex/overflow fullscreen that corrupted on tall output. Chrome spans full width; prose wraps ≤100 cols. The plain `Transcript` component is the inline-fallback renderer. `scripts/gen-mascot.ts` still bakes the PNGs + baked sprites (`mascot-sprite.ts` `GHOSTS`) — but those now feed **only the opt-in kitty/iTerm image path** (`image.ts`); the default blocks path renders the parametric engine instead. The splash scales to the terminal (big=2×/mini=1×/none by rows×cols, in `App.tsx`). The inline/working presence is the compact **state ghost** (see below) — a native-resolution head crop so Boo never dominates the transcript.\n\nCommands are grouped in `/help` (models · conversation · accounts · save · modes · settings · other) and `src/commands.ts` carries plain-language descriptions: /model [name] (fuzzy — \"haiku\"; `/model auto` routes, `/model all` lists every provider) /effort [fast|balanced|max] /prefer [kind model] (remember a confirmed routing preference for a task type) /clear /resume /retry /compact /context /memory /ask &lt;q&gt; (answer questions about Gearbox itself from its bundled docs via a cheap routed model; plain meta-questions auto-route here with a visible affordance) /account (unified: list/add/login/use/rm/refresh — `/accounts` and `/login` are hidden aliases; `/account refresh` re-discovers each account's real callable models) /cost (fullscreen: TOGGLES a persistent usage strip above the composer — context % · subscription 5h/7d headroom · session spend — stays until toggled off, persisted via `prefs.statusPinned`, doesn't capture input; `StatusStrip.tsx`. inline: one-shot card) /copy /export [file] /plan /yolo /theme /config (theme·vim·notify·inline; `/vim` is a hidden alias) /init /keys /help /exit. **Hidden** (work but not listed): /accounts /login /vim /ghost. **Removed:** /cwd (the working dir now shows in `/context`). `formatModelList` shows usable models first and collapses no-key providers to a one-line count.\n\n**Command panel (fullscreen only).** Big info-dump commands open a dismissable, Esc-closable overlay instead of dumping into the transcript (`Panel.tsx` + pure `panel.ts`, wired in `App.tsx`): `/help` `/keys` `/context` `/cost` `/memory` are scrollable static dumps (reuse `itemsToLines` + `Viewport`); `/account` and `/model` are interactive lists (↑↓ select · ⏎ acts — they just dispatch the equivalent `/account <n>` / `/model <id>` command and close), and `/model` has type-to-filter (127 Foundry models). The panel replaces the transcript Viewport region while open and takes precedence over `welcome`; the key handler is a branch in `useInput` placed after ⌃C so Esc closes the panel rather than interrupting a turn. Short confirmations (`model → haiku`, `remembered`, `✓ added`, errors) stay inline. Inline mode keeps the old inline printing (no alt-screen to overlay). `openInfoPanel` returns false inline so callers fall back to `push`.\n\n**Accounts: reliability by design.** Every subscription, API key, and cloud credential is meant to work all the time. Switching is **by name only** (a stable unique `slug` per account, e.g. `claude-work`; positional numbers are gone — removing an account never repoints another). Each account carries a cached **health** state (`✓ ready · ⚠ expired · ✗ invalid · ⏳ limited · — unknown`) refreshed at natural touchpoints (boot sweep, opening `/account`, on switch, on live failure) — never by background polling; probes are timeout-bounded. A turn runs through a **failover pool**: `resolve.rank(model)` returns every account that can serve the model's family (cross-provider — Claude can fall Anthropic key → Bedrock → Vertex), health-sorted; `agent/failover.ts` tries them best-first and, on a credential-class failure **before any output** (expired/invalid/no-credit/rate-limited), transparently advances to the next and tells the user which account ran. A real (network/model) error or any failure after output streamed does NOT churn the pool. When the pool is exhausted, one consolidated error names each account, why it failed, and the one command to fix it. Expired subscriptions get one-step re-login: `/account login <name>` (and CLI failures name that exact command). `/account add <paste>` runs the credential **sniffer** (`accounts/sniff.ts`) — paste an API key, an AWS access key or credentials block, a Vertex service-account JSON, an Azure endpoint, or a Vercel gateway key, and it identifies the provider, fills the gaps interactively, and live-tests it.\n\n**Permission gate:** `write_file`/`edit_file`/`run_shell` block on a confirm before mutating. Broker: `src/permission.ts` (`requestPermission` in the tools; `setPermissionHandler` installed by `App`; no handler → allow, so tests/headless are unchanged). Decisions: **once** (1), **always** (2, grants that kind for the session), **all/yolo** (a, auto-approves everything until toggled), **deny** (3/esc). YOLO is also toggled by `/yolo` or started with `--yolo`; a `⚡ yolo` badge shows in the status. The `!` prefix is user-initiated so it is NOT gated. Search/nav tools: `search` (ripgrep, Bun-walk fallback) and `glob` (`Bun.Glob`), both read-only (also in plan mode). The working indicator IS Boo now (`components/Working.tsx`): a compact head-crop ghost whose face follows the agent state — thinking (dots) → streaming (talk) → tool (loading dots) → a clean-finish celebrate (party hat + confetti) → error (crying with falling tears). `App.tsx` derives `mascotState` from the `onEvent` stream; the success/error beat **lingers ~1.5s** after the turn (`linger` state — the working line gates on `busy || linger`, since it would otherwise unmount the instant `busy` goes false). Crops are per-state (`stateView`): head (rows 4–14), head+dots (2–14), head+hat (0–14) so overlays outside the head still read. This deliberately supersedes the earlier \"Boo stays on the welcome splash only / in-flow movement reads as noise\" decision — the compact, state-bearing ghost is the point of the design port.\n\n## Conventions\n\n- Runtime: **Bun**. TypeScript + TSX. Run with `bun run src/cli.tsx`.\n- UI: **Ink** (React for terminals) + **@inkjs/ui**. Keep it calm and beautiful: restrained palette (one accent), generous spacing, consistent glyphs. The look lives in `src/ui/theme.ts` — change colors/glyphs there, not inline.\n- Open + free: MIT, no paid dependencies, no hosted backend, no telemetry. The only cost is the user's own model calls on their own keys.\n- Tools must be safe by default: confirm or sandbox anything destructive; never `rm -rf` or write outside the workspace without intent.\n\n## Run it\n\n```bash\nbun install\n# set at least one key:\nexport ANTHROPIC_API_KEY=... # or OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY / DEEPSEEK_API_KEY\nbun run src/cli.tsx # or: bun start\n```\n\nWith no key it launches in demo mode (a scripted transcript) so the UI still runs.\n\n## Test\n\n```bash\nbun test # render tests + agent-loop tests; no API key needed\nbun run typecheck # tsc --noEmit\n```"
145522
145653
  },
145523
145654
  {
145524
145655
  file: "DESIGN.md",
@@ -146641,7 +146772,7 @@ function gitBranch() {
146641
146772
 
146642
146773
  // src/ui/App.tsx
146643
146774
  init_proc();
146644
- var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
146775
+ var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
146645
146776
  import { basename as basename3, extname, resolve as resolve12 } from "node:path";
146646
146777
  import { existsSync as existsSync11, readFileSync as readFileSync16, statSync as statSync5 } from "node:fs";
146647
146778
  import { writeFile as fsWriteFile } from "node:fs/promises";
@@ -146791,16 +146922,16 @@ function ActivityRail({ items, width }) {
146791
146922
  const toolText = tools2.map((t2) => `${t2.status === "running" ? spin2 : t2.status === "err" ? "!" : "✓"} ${t2.name.replace(/_file$/, "").replace("run_shell", "shell")}`).join(" · ");
146792
146923
  const checkText = checks4.map((c) => `${c.ok ? "✓" : "!"} ${c.command}`).join(" · ");
146793
146924
  const line = [model ? model.model : null, phase ? phase.label : null, toolText || null, checkText || null].filter(Boolean).join(" · ");
146794
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
146925
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146795
146926
  paddingX: 1,
146796
146927
  marginTop: 1,
146797
146928
  width,
146798
146929
  children: [
146799
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
146930
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146800
146931
  color: color.accentDim,
146801
146932
  children: "activity "
146802
146933
  }, undefined, false, undefined, this),
146803
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
146934
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146804
146935
  color: color.faint,
146805
146936
  children: line.slice(0, Math.max(width - 10, 20))
146806
146937
  }, undefined, false, undefined, this)
@@ -146810,31 +146941,31 @@ function ActivityRail({ items, width }) {
146810
146941
  function SetupSplash({ state, width, skin, splashSize }) {
146811
146942
  const detected = state.importable.length + state.cloudImportable.length;
146812
146943
  const panelWidth = Math.min(Math.max(width - 4, 30), 58);
146813
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
146944
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146814
146945
  flexDirection: "column",
146815
146946
  alignItems: "center",
146816
146947
  children: [
146817
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(MascotSplash, {
146948
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(MascotSplash, {
146818
146949
  skin,
146819
146950
  size: splashSize
146820
146951
  }, undefined, false, undefined, this),
146821
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
146952
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146822
146953
  marginTop: 1,
146823
146954
  flexDirection: "column",
146824
146955
  alignItems: "center",
146825
146956
  children: [
146826
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
146957
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146827
146958
  color: color.accent,
146828
146959
  bold: true,
146829
146960
  children: "gearbox"
146830
146961
  }, undefined, false, undefined, this),
146831
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
146962
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146832
146963
  color: color.dim,
146833
146964
  children: "one terminal · every model you already pay for"
146834
146965
  }, undefined, false, undefined, this)
146835
146966
  ]
146836
146967
  }, undefined, true, undefined, this),
146837
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
146968
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146838
146969
  marginTop: 2,
146839
146970
  width: panelWidth,
146840
146971
  borderStyle: "round",
@@ -146843,18 +146974,18 @@ function SetupSplash({ state, width, skin, splashSize }) {
146843
146974
  paddingY: 1,
146844
146975
  flexDirection: "column",
146845
146976
  children: [
146846
- detected > 0 ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
146977
+ detected > 0 ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
146847
146978
  children: [
146848
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
146979
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146849
146980
  children: [
146850
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
146981
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146851
146982
  color: color.ok,
146852
146983
  children: [
146853
146984
  glyph.on,
146854
146985
  " "
146855
146986
  ]
146856
146987
  }, undefined, true, undefined, this),
146857
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
146988
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146858
146989
  color: color.text,
146859
146990
  children: [
146860
146991
  detected,
@@ -146865,75 +146996,75 @@ function SetupSplash({ state, width, skin, splashSize }) {
146865
146996
  }, undefined, true, undefined, this)
146866
146997
  ]
146867
146998
  }, undefined, true, undefined, this),
146868
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
146999
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146869
147000
  marginTop: 1,
146870
147001
  children: [
146871
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147002
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146872
147003
  color: color.accent,
146873
147004
  children: "/account import"
146874
147005
  }, undefined, false, undefined, this),
146875
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147006
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146876
147007
  color: color.dim,
146877
147008
  children: " connect automatically"
146878
147009
  }, undefined, false, undefined, this)
146879
147010
  ]
146880
147011
  }, undefined, true, undefined, this),
146881
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
147012
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146882
147013
  marginTop: 1,
146883
147014
  children: [
146884
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147015
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146885
147016
  color: color.faint,
146886
147017
  children: "or add a different key: "
146887
147018
  }, undefined, false, undefined, this),
146888
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147019
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146889
147020
  color: color.accent,
146890
147021
  children: "/account add <api-key>"
146891
147022
  }, undefined, false, undefined, this)
146892
147023
  ]
146893
147024
  }, undefined, true, undefined, this)
146894
147025
  ]
146895
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
147026
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
146896
147027
  children: [
146897
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147028
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146898
147029
  color: color.dim,
146899
147030
  children: "paste or type a key to get started"
146900
147031
  }, undefined, false, undefined, this),
146901
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
147032
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146902
147033
  marginTop: 1,
146903
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147034
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146904
147035
  color: color.accent,
146905
147036
  children: "/account add <api-key>"
146906
147037
  }, undefined, false, undefined, this)
146907
147038
  }, undefined, false, undefined, this)
146908
147039
  ]
146909
147040
  }, undefined, true, undefined, this),
146910
- (state.hasClaudeCli || state.hasCodexCli) && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
147041
+ (state.hasClaudeCli || state.hasCodexCli) && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146911
147042
  marginTop: 2,
146912
147043
  flexDirection: "column",
146913
147044
  children: [
146914
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147045
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146915
147046
  color: color.faint,
146916
147047
  children: "subscriptions detected"
146917
147048
  }, undefined, false, undefined, this),
146918
- state.hasClaudeCli && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
147049
+ state.hasClaudeCli && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146919
147050
  children: [
146920
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147051
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146921
147052
  color: color.accent,
146922
147053
  children: "/account add claude"
146923
147054
  }, undefined, false, undefined, this),
146924
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147055
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146925
147056
  color: color.faint,
146926
147057
  children: " Claude Pro / Max"
146927
147058
  }, undefined, false, undefined, this)
146928
147059
  ]
146929
147060
  }, undefined, true, undefined, this),
146930
- state.hasCodexCli && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
147061
+ state.hasCodexCli && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146931
147062
  children: [
146932
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147063
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146933
147064
  color: color.accent,
146934
147065
  children: "/account add codex"
146935
147066
  }, undefined, false, undefined, this),
146936
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147067
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146937
147068
  color: color.faint,
146938
147069
  children: " ChatGPT Plus"
146939
147070
  }, undefined, false, undefined, this)
@@ -146943,9 +147074,9 @@ function SetupSplash({ state, width, skin, splashSize }) {
146943
147074
  }, undefined, true, undefined, this)
146944
147075
  ]
146945
147076
  }, undefined, true, undefined, this),
146946
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
147077
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
146947
147078
  marginTop: 1,
146948
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
147079
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
146949
147080
  color: color.faint,
146950
147081
  children: "/onboard · /account · /help"
146951
147082
  }, undefined, false, undefined, this)
@@ -147030,6 +147161,25 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
147030
147161
  };
147031
147162
  const panelMaxScrollRef = import_react26.useRef(0);
147032
147163
  const panelAccountSlugsRef = import_react26.useRef([]);
147164
+ const [statusPinned, setStatusPinnedState] = import_react26.useState(() => Boolean(loadPrefs().statusPinned));
147165
+ const setStatusPinned = (v) => {
147166
+ setStatusPinnedState(v);
147167
+ updatePrefs({ statusPinned: v });
147168
+ };
147169
+ const currentUsageView = () => {
147170
+ const accounts = listAccounts();
147171
+ const resolve13 = (id) => {
147172
+ const a = getAccount(id);
147173
+ if (a) {
147174
+ const bin = a.auth.kind === "cli" ? a.auth.binary : undefined;
147175
+ return { name: accountName(a), kind: a.exec === "cli" ? "sub" : "api", balanceExposed: a.exec !== "cli" && balanceExposed(a.provider), limitNote: a.exec === "cli" ? `${bin === "codex" ? "Codex" : "Claude"} CLI hasn't reported quota windows yet` : undefined };
147176
+ }
147177
+ if (id === "unknown")
147178
+ return { name: "(unattributed)", kind: "api" };
147179
+ return { name: id, kind: "api" };
147180
+ };
147181
+ return buildUsageView(estimateCost(sessionRef.current.turns), resolve13, Date.now(), accounts.map((a) => a.id));
147182
+ };
147033
147183
  const buildPanelModelRows = (cur) => modelRegistry().filter((m2) => providerAvailable(m2.provider)).map((m2) => ({ id: m2.id, label: m2.label, provider: m2.provider, current: m2.id === cur }));
147034
147184
  const openInfoPanel = (title, item) => {
147035
147185
  if (!fullscreen)
@@ -149233,6 +149383,12 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149233
149383
  }
149234
149384
  case "cost":
149235
149385
  case "usage": {
149386
+ if (fullscreen) {
149387
+ const on = !statusPinned;
149388
+ setStatusPinned(on);
149389
+ notice(on ? "usage pinned above the composer — /cost to hide" : "usage hidden");
149390
+ return;
149391
+ }
149236
149392
  const accounts = listAccounts();
149237
149393
  const resolve13 = (id) => {
149238
149394
  const a = getAccount(id);
@@ -149815,6 +149971,11 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149815
149971
  footer += 1;
149816
149972
  if (quickPicker && quickRows.length)
149817
149973
  footer += quickPickerLimit + 2;
149974
+ const stripView = statusPinned ? currentUsageView() : null;
149975
+ const stripSub = stripView ? stripView.subscriptions.find((s2) => s2.name === activeCli?.label) ?? stripView.subscriptions[0] ?? null : null;
149976
+ const stripApi = stripView ? stripView.apiKeys[0] ?? null : null;
149977
+ if (statusPinned)
149978
+ footer += 2 + (ctxPct != null ? 1 : 0) + (stripSub ? Math.max(1, stripSub.limits?.length ?? 1) : 0) + (stripApi?.spend ? 1 : 0) + 1;
149818
149979
  const HEADER = 3;
149819
149980
  const transcriptHeight = Math.max(1, rows - HEADER - footer);
149820
149981
  const maxScroll = Math.max(0, lines.length - transcriptHeight);
@@ -149845,87 +150006,87 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149845
150006
  if (atBottomRef.current)
149846
150007
  setScrollTop(maxScroll);
149847
150008
  }, [lines.length, maxScroll]);
149848
- const hero = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150009
+ const hero = /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
149849
150010
  flexDirection: "column",
149850
150011
  alignItems: "center",
149851
- children: setupRequired ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(SetupSplash, {
150012
+ children: setupRequired ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(SetupSplash, {
149852
150013
  state: onboardingState,
149853
150014
  width,
149854
150015
  skin: ghostSkin,
149855
150016
  splashSize
149856
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
150017
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
149857
150018
  children: [
149858
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(MascotSplash, {
150019
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(MascotSplash, {
149859
150020
  skin: ghostSkin,
149860
150021
  size: splashSize
149861
150022
  }, undefined, false, undefined, this),
149862
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150023
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
149863
150024
  marginTop: 1,
149864
150025
  children: [
149865
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150026
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149866
150027
  color: color.dim,
149867
150028
  children: "talk or type "
149868
150029
  }, undefined, false, undefined, this),
149869
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150030
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149870
150031
  color: color.faint,
149871
150032
  children: [
149872
150033
  glyph.bullet,
149873
150034
  " "
149874
150035
  ]
149875
150036
  }, undefined, true, undefined, this),
149876
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150037
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149877
150038
  color: color.accentDim,
149878
150039
  children: "/"
149879
150040
  }, undefined, false, undefined, this),
149880
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150041
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149881
150042
  color: color.dim,
149882
150043
  children: "commands "
149883
150044
  }, undefined, false, undefined, this),
149884
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150045
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149885
150046
  color: color.accentDim,
149886
150047
  children: "@"
149887
150048
  }, undefined, false, undefined, this),
149888
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150049
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149889
150050
  color: color.dim,
149890
150051
  children: "files "
149891
150052
  }, undefined, false, undefined, this),
149892
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150053
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149893
150054
  color: color.accentDim,
149894
150055
  children: "!"
149895
150056
  }, undefined, false, undefined, this),
149896
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150057
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149897
150058
  color: color.dim,
149898
150059
  children: "shell"
149899
150060
  }, undefined, false, undefined, this)
149900
150061
  ]
149901
150062
  }, undefined, true, undefined, this),
149902
- firstRunRef.current ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150063
+ firstRunRef.current ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
149903
150064
  marginTop: 1,
149904
150065
  flexDirection: "column",
149905
150066
  alignItems: "center",
149906
150067
  children: [
149907
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150068
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149908
150069
  color: color.faint,
149909
150070
  children: [
149910
150071
  "new here? press ",
149911
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150072
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149912
150073
  color: color.accent,
149913
150074
  children: "?"
149914
150075
  }, undefined, false, undefined, this),
149915
150076
  " for shortcuts · ",
149916
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150077
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149917
150078
  color: color.accent,
149918
150079
  children: "shift+tab"
149919
150080
  }, undefined, false, undefined, this),
149920
150081
  " cycles modes · ",
149921
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150082
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149922
150083
  color: color.accent,
149923
150084
  children: "⌃Y"
149924
150085
  }, undefined, false, undefined, this),
149925
150086
  " copies the last reply"
149926
150087
  ]
149927
150088
  }, undefined, true, undefined, this),
149928
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150089
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149929
150090
  color: color.faint,
149930
150091
  children: "/config inline on for terminal scrollback · /keys for shortcuts"
149931
150092
  }, undefined, false, undefined, this)
@@ -149934,17 +150095,17 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149934
150095
  ]
149935
150096
  }, undefined, true, undefined, this)
149936
150097
  }, undefined, false, undefined, this);
149937
- const paletteJsx = pickerRows.length || cmdMatches.length || fileMatches.length ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150098
+ const paletteJsx = pickerRows.length || cmdMatches.length || fileMatches.length ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
149938
150099
  flexDirection: "column",
149939
150100
  children: [
149940
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CommandPalette, {
150101
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CommandPalette, {
149941
150102
  draft: edit2.value,
149942
150103
  selected: selectedPalette,
149943
150104
  limit: 7,
149944
150105
  rows: pickerRows,
149945
150106
  width
149946
150107
  }, undefined, false, undefined, this),
149947
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(FilePalette, {
150108
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(FilePalette, {
149948
150109
  matches: fileMatches,
149949
150110
  selected: selectedPalette,
149950
150111
  limit: 5,
@@ -149952,24 +150113,24 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149952
150113
  }, undefined, false, undefined, this)
149953
150114
  ]
149954
150115
  }, undefined, true, undefined, this) : null;
149955
- const quickPickerJsx = quickPicker && quickRows.length ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150116
+ const quickPickerJsx = quickPicker && quickRows.length ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
149956
150117
  flexDirection: "column",
149957
150118
  marginTop: 1,
149958
150119
  children: [
149959
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150120
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
149960
150121
  paddingX: 1,
149961
150122
  children: [
149962
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150123
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149963
150124
  color: color.accent,
149964
150125
  children: quickPicker === "model" ? "model" : "effort"
149965
150126
  }, undefined, false, undefined, this),
149966
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150127
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
149967
150128
  color: color.faint,
149968
150129
  children: " · ↑↓ select · ⏎ apply · esc close"
149969
150130
  }, undefined, false, undefined, this)
149970
150131
  ]
149971
150132
  }, undefined, true, undefined, this),
149972
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CommandPalette, {
150133
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(CommandPalette, {
149973
150134
  draft: "",
149974
150135
  selected: Math.min(quickPickerIndex, quickRows.length - 1),
149975
150136
  limit: quickPickerLimit,
@@ -149978,10 +150139,10 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149978
150139
  }, undefined, false, undefined, this)
149979
150140
  ]
149980
150141
  }, undefined, true, undefined, this) : null;
149981
- const composerJsx = perm ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(PermissionPrompt, {
150142
+ const composerJsx = perm ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(PermissionPrompt, {
149982
150143
  req: perm,
149983
150144
  width
149984
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Composer, {
150145
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Composer, {
149985
150146
  value: edit2.value,
149986
150147
  cursor: edit2.cursor,
149987
150148
  selectionAnchor: edit2.selectionAnchor,
@@ -149991,9 +150152,9 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
149991
150152
  width,
149992
150153
  vim
149993
150154
  }, undefined, false, undefined, this);
149994
- const footerJsx = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
150155
+ const footerJsx = /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
149995
150156
  children: [
149996
- busy || linger ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Working, {
150157
+ busy || linger ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Working, {
149997
150158
  state: mascotState,
149998
150159
  skin: ghostSkin,
149999
150160
  verb,
@@ -150008,15 +150169,15 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150008
150169
  linger: linger && !busy,
150009
150170
  width
150010
150171
  }, undefined, false, undefined, this) : null,
150011
- busy ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(ActivityRail, {
150172
+ busy ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(ActivityRail, {
150012
150173
  items,
150013
150174
  width
150014
150175
  }, undefined, false, undefined, this) : null,
150015
- queued.length ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150176
+ queued.length ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150016
150177
  paddingX: 1,
150017
150178
  marginTop: 1,
150018
150179
  flexDirection: "column",
150019
- children: queued.map((q, i2) => /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150180
+ children: queued.map((q, i2) => /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150020
150181
  color: color.faint,
150021
150182
  children: [
150022
150183
  "↳ queued: ",
@@ -150024,11 +150185,11 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150024
150185
  ]
150025
150186
  }, i2, true, undefined, this))
150026
150187
  }, undefined, false, undefined, this) : null,
150027
- mode2 !== "normal" ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150188
+ mode2 !== "normal" ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150028
150189
  paddingX: 1,
150029
150190
  marginTop: 1,
150030
150191
  children: [
150031
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150192
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150032
150193
  color: color.accent,
150033
150194
  children: [
150034
150195
  glyph.notice,
@@ -150036,7 +150197,7 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150036
150197
  mode2 === "plan" ? "plan mode" : "auto-accept edits"
150037
150198
  ]
150038
150199
  }, undefined, true, undefined, this),
150039
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150200
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150040
150201
  color: color.faint,
150041
150202
  children: [
150042
150203
  " · ",
@@ -150046,14 +150207,14 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150046
150207
  }, undefined, true, undefined, this)
150047
150208
  ]
150048
150209
  }, undefined, true, undefined, this) : null,
150049
- search ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150210
+ search ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150050
150211
  paddingX: 1,
150051
150212
  children: [
150052
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150213
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150053
150214
  color: color.accent,
150054
150215
  children: "(reverse-i-search)"
150055
150216
  }, undefined, false, undefined, this),
150056
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150217
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150057
150218
  color: color.text,
150058
150219
  children: [
150059
150220
  "`",
@@ -150061,15 +150222,15 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150061
150222
  "`: "
150062
150223
  ]
150063
150224
  }, undefined, true, undefined, this),
150064
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150225
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150065
150226
  color: color.dim,
150066
150227
  children: searchHistory(historyRef.current, search.q, search.idx) ?? (search.q ? "(no match)" : "")
150067
150228
  }, undefined, false, undefined, this)
150068
150229
  ]
150069
150230
  }, undefined, true, undefined, this) : null,
150070
- copiedNotice ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150231
+ copiedNotice ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150071
150232
  paddingX: 1,
150072
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
150233
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
150073
150234
  color: color.ok,
150074
150235
  children: [
150075
150236
  glyph.notice,
@@ -150079,7 +150240,16 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150079
150240
  }, undefined, true, undefined, this)
150080
150241
  }, undefined, false, undefined, this) : null,
150081
150242
  quickPickerJsx,
150082
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(StatusBar, {
150243
+ statusPinned ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(StatusStrip, {
150244
+ ctxPct,
150245
+ tokens,
150246
+ contextWindow: model?.contextWindow ?? null,
150247
+ cost: estimateCost(sessionRef.current.turns),
150248
+ sub: stripSub,
150249
+ api: stripApi,
150250
+ width
150251
+ }, undefined, false, undefined, this) : null,
150252
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(StatusBar, {
150083
150253
  model: modelLabel,
150084
150254
  branch,
150085
150255
  routing,
@@ -150093,7 +150263,7 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150093
150263
  effort: displayEffort,
150094
150264
  online
150095
150265
  }, undefined, false, undefined, this),
150096
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150266
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150097
150267
  height: PALETTE_ROWS,
150098
150268
  flexDirection: "column",
150099
150269
  children: paletteJsx
@@ -150101,26 +150271,26 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150101
150271
  composerJsx
150102
150272
  ]
150103
150273
  }, undefined, true, undefined, this);
150104
- const inlineFooterJsx = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
150274
+ const inlineFooterJsx = /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
150105
150275
  children: [
150106
150276
  paletteJsx,
150107
150277
  composerJsx
150108
150278
  ]
150109
150279
  }, undefined, true, undefined, this);
150110
150280
  if (fullscreen) {
150111
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150281
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150112
150282
  flexDirection: "column",
150113
150283
  width,
150114
150284
  height: rows,
150115
150285
  children: [
150116
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Banner, {
150286
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Banner, {
150117
150287
  model: modelLabel,
150118
150288
  cwd: basename3(process.cwd()),
150119
150289
  width
150120
150290
  }, undefined, false, undefined, this),
150121
- panel ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150291
+ panel ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150122
150292
  paddingX: 1,
150123
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Panel, {
150293
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Panel, {
150124
150294
  panel,
150125
150295
  width: panelW,
150126
150296
  height: transcriptHeight,
@@ -150129,14 +150299,14 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150129
150299
  currentModelId: panelCurrentModel,
150130
150300
  staticLines: panelStaticLines
150131
150301
  }, undefined, false, undefined, this)
150132
- }, undefined, false, undefined, this) : welcome ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150302
+ }, undefined, false, undefined, this) : welcome ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150133
150303
  height: transcriptHeight,
150134
150304
  flexDirection: "column",
150135
150305
  justifyContent: "center",
150136
150306
  children: hero
150137
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150307
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150138
150308
  paddingX: 1,
150139
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Viewport, {
150309
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Viewport, {
150140
150310
  lines,
150141
150311
  scrollTop: effScroll,
150142
150312
  height: transcriptHeight,
@@ -150148,24 +150318,24 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150148
150318
  ]
150149
150319
  }, undefined, true, undefined, this);
150150
150320
  }
150151
- const banner = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Banner, {
150321
+ const banner = /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Banner, {
150152
150322
  model: modelLabel,
150153
150323
  cwd: basename3(process.cwd()),
150154
150324
  width
150155
150325
  }, undefined, false, undefined, this);
150156
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150326
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150157
150327
  flexDirection: "column",
150158
150328
  width,
150159
150329
  children: [
150160
- welcome ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
150330
+ welcome ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
150161
150331
  children: [
150162
150332
  banner,
150163
- /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
150333
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
150164
150334
  marginTop: 1,
150165
150335
  children: hero
150166
150336
  }, undefined, false, undefined, this)
150167
150337
  ]
150168
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Transcript, {
150338
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Transcript, {
150169
150339
  items,
150170
150340
  width,
150171
150341
  header: banner,
@@ -150179,7 +150349,7 @@ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
150179
150349
  // src/cli.tsx
150180
150350
  init_providers();
150181
150351
  init_permission();
150182
- var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
150352
+ var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
150183
150353
  process.env.LANG = process.env.LANG || "en_US.UTF-8";
150184
150354
  process.env.LC_ALL = process.env.LC_ALL || "en_US.UTF-8";
150185
150355
  var VERSION16 = "0.1.32";
@@ -150677,7 +150847,7 @@ if (fullscreen)
150677
150847
  process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H");
150678
150848
  if (mouse)
150679
150849
  process.stdout.write("\x1B[?1000h\x1B[?1002h\x1B[?1006h");
150680
- var app = render_default(/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(App2, {
150850
+ var app = render_default(/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(App2, {
150681
150851
  selector,
150682
150852
  fullscreen,
150683
150853
  resumeId