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.
- package/dist/cli.mjs +391 -221
- 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/
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
140839
|
+
return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
140709
140840
|
backgroundColor: color.panelBg,
|
|
140710
140841
|
children: [
|
|
140711
140842
|
ln.slice(0, curCol),
|
|
140712
|
-
/* @__PURE__ */
|
|
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__ */
|
|
140852
|
+
return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
140722
140853
|
backgroundColor: color.panelBg,
|
|
140723
140854
|
children: [
|
|
140724
140855
|
ln.slice(0, selStart),
|
|
140725
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
140869
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
140739
140870
|
paddingX: 1,
|
|
140740
|
-
children: vim !== "off" ? /* @__PURE__ */
|
|
140871
|
+
children: vim !== "off" ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(jsx_dev_runtime8.Fragment, {
|
|
140741
140872
|
children: [
|
|
140742
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
140888
|
+
busy ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
140758
140889
|
paddingX: 1,
|
|
140759
140890
|
children: [
|
|
140760
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
140906
|
+
}, undefined, true, undefined, this) : value === "" ? /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
140776
140907
|
paddingX: 1,
|
|
140777
140908
|
children: [
|
|
140778
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
140932
|
+
children: lines.map((ln, i2) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
140802
140933
|
children: [
|
|
140803
|
-
/* @__PURE__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
141868
|
+
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
|
|
141738
141869
|
flexDirection: "column",
|
|
141739
|
-
children: data.map((row, i2) => /* @__PURE__ */
|
|
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__ */
|
|
141898
|
+
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
|
|
141768
141899
|
marginLeft: shake,
|
|
141769
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
141909
|
+
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
|
|
141779
141910
|
flexDirection: "column",
|
|
141780
|
-
children: lines.map((l, i2) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
141924
|
+
size2 !== "none" ? kitty ? /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(KittyGhost, {
|
|
141794
141925
|
variant: skin,
|
|
141795
141926
|
size: size2
|
|
141796
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
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__ */
|
|
141932
|
+
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
|
|
141802
141933
|
marginTop: size2 === "none" ? 0 : 1,
|
|
141803
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
141810
|
-
children: /* @__PURE__ */
|
|
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
|
|
141951
|
+
var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
|
|
141821
141952
|
function PermissionPrompt({ req, width }) {
|
|
141822
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
141961
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
141831
141962
|
children: [
|
|
141832
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
141980
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
141850
141981
|
marginTop: 1,
|
|
141851
|
-
children: /* @__PURE__ */
|
|
141982
|
+
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
141852
141983
|
flexGrow: 1,
|
|
141853
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
141991
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
|
|
141861
141992
|
marginTop: 1,
|
|
141862
141993
|
children: [
|
|
141863
|
-
/* @__PURE__ */
|
|
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__ */
|
|
141999
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
141869
142000
|
color: color.dim,
|
|
141870
142001
|
children: " once"
|
|
141871
142002
|
}, undefined, false, undefined, this),
|
|
141872
|
-
/* @__PURE__ */
|
|
142003
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
141873
142004
|
color: color.faint,
|
|
141874
142005
|
children: " "
|
|
141875
142006
|
}, undefined, false, undefined, this),
|
|
141876
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
142020
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
141890
142021
|
color: color.faint,
|
|
141891
142022
|
children: " "
|
|
141892
142023
|
}, undefined, false, undefined, this),
|
|
141893
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
142033
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
141903
142034
|
color: color.faint,
|
|
141904
142035
|
children: " "
|
|
141905
142036
|
}, undefined, false, undefined, this),
|
|
141906
|
-
/* @__PURE__ */
|
|
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__ */
|
|
142042
|
+
/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
141912
142043
|
color: color.dim,
|
|
141913
142044
|
children: " deny"
|
|
141914
142045
|
}, undefined, false, undefined, this),
|
|
141915
|
-
/* @__PURE__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
142097
|
+
/* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
141967
142098
|
color: labelColor,
|
|
141968
142099
|
children: [
|
|
141969
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
142114
|
+
!linger ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
141984
142115
|
children: [
|
|
141985
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
142227
|
+
return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
142097
142228
|
width,
|
|
142098
142229
|
children: [
|
|
142099
|
-
/* @__PURE__ */
|
|
142230
|
+
/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
|
|
142100
142231
|
flexDirection: "column",
|
|
142101
142232
|
width: width - 1,
|
|
142102
|
-
children: padded.map((l, i2) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
142370
|
-
return inner.map((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
|
|
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) >=
|
|
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
|
|
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__ */
|
|
143565
|
+
body = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
143435
143566
|
paddingX: 1,
|
|
143436
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
143581
|
+
body = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
143451
143582
|
flexDirection: "column",
|
|
143452
143583
|
paddingX: 1,
|
|
143453
|
-
children: rows.length === 0 ? /* @__PURE__ */
|
|
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__ */
|
|
143589
|
+
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
143459
143590
|
backgroundColor: sel ? color.accentBg : undefined,
|
|
143460
143591
|
children: [
|
|
143461
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
143648
|
+
body = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
143518
143649
|
flexDirection: "column",
|
|
143519
143650
|
paddingX: 1,
|
|
143520
|
-
children: rows.length === 0 ? /* @__PURE__ */
|
|
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__ */
|
|
143661
|
+
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
143531
143662
|
backgroundColor: sel ? color.accentBg : undefined,
|
|
143532
143663
|
children: [
|
|
143533
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
143691
|
+
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
143561
143692
|
flexDirection: "column",
|
|
143562
143693
|
width,
|
|
143563
143694
|
height,
|
|
143564
143695
|
children: [
|
|
143565
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
143718
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
143588
143719
|
width,
|
|
143589
143720
|
paddingX: 1,
|
|
143590
|
-
children: /* @__PURE__ */
|
|
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 <q> (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 <q> (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
|
|
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__ */
|
|
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__ */
|
|
146930
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
146800
146931
|
color: color.accentDim,
|
|
146801
146932
|
children: "activity "
|
|
146802
146933
|
}, undefined, false, undefined, this),
|
|
146803
|
-
/* @__PURE__ */
|
|
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__ */
|
|
146944
|
+
return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146814
146945
|
flexDirection: "column",
|
|
146815
146946
|
alignItems: "center",
|
|
146816
146947
|
children: [
|
|
146817
|
-
/* @__PURE__ */
|
|
146948
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(MascotSplash, {
|
|
146818
146949
|
skin,
|
|
146819
146950
|
size: splashSize
|
|
146820
146951
|
}, undefined, false, undefined, this),
|
|
146821
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
146977
|
+
detected > 0 ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
|
|
146847
146978
|
children: [
|
|
146848
|
-
/* @__PURE__ */
|
|
146979
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146849
146980
|
children: [
|
|
146850
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
146999
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146869
147000
|
marginTop: 1,
|
|
146870
147001
|
children: [
|
|
146871
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
147012
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146882
147013
|
marginTop: 1,
|
|
146883
147014
|
children: [
|
|
146884
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
147026
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
|
|
146896
147027
|
children: [
|
|
146897
|
-
/* @__PURE__ */
|
|
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__ */
|
|
147032
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146902
147033
|
marginTop: 1,
|
|
146903
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
147049
|
+
state.hasClaudeCli && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146919
147050
|
children: [
|
|
146920
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
147061
|
+
state.hasCodexCli && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146931
147062
|
children: [
|
|
146932
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
147077
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
146947
147078
|
marginTop: 1,
|
|
146948
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
150009
|
+
const hero = /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
149849
150010
|
flexDirection: "column",
|
|
149850
150011
|
alignItems: "center",
|
|
149851
|
-
children: setupRequired ? /* @__PURE__ */
|
|
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__ */
|
|
150017
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
|
|
149857
150018
|
children: [
|
|
149858
|
-
/* @__PURE__ */
|
|
150019
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(MascotSplash, {
|
|
149859
150020
|
skin: ghostSkin,
|
|
149860
150021
|
size: splashSize
|
|
149861
150022
|
}, undefined, false, undefined, this),
|
|
149862
|
-
/* @__PURE__ */
|
|
150023
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
149863
150024
|
marginTop: 1,
|
|
149864
150025
|
children: [
|
|
149865
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
150037
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
149877
150038
|
color: color.accentDim,
|
|
149878
150039
|
children: "/"
|
|
149879
150040
|
}, undefined, false, undefined, this),
|
|
149880
|
-
/* @__PURE__ */
|
|
150041
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
149881
150042
|
color: color.dim,
|
|
149882
150043
|
children: "commands "
|
|
149883
150044
|
}, undefined, false, undefined, this),
|
|
149884
|
-
/* @__PURE__ */
|
|
150045
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
149885
150046
|
color: color.accentDim,
|
|
149886
150047
|
children: "@"
|
|
149887
150048
|
}, undefined, false, undefined, this),
|
|
149888
|
-
/* @__PURE__ */
|
|
150049
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
149889
150050
|
color: color.dim,
|
|
149890
150051
|
children: "files "
|
|
149891
150052
|
}, undefined, false, undefined, this),
|
|
149892
|
-
/* @__PURE__ */
|
|
150053
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
149893
150054
|
color: color.accentDim,
|
|
149894
150055
|
children: "!"
|
|
149895
150056
|
}, undefined, false, undefined, this),
|
|
149896
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
150068
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
|
|
149908
150069
|
color: color.faint,
|
|
149909
150070
|
children: [
|
|
149910
150071
|
"new here? press ",
|
|
149911
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
150120
|
+
/* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
149960
150121
|
paddingX: 1,
|
|
149961
150122
|
children: [
|
|
149962
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
150142
|
+
const composerJsx = perm ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(PermissionPrompt, {
|
|
149982
150143
|
req: perm,
|
|
149983
150144
|
width
|
|
149984
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
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__ */
|
|
150155
|
+
const footerJsx = /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
|
|
149995
150156
|
children: [
|
|
149996
|
-
busy || linger ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
150188
|
+
mode2 !== "normal" ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
150028
150189
|
paddingX: 1,
|
|
150029
150190
|
marginTop: 1,
|
|
150030
150191
|
children: [
|
|
150031
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
150210
|
+
search ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
150050
150211
|
paddingX: 1,
|
|
150051
150212
|
children: [
|
|
150052
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
150231
|
+
copiedNotice ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
150071
150232
|
paddingX: 1,
|
|
150072
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
150291
|
+
panel ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
150122
150292
|
paddingX: 1,
|
|
150123
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
150307
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
150138
150308
|
paddingX: 1,
|
|
150139
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
150326
|
+
return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
|
|
150157
150327
|
flexDirection: "column",
|
|
150158
150328
|
width,
|
|
150159
150329
|
children: [
|
|
150160
|
-
welcome ? /* @__PURE__ */
|
|
150330
|
+
welcome ? /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(jsx_dev_runtime14.Fragment, {
|
|
150161
150331
|
children: [
|
|
150162
150332
|
banner,
|
|
150163
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
150850
|
+
var app = render_default(/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(App2, {
|
|
150681
150851
|
selector,
|
|
150682
150852
|
fullscreen,
|
|
150683
150853
|
resumeId
|