@kenkaiiii/gg-boss 4.3.163 → 4.3.164

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +3 -3
  2. package/dist/{chunk-YNWFCUMR.js → chunk-DZO3FVYX.js} +3 -3
  3. package/dist/chunk-DZO3FVYX.js.map +1 -0
  4. package/dist/{chunk-QT366Y52.js → chunk-JEGMYLRS.js} +3 -3
  5. package/dist/{chunk-WJ4S4TOY.js → chunk-NA54UQR4.js} +2 -2
  6. package/dist/{chunk-JBKZOBJ7.js → chunk-SSDL6C2P.js} +27834 -13526
  7. package/dist/{chunk-JBKZOBJ7.js.map → chunk-SSDL6C2P.js.map} +1 -1
  8. package/dist/cli.js +1695 -996
  9. package/dist/cli.js.map +1 -1
  10. package/dist/{devtools-526EIB4G.js → devtools-OQIIURAD.js} +11 -33
  11. package/dist/{devtools-526EIB4G.js.map → devtools-OQIIURAD.js.map} +1 -1
  12. package/dist/{dist-VXOVSHZ5.js → dist-IGN2W3JX.js} +2 -2
  13. package/dist/{chunk-EZYGVECW.js → ignore-3XU7YNRW.js} +3 -6
  14. package/dist/index.js +4 -6
  15. package/dist/index.js.map +1 -1
  16. package/dist/{chunk-RMSZMSH5.js → out-NHVJUVVH.js} +3 -6
  17. package/dist/{pixel-WPYTQADG.js → pixel-OQO4WMWJ.js} +4 -4
  18. package/dist/{pixel-fix-4WGZAJ5W.js → pixel-fix-JKVDORFT.js} +3 -3
  19. package/package.json +5 -5
  20. package/dist/chunk-YNWFCUMR.js.map +0 -1
  21. package/dist/ignore-AXNNXJD4.js +0 -7
  22. package/dist/out-NH6HQBFM.js +0 -7
  23. package/dist/out-NH6HQBFM.js.map +0 -1
  24. package/dist/pixel-WPYTQADG.js.map +0 -1
  25. /package/dist/{chunk-QT366Y52.js.map → chunk-JEGMYLRS.js.map} +0 -0
  26. /package/dist/{chunk-WJ4S4TOY.js.map → chunk-NA54UQR4.js.map} +0 -0
  27. /package/dist/{dist-VXOVSHZ5.js.map → dist-IGN2W3JX.js.map} +0 -0
  28. /package/dist/{chunk-EZYGVECW.js.map → ignore-3XU7YNRW.js.map} +0 -0
  29. /package/dist/{chunk-RMSZMSH5.js.map → out-NHVJUVVH.js.map} +0 -0
  30. /package/dist/{ignore-AXNNXJD4.js.map → pixel-OQO4WMWJ.js.map} +0 -0
  31. /package/dist/{pixel-fix-4WGZAJ5W.js.map → pixel-fix-JKVDORFT.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,32 +1,43 @@
1
1
  #!/usr/bin/env -S node --max-old-space-size=8192 --expose-gc
2
2
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
3
3
  import {
4
- ActivityIndicator,
5
4
  AnimationProvider,
6
5
  AssistantMessage,
7
6
  Box_default,
7
+ ChatControls,
8
+ ChatInputStack,
9
+ ChatLayout,
10
+ ChatLivePane,
8
11
  CompactionDone,
9
12
  CompactionSpinner,
10
13
  GGBoss,
11
14
  InputArea,
12
15
  MODELS,
13
16
  MessageResponse,
14
- ModelSelector,
15
- SelectList,
16
- Static,
17
- StreamingArea,
17
+ RESPONSE_LEFT_PADDING,
18
18
  TerminalSizeProvider,
19
19
  Text,
20
20
  ThemeContext,
21
21
  ToolExecution,
22
+ ToolGroupExecution,
22
23
  ToolUseLoader,
24
+ TranscriptItemFrame,
23
25
  UserMessage,
24
26
  bossStore,
27
+ bossToolGroupRenderers,
28
+ buildToolGroupSummary,
25
29
  closeLogger,
30
+ color,
31
+ dim,
32
+ formatHistoryWrite,
26
33
  getAppPaths,
27
34
  getBossState,
28
35
  getContextWindow,
36
+ getNextThinkingLevel,
29
37
  getSplashAudioDurationMs,
38
+ getTranscriptItemMarginTop,
39
+ gradientLine,
40
+ indent,
30
41
  initLogger,
31
42
  loadSettings,
32
43
  loadTheme,
@@ -36,9 +47,16 @@ import {
36
47
  require_jsx_runtime,
37
48
  require_react,
38
49
  saveSettings,
50
+ serializeCompletedItemToTerminalHistory,
39
51
  setStreamDiagnostic,
52
+ shouldSeparateTranscriptItemKinds,
53
+ shouldTopSpaceStreamingAssistant,
54
+ stripAnsi,
55
+ stripTerminalFocusSequences,
40
56
  subscribeToBossStore,
41
57
  tasksStore,
58
+ toolTonePalette,
59
+ truncatePlain,
42
60
  useAnimationActive,
43
61
  useAnimationTick,
44
62
  useBossState,
@@ -48,18 +66,17 @@ import {
48
66
  useTheme,
49
67
  use_app_default,
50
68
  use_input_default,
51
- use_stdout_default
52
- } from "./chunk-JBKZOBJ7.js";
53
- import "./chunk-RMSZMSH5.js";
54
- import "./chunk-EZYGVECW.js";
55
- import "./chunk-QT366Y52.js";
69
+ use_stdout_default,
70
+ wrapPlain
71
+ } from "./chunk-SSDL6C2P.js";
72
+ import "./chunk-JEGMYLRS.js";
56
73
  import {
57
74
  source_default
58
- } from "./chunk-WJ4S4TOY.js";
75
+ } from "./chunk-NA54UQR4.js";
59
76
  import {
60
77
  __toESM,
61
78
  init_esm_shims
62
- } from "./chunk-YNWFCUMR.js";
79
+ } from "./chunk-DZO3FVYX.js";
63
80
 
64
81
  // src/cli.ts
65
82
  init_esm_shims();
@@ -344,7 +361,7 @@ init_esm_shims();
344
361
  // package.json
345
362
  var package_default = {
346
363
  name: "@kenkaiiii/gg-boss",
347
- version: "4.3.163",
364
+ version: "4.3.164",
348
365
  type: "module",
349
366
  description: "Orchestrator agent that drives multiple ggcoder sessions across projects from a single chat",
350
367
  license: "MIT",
@@ -374,7 +391,7 @@ var package_default = {
374
391
  "@types/node": "^25.6.0",
375
392
  "@types/react": "^19.2.14",
376
393
  chalk: "^5.6.2",
377
- ink: "^7.0.2",
394
+ ink: "6.8.0",
378
395
  react: "^19.2.5",
379
396
  tsup: "^8.5.1",
380
397
  typescript: "^6.0.3",
@@ -422,24 +439,6 @@ var GRADIENT = [
422
439
  "#b91c1c"
423
440
  // red-700 (slight darker tail)
424
441
  ];
425
- var PULSE_COLORS = [
426
- "#dc2626",
427
- // crimson
428
- "#e11d48",
429
- // rose
430
- "#be185d",
431
- // wine
432
- "#a21caf",
433
- // magenta
434
- "#c026d3",
435
- // fuchsia
436
- "#a21caf",
437
- // back
438
- "#be185d",
439
- // back
440
- "#e11d48"
441
- // back
442
- ];
443
442
  var COLORS = {
444
443
  primary: "#e11d48",
445
444
  // crimson-rose — main brand color
@@ -502,9 +501,9 @@ function GradientText({ text }) {
502
501
  if (ch === " ") {
503
502
  chars.push(ch);
504
503
  } else {
505
- const color = GRADIENT[colorIdx % GRADIENT.length];
504
+ const color2 = GRADIENT[colorIdx % GRADIENT.length];
506
505
  chars.push(
507
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color, children: ch }, i)
506
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: color2, children: ch }, i)
508
507
  );
509
508
  colorIdx++;
510
509
  }
@@ -1001,8 +1000,14 @@ async function saveBossTelegramConfig(config) {
1001
1000
  }
1002
1001
  function formatItemForTelegram(item) {
1003
1002
  switch (item.kind) {
1003
+ case "banner":
1004
1004
  case "user":
1005
- case "tool":
1005
+ case "tool_start":
1006
+ case "tool_done":
1007
+ case "tool_group":
1008
+ case "compacting":
1009
+ case "compacted":
1010
+ case "stopped":
1006
1011
  case "worker_event":
1007
1012
  return null;
1008
1013
  case "assistant": {
@@ -1533,7 +1538,7 @@ function printSetupBanner() {
1533
1538
 
1534
1539
  // src/orchestrator-app.tsx
1535
1540
  init_esm_shims();
1536
- var import_react10 = __toESM(require_react(), 1);
1541
+ var import_react15 = __toESM(require_react(), 1);
1537
1542
 
1538
1543
  // ../ggcoder/dist/ui/components/index.js
1539
1544
  init_esm_shims();
@@ -1548,24 +1553,90 @@ init_esm_shims();
1548
1553
  var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
1549
1554
  var import_react4 = __toESM(require_react(), 1);
1550
1555
 
1551
- // ../ggcoder/dist/ui/components/SessionSelector.js
1556
+ // ../ggcoder/dist/ui/components/SelectList.js
1552
1557
  init_esm_shims();
1553
1558
  var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
1554
1559
  var import_react5 = __toESM(require_react(), 1);
1560
+ function SelectList({ items, onSelect, onCancel, initialIndex = 0, windowSize }) {
1561
+ const theme = useTheme();
1562
+ const [selectedIndex, setSelectedIndex] = (0, import_react5.useState)(initialIndex);
1563
+ const [filter, setFilter] = (0, import_react5.useState)("");
1564
+ const filtered = (0, import_react5.useMemo)(() => {
1565
+ if (!filter)
1566
+ return items;
1567
+ const lower = filter.toLowerCase();
1568
+ return items.filter((item) => item.label.toLowerCase().includes(lower) || item.value.toLowerCase().includes(lower));
1569
+ }, [items, filter]);
1570
+ use_input_default((input, key) => {
1571
+ const inputWithoutFocusReports = stripTerminalFocusSequences(input);
1572
+ if (!inputWithoutFocusReports && input)
1573
+ return;
1574
+ input = inputWithoutFocusReports;
1575
+ if (key.escape) {
1576
+ onCancel();
1577
+ return;
1578
+ }
1579
+ if (key.return) {
1580
+ if (filtered.length > 0) {
1581
+ onSelect(filtered[selectedIndex].value);
1582
+ }
1583
+ return;
1584
+ }
1585
+ if (key.upArrow) {
1586
+ setSelectedIndex((i) => Math.max(0, i - 1));
1587
+ return;
1588
+ }
1589
+ if (key.downArrow) {
1590
+ setSelectedIndex((i) => Math.min(filtered.length - 1, i + 1));
1591
+ return;
1592
+ }
1593
+ if (key.backspace || key.delete) {
1594
+ setFilter((f) => f.slice(0, -1));
1595
+ setSelectedIndex(0);
1596
+ return;
1597
+ }
1598
+ if (input && !key.ctrl && !key.meta) {
1599
+ setFilter((f) => f + input);
1600
+ setSelectedIndex(0);
1601
+ }
1602
+ });
1603
+ const total = filtered.length;
1604
+ const clampedIndex = Math.min(Math.max(selectedIndex, 0), Math.max(0, total - 1));
1605
+ const useWindow = windowSize !== void 0 && windowSize > 0 && total > windowSize;
1606
+ const start = useWindow ? Math.max(0, Math.min(clampedIndex - Math.floor(windowSize / 2), total - windowSize)) : 0;
1607
+ const end = useWindow ? Math.min(start + windowSize, total) : total;
1608
+ const visible = useWindow ? filtered.slice(start, end) : filtered;
1609
+ const hasAbove = useWindow && start > 0;
1610
+ const hasBelow = useWindow && end < total;
1611
+ return (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [filter && (0, import_jsx_runtime5.jsx)(Box_default, { marginBottom: 1, children: (0, import_jsx_runtime5.jsxs)(Text, { color: theme.textDim, children: ["Filter: ", filter] }) }), hasAbove && (0, import_jsx_runtime5.jsxs)(Text, { color: theme.textDim, children: [" \u2191 ", start, " more"] }), visible.map((item, i) => {
1612
+ const index = useWindow ? start + i : i;
1613
+ return (0, import_jsx_runtime5.jsxs)(Box_default, { children: [(0, import_jsx_runtime5.jsxs)(Text, { color: index === clampedIndex ? theme.primary : theme.text, children: [index === clampedIndex ? "\u276F " : " ", item.label] }), item.description && (0, import_jsx_runtime5.jsxs)(Text, { color: theme.textDim, children: [" \u2014 ", item.description] })] }, item.value);
1614
+ }), hasBelow && (0, import_jsx_runtime5.jsxs)(Text, { color: theme.textDim, children: [" \u2193 ", total - end, " more"] }), filtered.length === 0 && (0, import_jsx_runtime5.jsx)(Text, { color: theme.textDim, children: "No matches" }), (0, import_jsx_runtime5.jsx)(Box_default, { marginTop: 1, children: (0, import_jsx_runtime5.jsx)(Text, { color: theme.textDim, children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }) })] });
1615
+ }
1555
1616
 
1556
- // ../ggcoder/dist/ui/components/SettingsSelector.js
1617
+ // ../ggcoder/dist/ui/components/SessionSelector.js
1557
1618
  init_esm_shims();
1558
1619
  var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
1559
1620
  var import_react6 = __toESM(require_react(), 1);
1560
1621
 
1561
- // src/boss-footer.tsx
1622
+ // ../ggcoder/dist/ui/components/SettingsSelector.js
1562
1623
  init_esm_shims();
1563
- var import_react7 = __toESM(require_react(), 1);
1564
1624
  var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
1625
+ var import_react7 = __toESM(require_react(), 1);
1626
+
1627
+ // src/boss-chat-screen.tsx
1628
+ init_esm_shims();
1629
+ var import_react13 = __toESM(require_react(), 1);
1630
+
1631
+ // src/boss-footer.tsx
1632
+ init_esm_shims();
1633
+ var import_react8 = __toESM(require_react(), 1);
1634
+ var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
1565
1635
  var PARTIAL_BLOCKS = [" ", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588"];
1566
1636
  var LIGHT_SHADE = "\u2591";
1637
+ var BAR_WIDTH = 8;
1567
1638
  var SHORT_MODELS = {
1568
- "claude-opus-4-7": "Opus",
1639
+ "claude-opus-4-8": "Opus",
1569
1640
  "claude-sonnet-4-6": "Sonnet",
1570
1641
  "claude-haiku-4-5": "Haiku",
1571
1642
  "claude-haiku-4-5-20251001": "Haiku",
@@ -1577,17 +1648,51 @@ var SHORT_MODELS = {
1577
1648
  function shortModel(model) {
1578
1649
  return SHORT_MODELS[model] ?? model;
1579
1650
  }
1580
- function getContextPercent(model, tokensIn) {
1651
+ function getBossFooterContextPercent(model, tokensIn) {
1581
1652
  const limit = getContextWindow(model);
1582
1653
  if (!limit || tokensIn === 0) return 0;
1583
1654
  return Math.round(tokensIn / limit * 100);
1584
1655
  }
1656
+ function getContextColor(pct, theme) {
1657
+ if (pct >= 80) return theme.error;
1658
+ if (pct >= 50) return theme.warning;
1659
+ return theme.success;
1660
+ }
1661
+ function getThinkingColor(level, theme) {
1662
+ if (!level) return theme.textDim;
1663
+ if (level === "low") return theme.textMuted;
1664
+ if (level === "medium") return theme.accent;
1665
+ if (level === "high") return theme.warning;
1666
+ return COLORS.accent;
1667
+ }
1668
+ function getBossFooterThinkingLabel(level) {
1669
+ return level ? `Thinking ${level}` : "Thinking off";
1670
+ }
1585
1671
  var SHORT_RADIO = {
1586
1672
  "somafm-groove-salad": "Groove Salad",
1587
1673
  "somafm-drone-zone": "Drone Zone",
1588
1674
  "radio-paradise": "Radio Paradise",
1589
1675
  "george-fm": "George FM"
1590
1676
  };
1677
+ function renderContextBar({
1678
+ contextPct,
1679
+ contextColor,
1680
+ dimColor
1681
+ }) {
1682
+ const fillFloat = Math.min(contextPct / 100 * BAR_WIDTH, BAR_WIDTH);
1683
+ const barChars = [];
1684
+ for (let i = 0; i < BAR_WIDTH; i++) {
1685
+ const cellFill = Math.max(0, Math.min(1, fillFloat - i));
1686
+ const eighths = Math.round(cellFill * 8);
1687
+ barChars.push(
1688
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: eighths > 0 ? contextColor : dimColor, children: eighths > 0 ? PARTIAL_BLOCKS[eighths] : LIGHT_SHADE }, i)
1689
+ );
1690
+ }
1691
+ return barChars;
1692
+ }
1693
+ function getBossFooterScopeLabel(scope) {
1694
+ return scope === "all" ? "all projects" : scope;
1695
+ }
1591
1696
  function BossFooter({
1592
1697
  bossModel,
1593
1698
  workerModel,
@@ -1595,149 +1700,231 @@ function BossFooter({
1595
1700
  exitPending,
1596
1701
  bossThinkingLevel,
1597
1702
  updatePending,
1598
- currentRadioStationId
1703
+ currentRadioStationId,
1704
+ scope
1599
1705
  }) {
1600
1706
  const theme = useTheme();
1601
1707
  const { columns } = useTerminalSize();
1602
1708
  if (exitPending) {
1603
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.warning, children: "Press Ctrl+C again to exit" }) });
1604
- }
1605
- const contextPct = getContextPercent(bossModel, tokensIn);
1606
- const contextColor = contextPct >= 80 ? theme.error : contextPct >= 50 ? theme.warning : theme.success;
1607
- const sep = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.border, children: " \u2502 " });
1608
- const barWidth = 8;
1609
- const fillFloat = Math.min(contextPct / 100 * barWidth, barWidth);
1610
- const barChars = [];
1611
- for (let i = 0; i < barWidth; i++) {
1612
- const cellFill = Math.max(0, Math.min(1, fillFloat - i));
1613
- const eighths = Math.round(cellFill * 8);
1614
- if (eighths === 8) {
1615
- barChars.push(
1616
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: contextColor, children: PARTIAL_BLOCKS[8] }, i)
1617
- );
1618
- } else if (eighths > 0) {
1619
- barChars.push(
1620
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: contextColor, children: PARTIAL_BLOCKS[eighths] }, i)
1621
- );
1622
- } else {
1623
- barChars.push(
1624
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, children: LIGHT_SHADE }, i)
1625
- );
1626
- }
1709
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { paddingLeft: 1, paddingRight: 1, width: columns, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.warning, children: "Press Ctrl+C again to exit" }) });
1627
1710
  }
1711
+ const contextPct = getBossFooterContextPercent(bossModel, tokensIn);
1712
+ const contextColor = getContextColor(contextPct, theme);
1713
+ const sep = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.border, children: " \u2502 " });
1714
+ const bossName = shortModel(bossModel);
1715
+ const workerName = shortModel(workerModel);
1716
+ const thinkingText = getBossFooterThinkingLabel(bossThinkingLevel);
1628
1717
  const radioName = currentRadioStationId ? SHORT_RADIO[currentRadioStationId] ?? currentRadioStationId : null;
1629
- const bossM = shortModel(bossModel);
1630
- const wkrM = shortModel(workerModel);
1631
- const estFull = 2 + 12 + // bar + " 99%"
1632
- 3 + 5 + bossM.length + // " │ boss <model>"
1633
- 3 + 8 + wkrM.length + // " │ workers <model>"
1634
- 3 + 12 + // " │ Thinking off"
1635
- (radioName ? 3 + 2 + radioName.length : 0) + // " │ ♪ Name"
1636
- (updatePending ? 3 + 28 : 0);
1637
- const dropLabels = estFull > columns;
1638
- const dropThinking = estFull > columns + 14;
1639
- const useShortUpdate = updatePending && estFull > columns + 6;
1640
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { paddingX: 1, width: columns, children: [
1641
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexGrow: 1 }),
1642
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexShrink: 0, children: [
1643
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: barChars }),
1644
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: contextColor, children: [
1645
- " ",
1646
- contextPct,
1647
- "%"
1648
- ] }),
1649
- sep,
1650
- !dropLabels && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, children: "boss " }),
1651
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: COLORS.primary, bold: true, children: bossM }),
1718
+ const updateText = updatePending ? "Update ready. Restart GG Boss." : null;
1719
+ const leftText = getBossFooterScopeLabel(scope);
1720
+ const barChars = renderContextBar({
1721
+ contextPct,
1722
+ contextColor,
1723
+ dimColor: theme.textDim
1724
+ });
1725
+ const rightLen = BAR_WIDTH + 1 + String(contextPct).length + 3 + bossName.length + 3 + "workers ".length + workerName.length + 3 + thinkingText.length + (radioName ? 3 + 2 + radioName.length : 0) + (updateText ? 3 + updateText.length : 0);
1726
+ const availableWidth = columns - 2;
1727
+ const fitsOnOneLine = leftText.length + rightLen <= availableWidth;
1728
+ const hideRadio = !!radioName && leftText.length + rightLen > availableWidth + 8;
1729
+ const compactUpdate = !!updateText && leftText.length + rightLen > availableWidth + 12;
1730
+ const rightContent = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: barChars }),
1732
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: contextColor, children: [
1733
+ " ",
1734
+ contextPct,
1735
+ "%"
1736
+ ] }),
1737
+ sep,
1738
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.primary, bold: true, children: bossName }),
1739
+ sep,
1740
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: "workers " }),
1741
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: COLORS.accent, bold: true, children: workerName }),
1742
+ sep,
1743
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: getThinkingColor(bossThinkingLevel, theme), bold: bossThinkingLevel === "high", children: thinkingText }),
1744
+ radioName && !hideRadio && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1652
1745
  sep,
1653
- !dropLabels && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, children: "workers " }),
1654
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: COLORS.accent, bold: true, children: wkrM }),
1655
- !dropThinking && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1656
- sep,
1657
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: bossThinkingLevel ? theme.accent : theme.textDim, children: bossThinkingLevel ? "Thinking on" : "Thinking off" })
1658
- ] }),
1659
- radioName && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1660
- sep,
1661
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: theme.secondary ?? theme.accent, children: [
1662
- "\u266A ",
1663
- radioName
1664
- ] })
1665
- ] }),
1666
- updatePending && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1667
- sep,
1668
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.success, bold: true, wrap: "truncate", children: useShortUpdate ? "Update ready" : "Update ready. Restart GG Boss." })
1746
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.secondary, children: [
1747
+ "\u266A ",
1748
+ radioName
1669
1749
  ] })
1750
+ ] }),
1751
+ updateText && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1752
+ sep,
1753
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.success, bold: true, wrap: "truncate", children: compactUpdate ? "Update ready" : updateText })
1670
1754
  ] })
1671
1755
  ] });
1756
+ if (fitsOnOneLine) {
1757
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { paddingLeft: 1, paddingRight: 1, width: columns, children: [
1758
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: leftText }) }),
1759
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexShrink: 0, children: rightContent })
1760
+ ] });
1761
+ }
1762
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, width: columns, children: [
1763
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: leftText }) }),
1764
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { children: rightContent })
1765
+ ] });
1672
1766
  }
1673
1767
 
1674
- // src/slash-commands.ts
1768
+ // src/boss-model-selector.tsx
1675
1769
  init_esm_shims();
1676
- var BOSS_SLASH_COMMANDS = [
1677
- { name: "help", aliases: ["?"], description: "Show available commands" },
1678
- { name: "model-boss", aliases: [], description: "Switch the orchestrator's model" },
1679
- { name: "model-workers", aliases: [], description: "Switch every worker's model" },
1680
- { name: "compact", aliases: [], description: "Compact the boss's context now" },
1681
- { name: "clear", aliases: [], description: "Clear chat history and terminal" },
1682
- { name: "radio", aliases: [], description: "Stream a free internet radio station" },
1683
- { name: "quit", aliases: ["q", "exit"], description: "Exit gg-boss" }
1684
- ];
1685
- function isSlashCommand(value) {
1686
- return value.startsWith("/") && !value.startsWith("//");
1687
- }
1688
- function parseSlash(value) {
1689
- if (!isSlashCommand(value)) return null;
1690
- const rest = value.slice(1).trim();
1691
- if (!rest) return null;
1692
- const space = rest.indexOf(" ");
1693
- if (space === -1) return { name: rest.toLowerCase(), args: "" };
1694
- return { name: rest.slice(0, space).toLowerCase(), args: rest.slice(space + 1).trim() };
1695
- }
1696
- function canonicalName(name) {
1697
- for (const cmd of BOSS_SLASH_COMMANDS) {
1698
- if (cmd.name === name) return cmd.name;
1699
- if (cmd.aliases.includes(name)) return cmd.name;
1770
+ var import_react9 = __toESM(require_react(), 1);
1771
+ var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
1772
+ var MAX_MODELS_TO_SHOW = 8;
1773
+ var PROVIDER_LABEL = {
1774
+ anthropic: "Anthropic",
1775
+ openai: "OpenAI",
1776
+ gemini: "Gemini",
1777
+ glm: "Z.AI",
1778
+ moonshot: "Moonshot",
1779
+ xiaomi: "Xiaomi",
1780
+ minimax: "MiniMax",
1781
+ deepseek: "DeepSeek",
1782
+ openrouter: "OpenRouter"
1783
+ };
1784
+ var ESC = String.fromCharCode(27);
1785
+ var ESC_FOCUS_GAINED = `${ESC}[I`;
1786
+ var ESC_FOCUS_LOST = `${ESC}[O`;
1787
+ var ESC_LESS_FOCUS_GAINED = "[I";
1788
+ var ESC_LESS_FOCUS_LOST = "[O";
1789
+ function stripTerminalFocusSequences2(input) {
1790
+ const withoutEscFocusReports = input.replaceAll(ESC_FOCUS_GAINED, "").replaceAll(ESC_FOCUS_LOST, "");
1791
+ let remaining = withoutEscFocusReports;
1792
+ while (remaining.length > 0) {
1793
+ if (remaining.startsWith(ESC_LESS_FOCUS_GAINED) || remaining.startsWith(ESC_LESS_FOCUS_LOST)) {
1794
+ remaining = remaining.slice(2);
1795
+ continue;
1796
+ }
1797
+ return withoutEscFocusReports;
1700
1798
  }
1701
- return null;
1799
+ return "";
1702
1800
  }
1703
- function buildHelpText() {
1704
- const lines = ["**gg-boss commands**", ""];
1705
- for (const cmd of BOSS_SLASH_COMMANDS) {
1706
- const aliases = cmd.aliases.length > 0 ? ` (${cmd.aliases.map((a) => "/" + a).join(", ")})` : "";
1707
- lines.push(`- \`/${cmd.name}\`${aliases} \u2014 ${cmd.description}`);
1708
- }
1709
- lines.push("");
1710
- lines.push("**Global keybindings**");
1711
- lines.push("- `Ctrl+T` \u2014 open the Tasks pane");
1712
- lines.push("- `Tab` \u2014 switch project scope (All / per-project pill in the input)");
1713
- lines.push("- `Shift+Tab` \u2014 toggle the boss's extended thinking on/off");
1714
- lines.push("- `Esc` \u2014 interrupt the boss while it's running");
1715
- lines.push("- `Ctrl+C` (twice) \u2014 exit");
1716
- lines.push("");
1717
- lines.push("**Inside the Tasks pane (Ctrl+T)**");
1718
- lines.push("- `\u2191` / `\u2193` (or `k` / `j`) \u2014 navigate tasks");
1719
- lines.push("- `r` \u2014 run all pending and blocked tasks across idle workers");
1720
- lines.push("- `d` \u2014 delete the selected task");
1721
- lines.push("- `Esc` \u2014 close the Tasks pane");
1722
- lines.push("");
1723
- lines.push("**Inside model pickers (`/model-boss`, `/model-workers`)**");
1724
- lines.push("- `\u2191` / `\u2193` \u2014 navigate models");
1725
- lines.push("- `Enter` \u2014 select");
1726
- lines.push("- `Esc` \u2014 cancel");
1727
- lines.push("");
1728
- lines.push("**Radio** (`/radio`)");
1729
- lines.push("- Pick a station to stream while you work, or select `Off` to stop.");
1730
- lines.push("- Requires `mpv` (recommended), `ffplay`, `mpg123`, or `vlc/cvlc` installed.");
1731
- lines.push("");
1732
- lines.push("**Input area**");
1733
- lines.push("- `\u2191` / `\u2193` \u2014 recall previous prompts (when input is empty)");
1734
- lines.push("- `Enter` \u2014 send \xB7 `Shift+Enter` \u2014 newline");
1735
- lines.push("- `/` \u2014 open the slash-command menu (Tab / arrows to pick, Enter to insert)");
1736
- return lines.join("\n");
1801
+ function BossModelSelectList({
1802
+ items,
1803
+ onSelect,
1804
+ onCancel,
1805
+ initialIndex
1806
+ }) {
1807
+ const theme = useTheme();
1808
+ const { columns } = useTerminalSize();
1809
+ const [selectedIndex, setSelectedIndex] = (0, import_react9.useState)(initialIndex);
1810
+ const [filter, setFilter] = (0, import_react9.useState)("");
1811
+ const filtered = (0, import_react9.useMemo)(() => {
1812
+ if (!filter) return items;
1813
+ const lower = filter.toLowerCase();
1814
+ return items.filter(
1815
+ (item) => item.label.toLowerCase().includes(lower) || item.value.toLowerCase().includes(lower) || item.description.toLowerCase().includes(lower)
1816
+ );
1817
+ }, [items, filter]);
1818
+ use_input_default((input, key) => {
1819
+ const inputWithoutFocusReports = stripTerminalFocusSequences2(input);
1820
+ if (!inputWithoutFocusReports && input) return;
1821
+ input = inputWithoutFocusReports;
1822
+ if (key.escape) {
1823
+ onCancel();
1824
+ return;
1825
+ }
1826
+ if (key.return) {
1827
+ const selected = filtered[selectedIndex];
1828
+ if (selected) onSelect(selected.value);
1829
+ return;
1830
+ }
1831
+ if (key.upArrow) {
1832
+ setSelectedIndex((i) => Math.max(0, i - 1));
1833
+ return;
1834
+ }
1835
+ if (key.downArrow) {
1836
+ setSelectedIndex((i) => filtered.length === 0 ? 0 : Math.min(filtered.length - 1, i + 1));
1837
+ return;
1838
+ }
1839
+ if (key.backspace || key.delete) {
1840
+ setFilter((current) => current.slice(0, -1));
1841
+ setSelectedIndex(0);
1842
+ return;
1843
+ }
1844
+ if (input && !key.ctrl && !key.meta) {
1845
+ setFilter((current) => current + input);
1846
+ setSelectedIndex(0);
1847
+ }
1848
+ });
1849
+ const total = filtered.length;
1850
+ const idx = Math.min(Math.max(selectedIndex, 0), Math.max(0, total - 1));
1851
+ const start = total <= MAX_MODELS_TO_SHOW ? 0 : Math.max(0, Math.min(idx - Math.floor(MAX_MODELS_TO_SHOW / 2), total - MAX_MODELS_TO_SHOW));
1852
+ const end = Math.min(start + MAX_MODELS_TO_SHOW, total);
1853
+ const visible = filtered.slice(start, end);
1854
+ const width = Math.max(20, columns);
1855
+ const maxLabelLength = Math.max(0, ...filtered.map((item) => item.label.length));
1856
+ const labelColumnWidth = Math.min(maxLabelLength, Math.floor(width * 0.5));
1857
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, width, children: [
1858
+ filter && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.textDim, children: [
1859
+ "Filter: ",
1860
+ filter
1861
+ ] }),
1862
+ start > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.text, children: "\u25B2" }),
1863
+ visible.map((item, i) => {
1864
+ const actualIndex = start + i;
1865
+ const isSelected = actualIndex === idx;
1866
+ const textColor = isSelected ? theme.commandColor : theme.textDim;
1867
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1868
+ Box_default,
1869
+ {
1870
+ flexDirection: "row",
1871
+ backgroundColor: isSelected ? theme.border : void 0,
1872
+ children: [
1873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { width: labelColumnWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: textColor, children: item.label }) }),
1874
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { flexGrow: 1, paddingLeft: 3, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: textColor, wrap: "truncate", children: item.description.slice(0, 100) }) })
1875
+ ]
1876
+ },
1877
+ item.value
1878
+ );
1879
+ }),
1880
+ end < total && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: "\u25BC" }),
1881
+ total > MAX_MODELS_TO_SHOW && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.textDim, children: [
1882
+ "(",
1883
+ idx + 1,
1884
+ "/",
1885
+ total,
1886
+ ")"
1887
+ ] }),
1888
+ total === 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: "No matches" })
1889
+ ] });
1890
+ }
1891
+ function BossModelSelector({
1892
+ onSelect,
1893
+ onCancel,
1894
+ currentModel,
1895
+ currentProvider
1896
+ }) {
1897
+ const currentValue = `${currentProvider}:${currentModel}`;
1898
+ const items = (0, import_react9.useMemo)(
1899
+ () => MODELS.map((model) => {
1900
+ const value = `${model.provider}:${model.id}`;
1901
+ const isCurrent = value === currentValue;
1902
+ return {
1903
+ label: `${isCurrent ? "* " : " "}${model.name}`,
1904
+ value,
1905
+ description: `${PROVIDER_LABEL[model.provider] ?? model.provider} \xB7 ${model.id}`
1906
+ };
1907
+ }),
1908
+ [currentValue]
1909
+ );
1910
+ const initialIndex = Math.max(
1911
+ 0,
1912
+ items.findIndex((item) => item.value === currentValue)
1913
+ );
1914
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1915
+ BossModelSelectList,
1916
+ {
1917
+ items,
1918
+ onSelect,
1919
+ onCancel,
1920
+ initialIndex
1921
+ }
1922
+ );
1737
1923
  }
1738
1924
 
1739
- // src/tool-formatters.ts
1925
+ // src/boss-tasks-overlay.tsx
1740
1926
  init_esm_shims();
1927
+ var import_react10 = __toESM(require_react(), 1);
1741
1928
 
1742
1929
  // src/colors.ts
1743
1930
  init_esm_shims();
@@ -1770,206 +1957,48 @@ function projectColor(name) {
1770
1957
  return PROJECT_COLORS[stableHash(name) % PROJECT_COLORS.length];
1771
1958
  }
1772
1959
 
1773
- // src/tool-formatters.ts
1774
- function truncate2(s, max) {
1775
- if (max <= 1) return "\u2026";
1776
- return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
1777
- }
1778
- function promptWorkerDetailLen(project) {
1779
- const cols = process.stdout.columns ?? 80;
1780
- const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
1781
- return Math.max(20, cols - fixed);
1960
+ // src/boss-tasks-overlay.tsx
1961
+ var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
1962
+ function statusGlyph(status) {
1963
+ switch (status) {
1964
+ case "done":
1965
+ return "\u2713";
1966
+ case "in_progress":
1967
+ return "~";
1968
+ case "blocked":
1969
+ return "\u2717";
1970
+ case "skipped":
1971
+ return "\u2014";
1972
+ default:
1973
+ return " ";
1974
+ }
1782
1975
  }
1783
- var bossToolFormatters = {
1784
- formatLabel(name) {
1785
- switch (name) {
1786
- case "list_workers":
1787
- return "List Workers";
1788
- case "get_worker_status":
1789
- return "Worker Status";
1790
- case "prompt_worker":
1791
- return "Prompt Worker";
1792
- case "get_worker_summary":
1793
- return "Worker Summary";
1794
- default:
1795
- return void 0;
1796
- }
1797
- },
1798
- formatDetail(name, args) {
1799
- switch (name) {
1800
- case "list_workers":
1801
- return "";
1802
- case "get_worker_status":
1803
- case "get_worker_summary":
1804
- return truncate2(String(args.project ?? ""), 40);
1805
- case "prompt_worker": {
1806
- const project = String(args.project ?? "");
1807
- const message = String(args.message ?? "").replace(/\s+/g, " ");
1808
- const fresh = args.fresh === true;
1809
- const maxMsg = promptWorkerDetailLen(project) - (fresh ? 8 : 0);
1810
- const truncMsg = truncate2(message, Math.max(15, maxMsg));
1811
- const head = fresh ? "fresh \xB7 " : "";
1812
- return project ? `${head}${project} \xB7 ${truncMsg}` : `${head}${truncMsg}`;
1813
- }
1814
- default:
1815
- return void 0;
1816
- }
1817
- },
1818
- formatInline(name, result, isError) {
1819
- if (isError) return void 0;
1820
- switch (name) {
1821
- case "list_workers": {
1822
- const lines = result.split("\n").filter((l) => l.startsWith("-"));
1823
- return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
1824
- }
1825
- case "prompt_worker": {
1826
- if (result.includes("currently working")) {
1827
- return { text: "busy \u2014 skipped", color: "#fbbf24" };
1828
- }
1829
- if (result.includes("Unknown project")) {
1830
- return { text: "unknown project", color: "#f87171" };
1831
- }
1832
- const project = String(result.match(/"([^"]+)"/)?.[1] ?? "");
1833
- const color = project ? projectColor(project) : "#e11d48";
1834
- return { text: "dispatched", color };
1835
- }
1836
- case "get_worker_status": {
1837
- const parts = result.split(":");
1838
- if (parts.length < 2) return void 0;
1839
- const status = parts.slice(1).join(":").trim();
1840
- const project = parts[0].trim();
1841
- return { text: status, color: projectColor(project) };
1842
- }
1843
- case "get_worker_summary": {
1844
- const turnMatch = result.match(/Turn:\s*(\d+)/);
1845
- const projectMatch = result.match(/Project:\s*(.+)/);
1846
- const toolsMatch = result.match(/Tools used:\s*(.+)/);
1847
- const tools = toolsMatch ? toolsMatch[1] : "";
1848
- const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
1849
- const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
1850
- const tCount = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
1851
- const summary = [turn, tCount].filter(Boolean).join(" \xB7 ");
1852
- if (!summary) return void 0;
1853
- const project = projectMatch ? projectMatch[1].trim() : "";
1854
- return project ? { text: summary, color: projectColor(project) } : { text: summary, color: "#9ca3af" };
1855
- }
1856
- default:
1857
- return void 0;
1858
- }
1859
- }
1860
- };
1861
-
1862
- // src/boss-phrases.ts
1863
- init_esm_shims();
1864
- var BOSS_PHRASES = {
1865
- // Generic between-states fallback. Probably never shown but keep for safety.
1866
- idle: ["Standing by", "Waiting for orders", "On call"],
1867
- // Boss has issued a request, waiting for the LLM to begin streaming.
1868
- waiting: [
1869
- "Briefing",
1870
- "Reviewing the room",
1871
- "Triaging",
1872
- "Lining up the brief",
1873
- "Surveying projects",
1874
- "Reading the room",
1875
- "Picking the right hand",
1876
- "Marshalling thoughts",
1877
- "Checking the board",
1878
- "Sizing up the work"
1879
- ],
1880
- // LLM is mid-thinking-block (extended reasoning).
1881
- thinking: [
1882
- "Strategising",
1883
- "Plotting next move",
1884
- "Weighing options",
1885
- "Reasoning",
1886
- "Deliberating",
1887
- "Thinking it through",
1888
- "Mapping the play",
1889
- "Considering angles",
1890
- "Calculating odds",
1891
- "Drafting the call"
1892
- ],
1893
- // LLM is streaming text — boss is forming its dispatch / response.
1894
- generating: [
1895
- "Drafting",
1896
- "Composing dispatch",
1897
- "Writing the brief",
1898
- "Penning instructions",
1899
- "Wording it up",
1900
- "Putting it on paper",
1901
- "Phrasing the ask",
1902
- "Forming the directive",
1903
- "Scripting the plan"
1904
- ],
1905
- // Boss is invoking a tool — most often prompt_worker.
1906
- tools: [
1907
- "Coordinating",
1908
- "Dispatching",
1909
- "Routing",
1910
- "Delegating",
1911
- "Issuing orders",
1912
- "Handing off",
1913
- "Aligning workers",
1914
- "Conducting",
1915
- "Calling the team",
1916
- "Steering",
1917
- "Pulling levers"
1918
- ],
1919
- // Provider retry (overloaded / rate-limited / etc.).
1920
- retrying: [
1921
- "Reattempting",
1922
- "Course correcting",
1923
- "Trying again",
1924
- "Pushing through",
1925
- "Holding the line"
1926
- ]
1927
- };
1928
-
1929
- // src/boss-tasks-overlay.tsx
1930
- init_esm_shims();
1931
- var import_react8 = __toESM(require_react(), 1);
1932
- var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
1933
- function statusGlyph(status) {
1934
- switch (status) {
1935
- case "done":
1936
- return "\u2713";
1937
- case "in_progress":
1938
- return "~";
1939
- case "blocked":
1940
- return "\u2717";
1941
- case "skipped":
1942
- return "\u2014";
1943
- default:
1944
- return " ";
1945
- }
1946
- }
1947
- function BossTasksOverlay({
1948
- boss,
1949
- workers,
1950
- onClose
1951
- }) {
1952
- const theme = useTheme();
1953
- const tasksState = useTasksState();
1954
- const tasks = tasksState.tasks;
1955
- const [selectedIndex, setSelectedIndex] = (0, import_react8.useState)(0);
1956
- const [status, setStatusMsg] = (0, import_react8.useState)("");
1957
- const statusTimer = (0, import_react8.useRef)(null);
1958
- const showStatus = (0, import_react8.useCallback)((msg) => {
1959
- setStatusMsg(msg);
1960
- if (statusTimer.current) clearTimeout(statusTimer.current);
1961
- statusTimer.current = setTimeout(() => setStatusMsg(""), 2500);
1962
- }, []);
1963
- const groupedTasks = workers.map((w) => ({
1964
- project: w.name,
1965
- tasks: tasks.filter((t) => t.project === w.name).sort((a, b) => a.createdAt.localeCompare(b.createdAt))
1966
- }));
1967
- const flatTasks = groupedTasks.flatMap((g) => g.tasks);
1968
- (0, import_react8.useEffect)(() => {
1969
- if (flatTasks.length === 0) {
1970
- setSelectedIndex(0);
1971
- } else if (selectedIndex >= flatTasks.length) {
1972
- setSelectedIndex(flatTasks.length - 1);
1976
+ function BossTasksOverlay({
1977
+ boss,
1978
+ workers,
1979
+ onClose
1980
+ }) {
1981
+ const theme = useTheme();
1982
+ const tasksState = useTasksState();
1983
+ const tasks = tasksState.tasks;
1984
+ const [selectedIndex, setSelectedIndex] = (0, import_react10.useState)(0);
1985
+ const [status, setStatusMsg] = (0, import_react10.useState)("");
1986
+ const statusTimer = (0, import_react10.useRef)(null);
1987
+ const showStatus = (0, import_react10.useCallback)((msg) => {
1988
+ setStatusMsg(msg);
1989
+ if (statusTimer.current) clearTimeout(statusTimer.current);
1990
+ statusTimer.current = setTimeout(() => setStatusMsg(""), 2500);
1991
+ }, []);
1992
+ const groupedTasks = workers.map((w) => ({
1993
+ project: w.name,
1994
+ tasks: tasks.filter((t) => t.project === w.name).sort((a, b) => a.createdAt.localeCompare(b.createdAt))
1995
+ }));
1996
+ const flatTasks = groupedTasks.flatMap((g) => g.tasks);
1997
+ (0, import_react10.useEffect)(() => {
1998
+ if (flatTasks.length === 0) {
1999
+ setSelectedIndex(0);
2000
+ } else if (selectedIndex >= flatTasks.length) {
2001
+ setSelectedIndex(flatTasks.length - 1);
1973
2002
  }
1974
2003
  }, [flatTasks.length, selectedIndex]);
1975
2004
  const selected = flatTasks[selectedIndex];
@@ -2025,11 +2054,11 @@ function BossTasksOverlay({
2025
2054
  const inProgressCount = tasks.filter((t) => t.status === "in_progress").length;
2026
2055
  const pendingCount = tasks.filter((t) => t.status === "pending").length;
2027
2056
  const blockedCount = tasks.filter((t) => t.status === "blocked").length;
2028
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, paddingX: 1, children: [
2029
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { children: [
2030
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: COLORS.primary, bold: true, children: "Tasks" }),
2031
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${tasks.length} total \xB7 ` }),
2032
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2057
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, paddingX: 1, children: [
2058
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.primary, bold: true, children: "Tasks" }),
2060
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${tasks.length} total \xB7 ` }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2033
2062
  CountsRow,
2034
2063
  {
2035
2064
  theme,
@@ -2040,28 +2069,28 @@ function BossTasksOverlay({
2040
2069
  }
2041
2070
  )
2042
2071
  ] }),
2043
- flatTasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.textDim, children: [
2072
+ flatTasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
2044
2073
  " No tasks yet. Ask the boss to plan some \u2014 e.g. ",
2045
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.text, children: '"plan some work"' }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: '"plan some work"' }),
2046
2075
  "."
2047
2076
  ] }) }),
2048
- showingTop && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: ` \u2191 ${startIdx} more above` }),
2077
+ showingTop && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ` \u2191 ${startIdx} more above` }),
2049
2078
  groupedTasks.map((group, gIdx) => {
2050
2079
  const startInFlat = groupedTasks.slice(0, gIdx).reduce((acc, g) => acc + g.tasks.length, 0);
2051
2080
  const visibleInSection = group.tasks.filter((t) => visibleIdSet.has(t.id));
2052
2081
  if (visibleInSection.length === 0) return null;
2053
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
2054
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
2055
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: projectColor(group.project), bold: true, children: group.project }),
2056
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${group.tasks.length}` })
2082
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
2083
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
2084
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: projectColor(group.project), bold: true, children: group.project }),
2085
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${group.tasks.length}` })
2057
2086
  ] }),
2058
2087
  visibleInSection.map((task) => {
2059
2088
  const realIdx = startInFlat + group.tasks.indexOf(task);
2060
2089
  const isSelected = realIdx === selectedIndex;
2061
2090
  const prefix = isSelected ? "\u276F " : " ";
2062
2091
  const glyph = statusGlyph(task.status);
2063
- const color = isSelected ? theme.primary : task.status === "done" ? theme.success : task.status === "in_progress" ? theme.warning : task.status === "blocked" ? theme.error : theme.text;
2064
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color, bold: isSelected, children: [
2092
+ const color2 = isSelected ? theme.primary : task.status === "done" ? theme.success : task.status === "in_progress" ? theme.warning : task.status === "blocked" ? theme.error : theme.text;
2093
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: color2, bold: isSelected, children: [
2065
2094
  prefix,
2066
2095
  "[",
2067
2096
  glyph,
@@ -2071,16 +2100,16 @@ function BossTasksOverlay({
2071
2100
  })
2072
2101
  ] }, group.project);
2073
2102
  }),
2074
- showingBottom && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: ` \u2193 ${flatTasks.length - endIdx} more below` }),
2075
- status && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.success, children: " " + status }) }),
2076
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.textDim, children: [
2077
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.primary, children: "\u2191\u2193" }),
2103
+ showingBottom && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ` \u2193 ${flatTasks.length - endIdx} more below` }),
2104
+ status && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.success, children: " " + status }) }),
2105
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
2106
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.primary, children: "\u2191\u2193" }),
2078
2107
  " move \xB7 (",
2079
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.primary, children: "d" }),
2108
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.primary, children: "d" }),
2080
2109
  ")elete \xB7 (",
2081
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.primary, children: "r" }),
2110
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.primary, children: "r" }),
2082
2111
  ")un pending \xB7 ",
2083
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.primary, children: "ESC" }),
2112
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.primary, children: "ESC" }),
2084
2113
  " close"
2085
2114
  ] }) })
2086
2115
  ] });
@@ -2092,24 +2121,24 @@ function CountsRow({
2092
2121
  pending,
2093
2122
  blocked
2094
2123
  }) {
2095
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
2096
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.success, children: [
2124
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
2125
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.success, children: [
2097
2126
  done,
2098
2127
  " done"
2099
2128
  ] }),
2100
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2101
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.warning, children: [
2129
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2130
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.warning, children: [
2102
2131
  active,
2103
2132
  " active"
2104
2133
  ] }),
2105
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2106
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.text, children: [
2134
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2135
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.text, children: [
2107
2136
  pending,
2108
2137
  " pending"
2109
2138
  ] }),
2110
- blocked > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2111
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2112
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.error, children: [
2139
+ blocked > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2140
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2141
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.error, children: [
2113
2142
  blocked,
2114
2143
  " blocked"
2115
2144
  ] })
@@ -2117,9 +2146,100 @@ function CountsRow({
2117
2146
  ] });
2118
2147
  }
2119
2148
 
2149
+ // src/boss-worker-status-row.tsx
2150
+ init_esm_shims();
2151
+ var import_react11 = __toESM(require_react(), 1);
2152
+ var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
2153
+ var SHIMMER_WIDTH = 3;
2154
+ function formatWorkerElapsed(ms) {
2155
+ const total = Math.floor(ms / 1e3);
2156
+ const m = Math.floor(total / 60);
2157
+ const s = total % 60;
2158
+ return `${m}:${s.toString().padStart(2, "0")}`;
2159
+ }
2160
+ function AnimationActiveSentinel() {
2161
+ useAnimationActive();
2162
+ return null;
2163
+ }
2164
+ function ShimmerName({
2165
+ name,
2166
+ color: color2,
2167
+ tick
2168
+ }) {
2169
+ const cycle = name.length + SHIMMER_WIDTH * 2;
2170
+ const shimmerPos = tick % cycle - SHIMMER_WIDTH;
2171
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: name.split("").map((ch, i) => {
2172
+ const isBright = Math.abs(i - shimmerPos) <= SHIMMER_WIDTH;
2173
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: color2, bold: isBright, dimColor: !isBright, children: ch }, i);
2174
+ }) });
2175
+ }
2176
+ function BossWorkerStatusRow({
2177
+ workers,
2178
+ pendingMessages
2179
+ }) {
2180
+ const theme = useTheme();
2181
+ const { columns } = useTerminalSize();
2182
+ const working = workers.filter((w) => w.status === "working");
2183
+ const errored = workers.filter((w) => w.status === "error");
2184
+ const idleCount = workers.length - working.length - errored.length;
2185
+ const anyWorking = working.length > 0;
2186
+ const tick = useAnimationTick();
2187
+ const now = Date.now();
2188
+ if (workers.length === 0) return null;
2189
+ const slots = [];
2190
+ for (const w of working) {
2191
+ const projectHue = projectColor(w.name);
2192
+ const elapsed = w.workStartedAt ? formatWorkerElapsed(now - w.workStartedAt) : null;
2193
+ slots.push(
2194
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react11.default.Fragment, { children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ShimmerName, { name: w.name, color: projectHue, tick }),
2196
+ elapsed && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.textDim, children: [
2197
+ " ",
2198
+ elapsed
2199
+ ] })
2200
+ ] }, `w-${w.name}`)
2201
+ );
2202
+ }
2203
+ for (const w of errored) {
2204
+ slots.push(
2205
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react11.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.error, children: [
2206
+ "\u2717 ",
2207
+ w.name
2208
+ ] }) }, `e-${w.name}`)
2209
+ );
2210
+ }
2211
+ if (idleCount > 0) {
2212
+ slots.push(
2213
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react11.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.textDim, children: [
2214
+ "\u25CB ",
2215
+ idleCount,
2216
+ " idle"
2217
+ ] }) }, "idle")
2218
+ );
2219
+ }
2220
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { paddingX: 1, width: columns, flexShrink: 1, children: [
2221
+ anyWorking && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AnimationActiveSentinel, {}),
2222
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { wrap: "truncate", children: [
2223
+ slots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react11.default.Fragment, { children: [
2224
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.border, children: " \u2502 " }),
2225
+ slot
2226
+ ] }, i)),
2227
+ pendingMessages > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2228
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: " " }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.warning, children: [
2230
+ pendingMessages,
2231
+ " message",
2232
+ pendingMessages === 1 ? "" : "s",
2233
+ " queued"
2234
+ ] })
2235
+ ] })
2236
+ ] })
2237
+ ] });
2238
+ }
2239
+
2120
2240
  // src/radio-picker.tsx
2121
2241
  init_esm_shims();
2122
- var import_react9 = __toESM(require_react(), 1);
2242
+ var import_react12 = __toESM(require_react(), 1);
2123
2243
 
2124
2244
  // src/radio.ts
2125
2245
  init_esm_shims();
@@ -2287,7 +2407,7 @@ function buildInstallHint() {
2287
2407
  }
2288
2408
 
2289
2409
  // src/radio-picker.tsx
2290
- var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
2410
+ var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
2291
2411
  function RadioPicker({
2292
2412
  currentStationId: currentStationId2,
2293
2413
  onSelect,
@@ -2309,7 +2429,7 @@ function RadioPicker({
2309
2429
  0,
2310
2430
  items.findIndex((i) => i.value === (currentStationId2 ?? "off"))
2311
2431
  );
2312
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2432
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2313
2433
  SelectList,
2314
2434
  {
2315
2435
  items,
@@ -2321,42 +2441,904 @@ function RadioPicker({
2321
2441
  );
2322
2442
  }
2323
2443
 
2324
- // src/auto-update.ts
2325
- init_esm_shims();
2326
- import { spawn as spawn2 } from "child_process";
2327
- import fs4 from "fs";
2328
- import path4 from "path";
2329
- import os2 from "os";
2330
- var PACKAGE_NAME = "@kenkaiiii/gg-boss";
2331
- var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
2332
- var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
2333
- var FETCH_TIMEOUT_MS = 1e4;
2334
- function getStateFilePath() {
2335
- return path4.join(os2.homedir(), ".gg", "boss", "update-state.json");
2336
- }
2337
- function readState() {
2338
- try {
2339
- const raw = fs4.readFileSync(getStateFilePath(), "utf-8");
2340
- return JSON.parse(raw);
2341
- } catch {
2342
- return null;
2343
- }
2344
- }
2345
- function writeState(state) {
2346
- try {
2347
- const dir = path4.dirname(getStateFilePath());
2348
- fs4.mkdirSync(dir, { recursive: true, mode: 448 });
2349
- fs4.writeFileSync(getStateFilePath(), JSON.stringify(state));
2350
- } catch {
2444
+ // src/boss-chat-screen.tsx
2445
+ var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
2446
+ function BossChatScreen({
2447
+ boss,
2448
+ columns,
2449
+ state,
2450
+ overlay,
2451
+ controlsRef = () => {
2452
+ },
2453
+ livePane,
2454
+ theme,
2455
+ statusSlotVisible,
2456
+ activityVisible,
2457
+ stallStatusVisible,
2458
+ doneStatus,
2459
+ elapsedMs,
2460
+ runStartRef,
2461
+ charCountRef,
2462
+ realTokensAccumRef,
2463
+ lastUserMessage,
2464
+ activeToolNames,
2465
+ inputActive,
2466
+ isRunning,
2467
+ onSubmit,
2468
+ onAbort,
2469
+ onTab,
2470
+ onShiftTab,
2471
+ commands,
2472
+ scopeBadge,
2473
+ onCloseOverlay,
2474
+ onModelSelect,
2475
+ currentRadio,
2476
+ onRadioSelect,
2477
+ bossModel,
2478
+ workerModel,
2479
+ updatePending,
2480
+ currentRadioStationId,
2481
+ workers,
2482
+ pendingMessages,
2483
+ formatDuration
2484
+ }) {
2485
+ if (overlay === "tasks") {
2486
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChatLayout, { columns, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BossTasksOverlay, { boss, workers, onClose: onCloseOverlay }) });
2351
2487
  }
2488
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ChatLayout, { columns, children: [
2489
+ livePane,
2490
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ChatControls, { controlsRef, children: [
2491
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2492
+ ChatInputStack,
2493
+ {
2494
+ columns,
2495
+ theme,
2496
+ statusSlotVisible,
2497
+ activityVisible,
2498
+ stallStatusVisible,
2499
+ doneStatus,
2500
+ activityPhase: state.activityPhase,
2501
+ elapsedMs,
2502
+ runStartRef,
2503
+ thinkingMs: state.streaming?.thinkingMs ?? 0,
2504
+ isThinking: state.activityPhase === "thinking",
2505
+ thinkingLevel: state.bossThinkingLevel,
2506
+ tokenEstimate: state.bossInputTokens,
2507
+ charCountRef,
2508
+ realTokensAccumRef,
2509
+ lastUserMessage,
2510
+ activeToolNames,
2511
+ retryInfo: state.retryInfo,
2512
+ planDone: 0,
2513
+ planTotal: 0,
2514
+ renderMarkdown: true,
2515
+ formatDuration
2516
+ }
2517
+ ),
2518
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2519
+ InputArea,
2520
+ {
2521
+ onSubmit,
2522
+ onAbort,
2523
+ disabled: isRunning,
2524
+ isActive: inputActive,
2525
+ cwd: process.cwd(),
2526
+ commands,
2527
+ scopeBadge,
2528
+ disableMouseTracking: true,
2529
+ onTab,
2530
+ onShiftTab
2531
+ }
2532
+ ),
2533
+ overlay === "model-boss" || overlay === "model-workers" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2534
+ BossModelSelector,
2535
+ {
2536
+ onSelect: onModelSelect,
2537
+ onCancel: onCloseOverlay,
2538
+ currentModel: overlay === "model-boss" ? state.bossModel : state.workerModel,
2539
+ currentProvider: overlay === "model-boss" ? state.bossProvider : state.workerProvider
2540
+ }
2541
+ ) : overlay === "radio" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2542
+ RadioPicker,
2543
+ {
2544
+ currentStationId: currentRadio,
2545
+ onCancel: onCloseOverlay,
2546
+ onSelect: onRadioSelect
2547
+ }
2548
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2549
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2550
+ BossFooter,
2551
+ {
2552
+ bossModel,
2553
+ workerModel,
2554
+ tokensIn: state.bossInputTokens,
2555
+ exitPending: state.exitPending,
2556
+ bossThinkingLevel: state.bossThinkingLevel,
2557
+ updatePending,
2558
+ currentRadioStationId,
2559
+ scope: state.scope
2560
+ }
2561
+ ),
2562
+ !state.exitPending && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BossWorkerStatusRow, { workers, pendingMessages })
2563
+ ] })
2564
+ ] })
2565
+ ] });
2352
2566
  }
2353
- function compareVersions(a, b) {
2354
- const pa = a.split(".").map(Number);
2355
- const pb = b.split(".").map(Number);
2356
- for (let i = 0; i < 3; i++) {
2357
- const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
2358
- if (diff !== 0) return diff;
2359
- }
2567
+
2568
+ // src/slash-commands.ts
2569
+ init_esm_shims();
2570
+ var BOSS_SLASH_COMMANDS = [
2571
+ { name: "help", aliases: ["?"], description: "Show available commands" },
2572
+ {
2573
+ name: "model-boss",
2574
+ aliases: ["m", "model", "models"],
2575
+ description: "Switch the orchestrator's model"
2576
+ },
2577
+ { name: "model-workers", aliases: [], description: "Switch every worker's model" },
2578
+ { name: "compact", aliases: [], description: "Compact the boss's context now" },
2579
+ { name: "clear", aliases: [], description: "Clear chat history and terminal" },
2580
+ { name: "radio", aliases: [], description: "Stream a free internet radio station" },
2581
+ { name: "quit", aliases: ["q", "exit"], description: "Exit gg-boss" }
2582
+ ];
2583
+ function isSlashCommand(value) {
2584
+ return value.startsWith("/") && !value.startsWith("//");
2585
+ }
2586
+ function parseSlash(value) {
2587
+ if (!isSlashCommand(value)) return null;
2588
+ const rest = value.slice(1).trim();
2589
+ if (!rest) return null;
2590
+ const space = rest.indexOf(" ");
2591
+ if (space === -1) return { name: rest.toLowerCase(), args: "" };
2592
+ return { name: rest.slice(0, space).toLowerCase(), args: rest.slice(space + 1).trim() };
2593
+ }
2594
+ function canonicalName(name) {
2595
+ for (const cmd of BOSS_SLASH_COMMANDS) {
2596
+ if (cmd.name === name) return cmd.name;
2597
+ if (cmd.aliases.includes(name)) return cmd.name;
2598
+ }
2599
+ return null;
2600
+ }
2601
+ function buildHelpText() {
2602
+ const lines = ["**gg-boss commands**", ""];
2603
+ for (const cmd of BOSS_SLASH_COMMANDS) {
2604
+ const aliases = cmd.aliases.length > 0 ? ` (${cmd.aliases.map((a) => "/" + a).join(", ")})` : "";
2605
+ lines.push(`- \`/${cmd.name}\`${aliases} \u2014 ${cmd.description}`);
2606
+ }
2607
+ lines.push("");
2608
+ lines.push("**Global keybindings**");
2609
+ lines.push("- `Ctrl+T` \u2014 open the Tasks pane");
2610
+ lines.push("- `Tab` \u2014 switch project scope (All / per-project pill in the input)");
2611
+ lines.push("- `Shift+Tab` \u2014 cycle the boss's thinking level, then off");
2612
+ lines.push("- `Esc` \u2014 interrupt the boss while it's running");
2613
+ lines.push("- `Ctrl+C` (twice) \u2014 exit");
2614
+ lines.push("");
2615
+ lines.push("**Inside the Tasks pane (Ctrl+T)**");
2616
+ lines.push("- `\u2191` / `\u2193` (or `k` / `j`) \u2014 navigate tasks");
2617
+ lines.push("- `r` \u2014 run all pending and blocked tasks across idle workers");
2618
+ lines.push("- `d` \u2014 delete the selected task");
2619
+ lines.push("- `Esc` \u2014 close the Tasks pane");
2620
+ lines.push("");
2621
+ lines.push("**Inside model pickers (`/model`, `/models`, `/model-boss`, `/model-workers`)**");
2622
+ lines.push("- `\u2191` / `\u2193` \u2014 navigate models");
2623
+ lines.push("- `Enter` \u2014 select");
2624
+ lines.push("- `Esc` \u2014 cancel");
2625
+ lines.push("");
2626
+ lines.push("**Radio** (`/radio`)");
2627
+ lines.push("- Pick a station to stream while you work, or select `Off` to stop.");
2628
+ lines.push("- Requires `mpv` (recommended), `ffplay`, `mpg123`, or `vlc/cvlc` installed.");
2629
+ lines.push("");
2630
+ lines.push("**Input area**");
2631
+ lines.push("- `\u2191` / `\u2193` \u2014 recall previous prompts (when input is empty)");
2632
+ lines.push("- `Enter` \u2014 send \xB7 `Shift+Enter` \u2014 newline");
2633
+ lines.push("- `/` \u2014 open the slash-command menu (Tab / arrows to pick, Enter to insert)");
2634
+ return lines.join("\n");
2635
+ }
2636
+
2637
+ // src/boss-transcript-rows.tsx
2638
+ init_esm_shims();
2639
+ var import_react14 = __toESM(require_react(), 1);
2640
+
2641
+ // src/boss-spacing.ts
2642
+ init_esm_shims();
2643
+ var BOSS_SPACING_KINDS = /* @__PURE__ */ new Set([
2644
+ "user",
2645
+ "assistant",
2646
+ "tool_start",
2647
+ "tool_done",
2648
+ "tool_group",
2649
+ "worker_event",
2650
+ "worker_error",
2651
+ "task_dispatch",
2652
+ "info",
2653
+ "update_notice",
2654
+ "compacting",
2655
+ "compacted",
2656
+ "stopped"
2657
+ ]);
2658
+ var BOSS_COMPACT_BOUNDARIES = /* @__PURE__ */ new Set([
2659
+ "user\u2192assistant",
2660
+ "assistant\u2192user",
2661
+ "user\u2192queued"
2662
+ ]);
2663
+
2664
+ // src/tool-formatters.ts
2665
+ init_esm_shims();
2666
+ function truncate2(s, max) {
2667
+ if (max <= 1) return "\u2026";
2668
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
2669
+ }
2670
+ function promptWorkerDetailLen(project) {
2671
+ const cols = process.stdout.columns ?? 80;
2672
+ const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
2673
+ return Math.max(20, cols - fixed);
2674
+ }
2675
+ var bossToolFormatters = {
2676
+ formatLabel(name) {
2677
+ switch (name) {
2678
+ case "list_workers":
2679
+ return "List Workers";
2680
+ case "get_worker_status":
2681
+ return "Worker Status";
2682
+ case "prompt_worker":
2683
+ return "Prompt Worker";
2684
+ case "get_worker_summary":
2685
+ return "Worker Summary";
2686
+ default:
2687
+ return void 0;
2688
+ }
2689
+ },
2690
+ formatDetail(name, args) {
2691
+ switch (name) {
2692
+ case "list_workers":
2693
+ return "";
2694
+ case "get_worker_status":
2695
+ case "get_worker_summary":
2696
+ return truncate2(String(args.project ?? ""), 40);
2697
+ case "prompt_worker": {
2698
+ const project = String(args.project ?? "");
2699
+ const message = String(args.message ?? "").replace(/\s+/g, " ");
2700
+ const fresh = args.fresh === true;
2701
+ const maxMsg = promptWorkerDetailLen(project) - (fresh ? 8 : 0);
2702
+ const truncMsg = truncate2(message, Math.max(15, maxMsg));
2703
+ const head = fresh ? "fresh \xB7 " : "";
2704
+ return project ? `${head}${project} \xB7 ${truncMsg}` : `${head}${truncMsg}`;
2705
+ }
2706
+ default:
2707
+ return void 0;
2708
+ }
2709
+ },
2710
+ formatInline(name, result, isError) {
2711
+ if (isError) return void 0;
2712
+ switch (name) {
2713
+ case "list_workers": {
2714
+ const lines = result.split("\n").filter((l) => l.startsWith("-"));
2715
+ return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
2716
+ }
2717
+ case "prompt_worker": {
2718
+ if (result.includes("currently working")) {
2719
+ return { text: "busy \u2014 skipped", color: "#fbbf24" };
2720
+ }
2721
+ if (result.includes("Unknown project")) {
2722
+ return { text: "unknown project", color: "#f87171" };
2723
+ }
2724
+ const project = String(result.match(/"([^"]+)"/)?.[1] ?? "");
2725
+ const color2 = project ? projectColor(project) : "#e11d48";
2726
+ return { text: "dispatched", color: color2 };
2727
+ }
2728
+ case "get_worker_status": {
2729
+ const parts = result.split(":");
2730
+ if (parts.length < 2) return void 0;
2731
+ const status = parts.slice(1).join(":").trim();
2732
+ const project = parts[0].trim();
2733
+ return { text: status, color: projectColor(project) };
2734
+ }
2735
+ case "get_worker_summary": {
2736
+ const turnMatch = result.match(/Turn:\s*(\d+)/);
2737
+ const projectMatch = result.match(/Project:\s*(.+)/);
2738
+ const toolsMatch = result.match(/Tools used:\s*(.+)/);
2739
+ const tools = toolsMatch ? toolsMatch[1] : "";
2740
+ const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
2741
+ const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
2742
+ const tCount = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
2743
+ const summary = [turn, tCount].filter(Boolean).join(" \xB7 ");
2744
+ if (!summary) return void 0;
2745
+ const project = projectMatch ? projectMatch[1].trim() : "";
2746
+ return project ? { text: summary, color: projectColor(project) } : { text: summary, color: "#9ca3af" };
2747
+ }
2748
+ default:
2749
+ return void 0;
2750
+ }
2751
+ }
2752
+ };
2753
+
2754
+ // src/boss-transcript-rows.tsx
2755
+ var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
2756
+ function getBossTranscriptMarginTop({
2757
+ row,
2758
+ previousRow
2759
+ }) {
2760
+ if (row.kind === "banner" || previousRow?.kind === "banner") return 0;
2761
+ if (!BOSS_SPACING_KINDS.has(row.kind)) return 0;
2762
+ return getTranscriptItemMarginTop({
2763
+ item: row,
2764
+ previousLiveItem: previousRow,
2765
+ spacingKinds: BOSS_SPACING_KINDS,
2766
+ compactBoundaries: BOSS_COMPACT_BOUNDARIES
2767
+ });
2768
+ }
2769
+ function BossTranscriptRow({
2770
+ row,
2771
+ previousRow
2772
+ }) {
2773
+ if (row.kind === "banner") {
2774
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(BossBanner, { subtitle: "Orchestrator", showShortcuts: true }) });
2775
+ }
2776
+ const marginTop = getBossTranscriptMarginTop({ row, previousRow });
2777
+ const renderWithSpacing = (node) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TranscriptItemFrame, { marginTop, children: node });
2778
+ if (row.kind === "user") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(UserMessage, { text: row.text }));
2779
+ if (row.kind === "assistant") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AssistantRow, { item: row }));
2780
+ if (row.kind === "tool_start") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolStartHistoryRow, { item: row }));
2781
+ if (row.kind === "tool_done") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolHistoryRow, { item: row }));
2782
+ if (row.kind === "tool_group") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolGroupRow, { item: row }));
2783
+ if (row.kind === "worker_event") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(WorkerEventRow, { item: row }));
2784
+ if (row.kind === "worker_error") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(WorkerErrorRow, { item: row }));
2785
+ if (row.kind === "info") {
2786
+ return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(InfoRow, { text: row.text, level: row.level ?? "info" }));
2787
+ }
2788
+ if (row.kind === "task_dispatch") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TaskDispatchRow, { tasks: row.tasks }));
2789
+ if (row.kind === "update_notice") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(UpdateNoticeRow, { text: row.text }));
2790
+ if (row.kind === "compacting") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CompactionSpinner, { staticDisplay: true }));
2791
+ if (row.kind === "compacted") {
2792
+ return renderWithSpacing(
2793
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2794
+ CompactionDone,
2795
+ {
2796
+ originalCount: row.originalCount,
2797
+ newCount: row.newCount,
2798
+ tokensBefore: row.tokensBefore,
2799
+ tokensAfter: row.tokensAfter
2800
+ }
2801
+ )
2802
+ );
2803
+ }
2804
+ if (row.kind === "stopped") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(InfoRow, { text: row.text, level: "warning" }));
2805
+ return null;
2806
+ }
2807
+ function UpdateNoticeRow({ text }) {
2808
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { flexShrink: 1, borderStyle: "round", borderColor: COLORS.accent, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { wrap: "wrap", children: [
2809
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: COLORS.accent, bold: true, children: "\u2728 " }),
2810
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: COLORS.primary, bold: true, children: text })
2811
+ ] }) });
2812
+ }
2813
+ function TaskDispatchRow({
2814
+ tasks
2815
+ }) {
2816
+ const theme = useTheme();
2817
+ const count = tasks.length;
2818
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
2819
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { children: [
2820
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: COLORS.primary, bold: true, children: "\u23FA " }),
2821
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { color: theme.text, bold: true, children: [
2822
+ "Running ",
2823
+ count,
2824
+ " task",
2825
+ count === 1 ? "" : "s",
2826
+ ":"
2827
+ ] })
2828
+ ] }),
2829
+ tasks.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { children: [
2830
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.textDim, children: " \u2022 " }),
2831
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: projectColor(t.project), bold: true, children: t.project }),
2832
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.textDim, children: ": " }),
2833
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.text, children: t.title })
2834
+ ] }, `${t.project}-${i}`))
2835
+ ] });
2836
+ }
2837
+ var SHORTCUT_PATTERNS = [
2838
+ // Modifier+Key combos: Ctrl+T, Shift+Tab, Cmd+K, Ctrl+Shift+P, Ctrl+C
2839
+ /\b(?:Ctrl|Cmd|Alt|Option|Opt|Shift|Meta|Win|Super)(?:\s*\+\s*(?:Ctrl|Cmd|Alt|Option|Opt|Shift|Meta|Win|Super))*\s*\+\s*(?:Tab|Enter|Esc|Escape|Space|Backspace|Delete|Del|Home|End|PageUp|PageDown|Up|Down|Left|Right|F[1-9]|F1[0-2]|[A-Z0-9]|\/|\?|\.|,|;|=|-)\b/g,
2840
+ // Bare named keys (only when surrounded by clear key context)
2841
+ /\b(?:Ctrl-[A-Z]|F[1-9]|F1[0-2])\b/g
2842
+ ];
2843
+ function highlightShortcuts(text) {
2844
+ if (!text) return text;
2845
+ const SENTINEL = "\uE000";
2846
+ const masks = [];
2847
+ let masked = text.replace(/```[\s\S]*?```|`[^`]+`/g, (m) => {
2848
+ const idx = masks.push(m) - 1;
2849
+ return `${SENTINEL}${idx}${SENTINEL}`;
2850
+ });
2851
+ for (const re of SHORTCUT_PATTERNS) {
2852
+ masked = masked.replace(re, (m) => `\`${m}\``);
2853
+ }
2854
+ return masked.replace(
2855
+ new RegExp(`${SENTINEL}(\\d+)${SENTINEL}`, "g"),
2856
+ (_, i) => masks[Number(i)]
2857
+ );
2858
+ }
2859
+ function AssistantRow({ item }) {
2860
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AssistantMessage, { text: highlightShortcuts(item.text), renderMarkdown: true });
2861
+ }
2862
+ function ToolStartHistoryRow({
2863
+ item
2864
+ }) {
2865
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2866
+ ToolExecution,
2867
+ {
2868
+ status: "running",
2869
+ name: item.name,
2870
+ args: item.args,
2871
+ formatters: bossToolFormatters
2872
+ }
2873
+ );
2874
+ }
2875
+ function ToolGroupRow({ item }) {
2876
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolGroupExecution, { tools: item.tools, summaryRenderers: bossToolGroupRenderers });
2877
+ }
2878
+ function ToolHistoryRow({ item }) {
2879
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2880
+ ToolExecution,
2881
+ {
2882
+ status: "done",
2883
+ name: item.name,
2884
+ args: item.args,
2885
+ result: item.result,
2886
+ isError: item.isError,
2887
+ details: item.details,
2888
+ formatters: bossToolFormatters
2889
+ }
2890
+ );
2891
+ }
2892
+ function parseStatusGrade(text) {
2893
+ const matches = [
2894
+ ...text.matchAll(/(?:^|\b)Status:\s*(DONE|UNVERIFIED|PARTIAL|BLOCKED|INFO)\b/gim)
2895
+ ];
2896
+ const last = matches[matches.length - 1];
2897
+ if (!last) return null;
2898
+ return last[1].toUpperCase();
2899
+ }
2900
+ function statusGradeColor(grade, theme) {
2901
+ switch (grade) {
2902
+ case "DONE":
2903
+ return theme.success;
2904
+ case "UNVERIFIED":
2905
+ case "PARTIAL":
2906
+ return theme.warning;
2907
+ case "BLOCKED":
2908
+ return theme.error;
2909
+ case "INFO":
2910
+ return theme.textDim;
2911
+ default:
2912
+ return theme.textDim;
2913
+ }
2914
+ }
2915
+ function WorkerEventRow({ item }) {
2916
+ const theme = useTheme();
2917
+ const failedCount = item.toolsUsed.filter((t) => !t.ok).length;
2918
+ const grade = parseStatusGrade(item.finalText);
2919
+ const loaderStatus = grade === "BLOCKED" || failedCount > 0 ? "error" : grade === "UNVERIFIED" || grade === "PARTIAL" ? "queued" : "done";
2920
+ const headerColor = loaderStatus === "error" ? theme.toolError : projectColor(item.project);
2921
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "row", children: [
2922
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolUseLoader, { status: loaderStatus }),
2923
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { wrap: "wrap", children: [
2924
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: headerColor, bold: true, children: item.project }),
2925
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.text, children: ` turn ${item.turnIndex}` }),
2926
+ grade && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2927
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
2928
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: statusGradeColor(grade, theme), bold: true, children: grade })
2929
+ ] })
2930
+ ] }) })
2931
+ ] });
2932
+ }
2933
+ function WorkerErrorRow({ item }) {
2934
+ const theme = useTheme();
2935
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "column", children: [
2936
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "row", children: [
2937
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolUseLoader, { status: "error" }),
2938
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { wrap: "wrap", children: [
2939
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.toolError, bold: true, children: item.project }),
2940
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.textDim, children: " worker error" })
2941
+ ] }) })
2942
+ ] }),
2943
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.error, wrap: "wrap", children: item.message }) })
2944
+ ] });
2945
+ }
2946
+ function InfoRow({
2947
+ text,
2948
+ level
2949
+ }) {
2950
+ if (level === "info") return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AssistantMessage, { text });
2951
+ const theme = useTheme();
2952
+ const color2 = level === "error" ? theme.error : theme.warning;
2953
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "row", children: [
2954
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolUseLoader, { status: level === "error" ? "error" : "queued" }),
2955
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: color2, wrap: "wrap", children: text }) })
2956
+ ] });
2957
+ }
2958
+ function BossStreamingTurnView({
2959
+ turn,
2960
+ isRunning,
2961
+ liveItems = [],
2962
+ lastPendingHistoryItem,
2963
+ lastHistoryItem,
2964
+ availableTerminalHeight = 20
2965
+ }) {
2966
+ const visibleLiveItems = liveItems.filter(
2967
+ (item) => item.kind === "user" || item.kind === "tool_start" || item.kind === "tool_done" || item.kind === "tool_group" || item.kind === "compacting" || item.kind === "compacted"
2968
+ );
2969
+ const lastLiveItem = visibleLiveItems[visibleLiveItems.length - 1];
2970
+ const visibleStreamingText = turn?.text ?? "";
2971
+ const previousTranscriptItem = lastPendingHistoryItem ?? lastHistoryItem;
2972
+ const isAwaitingAssistantAfterUser = isRunning && visibleStreamingText.trim().length === 0 && (lastLiveItem?.kind === "user" || !lastLiveItem && previousTranscriptItem?.kind === "user");
2973
+ const shouldReserveStreamingSpacing = isRunning && (visibleStreamingText.trim().length > 0 || visibleLiveItems.some((item) => BOSS_SPACING_KINDS.has(item.kind)) || isAwaitingAssistantAfterUser);
2974
+ const assistantMarginTop = shouldTopSpaceStreamingAssistant({
2975
+ visibleStreamingText,
2976
+ lastLiveItem,
2977
+ lastPendingHistoryItem,
2978
+ lastHistoryItem,
2979
+ spacingKinds: BOSS_SPACING_KINDS,
2980
+ compactBoundaries: BOSS_COMPACT_BOUNDARIES
2981
+ }) ? 1 : 0;
2982
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2983
+ ChatLivePane,
2984
+ {
2985
+ liveItems: visibleLiveItems,
2986
+ renderItem: (_item, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2987
+ BossTranscriptRow,
2988
+ {
2989
+ row: visibleLiveItems[index],
2990
+ previousRow: index > 0 ? visibleLiveItems[index - 1] : previousTranscriptItem
2991
+ }
2992
+ ),
2993
+ isRunning,
2994
+ visibleStreamingText,
2995
+ streamingThinking: "",
2996
+ thinkingMs: turn?.thinkingMs ?? 0,
2997
+ reserveStreamingSpacing: shouldReserveStreamingSpacing,
2998
+ renderMarkdown: true,
2999
+ measuredLiveAreaRows: availableTerminalHeight,
3000
+ assistantMarginTop,
3001
+ streamingContinuation: false
3002
+ }
3003
+ );
3004
+ }
3005
+
3006
+ // src/boss-terminal-history.tsx
3007
+ init_esm_shims();
3008
+ function createBossTerminalHistoryPrinter({
3009
+ stream = process.stdout
3010
+ } = {}) {
3011
+ const printed = /* @__PURE__ */ new Set();
3012
+ let previousPrintedKind = null;
3013
+ return {
3014
+ print(items, context, options) {
3015
+ const writeOutput = options?.write ?? ((data) => void stream.write(data));
3016
+ for (const item of items) {
3017
+ if (!options?.force && printed.has(item.id)) continue;
3018
+ const output = serializeBossItemToTerminalHistory(item, context);
3019
+ const formatted = formatHistoryWrite(output, {
3020
+ leadingSeparator: item.kind === "banner" ? false : shouldSeparateTranscriptItemKinds({
3021
+ previousKind: previousPrintedKind ?? void 0,
3022
+ currentKind: item.kind,
3023
+ spacingKinds: BOSS_SPACING_KINDS,
3024
+ compactBoundaries: BOSS_COMPACT_BOUNDARIES
3025
+ }),
3026
+ trailingBlankLine: item.kind === "banner",
3027
+ trailingNewlines: item.kind === "user" ? 1 : void 0
3028
+ });
3029
+ if (formatted.length === 0) continue;
3030
+ printed.add(item.id);
3031
+ writeOutput(formatted);
3032
+ previousPrintedKind = item.kind;
3033
+ }
3034
+ },
3035
+ clear() {
3036
+ printed.clear();
3037
+ previousPrintedKind = null;
3038
+ },
3039
+ resetPrinted() {
3040
+ printed.clear();
3041
+ previousPrintedKind = null;
3042
+ },
3043
+ get printedIds() {
3044
+ return printed;
3045
+ }
3046
+ };
3047
+ }
3048
+ function serializeBossItemToTerminalHistory(item, context) {
3049
+ switch (item.kind) {
3050
+ case "banner":
3051
+ return renderBanner(context);
3052
+ case "worker_event":
3053
+ return renderWorkerEvent(item, context);
3054
+ case "worker_error":
3055
+ return renderWorkerError(item, context);
3056
+ case "task_dispatch":
3057
+ return renderTaskDispatch(item, context);
3058
+ case "tool_group":
3059
+ return renderToolGroup(item, context);
3060
+ case "update_notice":
3061
+ return renderUpdateNotice(item, context);
3062
+ default:
3063
+ return serializeCompletedItemToTerminalHistory(toGGCoderCompletedItem(item), context);
3064
+ }
3065
+ }
3066
+ function renderBanner(context) {
3067
+ const logo = LOGO_LINES.map((lineText) => gradientLine(lineText, GRADIENT));
3068
+ const shortcuts = `${color(COLORS.primary, "^T")} ${dim(context, "tasks ")}${color(
3069
+ COLORS.primary,
3070
+ "Tab"
3071
+ )} ${dim(context, "scope ")}${color(COLORS.primary, "\u21E7Tab")} ${dim(
3072
+ context,
3073
+ "thinking "
3074
+ )}${color(COLORS.primary, "ESC")} ${dim(context, "interrupt")}`;
3075
+ return [
3076
+ "",
3077
+ `${logo[0]}${LOGO_GAP}${color(COLORS.primary, BRAND, true)}${dim(
3078
+ context,
3079
+ ` v${VERSION} \xB7 By `
3080
+ )}${color(COLORS.text, AUTHOR, true)}`,
3081
+ `${logo[1]}${LOGO_GAP}${color(COLORS.accent, "Orchestrator")}`,
3082
+ `${logo[2]}${LOGO_GAP}${shortcuts}`,
3083
+ ""
3084
+ ].join("\n");
3085
+ }
3086
+ function renderUpdateNotice(item, context) {
3087
+ return renderRoundNoticeBox(
3088
+ [`${color(COLORS.accent, "\u2728 ", true)}${color(COLORS.primary, item.text, true)}`],
3089
+ context,
3090
+ COLORS.accent
3091
+ );
3092
+ }
3093
+ function renderToolGroup(item, context) {
3094
+ const tools = item.tools;
3095
+ const allDone = tools.every((tool) => tool.status === "done");
3096
+ const hasError = tools.some((tool) => tool.isError);
3097
+ const status = allDone ? hasError ? "error" : "done" : "running";
3098
+ const label = buildToolGroupSummary(tools, allDone, bossToolGroupRenderers).map((seg) => {
3099
+ const hex = seg.tone ? toolTonePalette(context.theme, seg.tone).primary : context.theme.toolName;
3100
+ return color(hex, seg.text, seg.bold);
3101
+ }).join("");
3102
+ return toolStatusHeader({ status, label, context, labelAlreadyStyled: true });
3103
+ }
3104
+ function renderWorkerEvent(item, context) {
3105
+ const theme = context.theme;
3106
+ const failedCount = item.toolsUsed.filter((tool) => !tool.ok).length;
3107
+ const grade = parseStatusGrade(item.finalText);
3108
+ const isError = grade === "BLOCKED" || failedCount > 0;
3109
+ const isWarning = grade === "UNVERIFIED" || grade === "PARTIAL";
3110
+ const statusColor = isError ? theme.error : isWarning ? theme.warning : theme.success;
3111
+ const headerColor = isError ? theme.error : projectColor(item.project);
3112
+ return toolStatusHeader({
3113
+ status: isError ? "error" : isWarning ? "queued" : "done",
3114
+ label: color(headerColor, item.project, true),
3115
+ suffix: `${color(theme.text, ` turn ${item.turnIndex}`)}${grade ? `${dim(context, " \xB7 ")}${color(statusColor, grade, true)}` : ""}`,
3116
+ context,
3117
+ labelAlreadyStyled: true
3118
+ });
3119
+ }
3120
+ function renderWorkerError(item, context) {
3121
+ return [
3122
+ toolStatusHeader({
3123
+ status: "error",
3124
+ label: color(context.theme.error, item.project, true),
3125
+ suffix: dim(context, " worker error"),
3126
+ context,
3127
+ labelAlreadyStyled: true
3128
+ }),
3129
+ messageResponseText(color(context.theme.error, item.message), context)
3130
+ ].join("\n");
3131
+ }
3132
+ function renderTaskDispatch(item, context) {
3133
+ const count = item.tasks.length;
3134
+ const lines = [
3135
+ toolStatusHeader({
3136
+ status: "done",
3137
+ label: color(context.theme.text, `Running ${count} task${count === 1 ? "" : "s"}:`, true),
3138
+ context,
3139
+ dotColor: COLORS.primary,
3140
+ labelAlreadyStyled: true
3141
+ })
3142
+ ];
3143
+ for (const task of item.tasks) {
3144
+ lines.push(
3145
+ ` \u2022 ${color(projectColor(task.project), task.project, true)}${dim(context, ": ")}${color(context.theme.text, task.title)}`
3146
+ );
3147
+ }
3148
+ return lines.join("\n");
3149
+ }
3150
+ function toGGCoderCompletedItem(item) {
3151
+ switch (item.kind) {
3152
+ case "user":
3153
+ return { kind: "user", id: item.id, text: item.text };
3154
+ case "assistant":
3155
+ return {
3156
+ kind: "assistant",
3157
+ id: item.id,
3158
+ text: item.text,
3159
+ thinking: item.thinking,
3160
+ thinkingMs: item.thinkingMs,
3161
+ continuation: item.continuation
3162
+ };
3163
+ case "tool_start":
3164
+ return {
3165
+ kind: "tool_start",
3166
+ id: item.id,
3167
+ toolCallId: item.toolCallId,
3168
+ name: item.name,
3169
+ args: formatBossToolArgsForHistory(item.name, item.args),
3170
+ startedAt: item.startedAt,
3171
+ animateUntil: item.animateUntil,
3172
+ progressOutput: item.progressOutput
3173
+ };
3174
+ case "tool_done":
3175
+ return {
3176
+ kind: "tool_done",
3177
+ id: item.id,
3178
+ name: item.name,
3179
+ args: formatBossToolArgsForHistory(item.name, item.args),
3180
+ result: formatBossToolResultForHistory(item.name, item.result, item.isError),
3181
+ isError: item.isError,
3182
+ durationMs: item.durationMs,
3183
+ details: item.details
3184
+ };
3185
+ case "info":
3186
+ return { kind: "info", id: item.id, text: item.text };
3187
+ case "compacting":
3188
+ return { kind: "compacting", id: item.id };
3189
+ case "compacted":
3190
+ return {
3191
+ kind: "compacted",
3192
+ id: item.id,
3193
+ originalCount: item.originalCount,
3194
+ newCount: item.newCount,
3195
+ tokensBefore: item.tokensBefore,
3196
+ tokensAfter: item.tokensAfter
3197
+ };
3198
+ case "stopped":
3199
+ return { kind: "stopped", id: item.id, text: item.text };
3200
+ }
3201
+ }
3202
+ function formatBossToolArgsForHistory(name, args) {
3203
+ switch (name) {
3204
+ case "list_workers":
3205
+ return { action: "workers" };
3206
+ case "get_worker_status":
3207
+ case "get_worker_summary":
3208
+ return { action: String(args.project ?? "") };
3209
+ case "prompt_worker": {
3210
+ const project = String(args.project ?? "");
3211
+ const message = String(args.message ?? "").replace(/\s+/gu, " ");
3212
+ const fresh = args.fresh === true ? "fresh \xB7 " : "";
3213
+ const detail = project ? `${fresh}${project} \xB7 ${message}` : `${fresh}${message}`;
3214
+ return {
3215
+ action: truncatePlain(detail, Math.max(20, contextlessPromptWorkerDetailLen(project)))
3216
+ };
3217
+ }
3218
+ default:
3219
+ return args;
3220
+ }
3221
+ }
3222
+ function formatBossToolResultForHistory(name, result, isError) {
3223
+ if (isError) return result;
3224
+ const inline = formatBossToolInlineForHistory(name, result);
3225
+ if (!inline) return result;
3226
+ return typeof inline === "string" ? inline : inline.text;
3227
+ }
3228
+ function formatBossToolInlineForHistory(name, result) {
3229
+ switch (name) {
3230
+ case "list_workers": {
3231
+ const lines = result.split("\n").filter((lineText) => lineText.startsWith("-"));
3232
+ return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
3233
+ }
3234
+ case "prompt_worker": {
3235
+ if (result.includes("currently working")) return "busy \u2014 skipped";
3236
+ if (result.includes("Unknown project")) return "unknown project";
3237
+ return "dispatched";
3238
+ }
3239
+ case "get_worker_status": {
3240
+ const parts = result.split(":");
3241
+ if (parts.length < 2) return void 0;
3242
+ return parts.slice(1).join(":").trim();
3243
+ }
3244
+ case "get_worker_summary": {
3245
+ const turnMatch = result.match(/Turn:\s*(\d+)/u);
3246
+ const toolsMatch = result.match(/Tools used:\s*(.+)/u);
3247
+ const tools = toolsMatch ? toolsMatch[1] : "";
3248
+ const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
3249
+ const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
3250
+ const toolSummary = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
3251
+ return [turn, toolSummary].filter(Boolean).join(" \xB7 ") || void 0;
3252
+ }
3253
+ default:
3254
+ return void 0;
3255
+ }
3256
+ }
3257
+ function contextlessPromptWorkerDetailLen(project) {
3258
+ const cols = process.stdout.columns ?? 80;
3259
+ const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
3260
+ return Math.max(20, cols - fixed);
3261
+ }
3262
+ function toolStatusHeader({
3263
+ status,
3264
+ label,
3265
+ suffix = "",
3266
+ context,
3267
+ dotColor,
3268
+ labelAlreadyStyled = false
3269
+ }) {
3270
+ const resolvedDotColor = dotColor ?? (status === "error" ? context.theme.error : status === "done" ? context.theme.success : status === "queued" ? context.theme.warning : context.theme.spinnerColor);
3271
+ const indicator = status === "running" ? "\u280B" : "\u23FA";
3272
+ const labelText = labelAlreadyStyled ? label : color(context.theme.toolName, label, true);
3273
+ return `${RESPONSE_LEFT_PADDING}${color(resolvedDotColor, indicator)} ${labelText}${suffix}`;
3274
+ }
3275
+ function messageResponseText(text, context) {
3276
+ const [first, ...rest] = wrapPlain(text, Math.max(10, context.columns - 8)).split("\n");
3277
+ return [
3278
+ `${RESPONSE_LEFT_PADDING}${dim(context, " \u23BF ")}${first ?? ""}`,
3279
+ ...rest.map((lineText) => `${RESPONSE_LEFT_PADDING}${dim(context, " ")}${lineText}`)
3280
+ ].join("\n");
3281
+ }
3282
+ function renderRoundNoticeBox(lines, context, borderColor) {
3283
+ const width = Math.max(
3284
+ 4,
3285
+ Math.min(
3286
+ context.columns - RESPONSE_LEFT_PADDING.length,
3287
+ Math.max(...lines.map((lineText) => stripAnsi(lineText).length)) + 4
3288
+ )
3289
+ );
3290
+ const contentWidth = Math.max(1, width - 4);
3291
+ const top = `${color(borderColor, "\u256D")}${color(borderColor, "\u2500".repeat(width - 2))}${color(
3292
+ borderColor,
3293
+ "\u256E"
3294
+ )}`;
3295
+ const bottom = `${color(borderColor, "\u2570")}${color(borderColor, "\u2500".repeat(width - 2))}${color(
3296
+ borderColor,
3297
+ "\u256F"
3298
+ )}`;
3299
+ const body = lines.flatMap((lineText) => wrapPlain(lineText, contentWidth).split("\n")).map((lineText) => {
3300
+ const plainLength = stripAnsi(lineText).length;
3301
+ return `${color(borderColor, "\u2502")} ${lineText}${" ".repeat(Math.max(0, contentWidth - plainLength))} ${color(borderColor, "\u2502")}`;
3302
+ });
3303
+ return indent([top, ...body, bottom].join("\n"), RESPONSE_LEFT_PADDING);
3304
+ }
3305
+
3306
+ // src/auto-update.ts
3307
+ init_esm_shims();
3308
+ import { spawn as spawn2 } from "child_process";
3309
+ import fs4 from "fs";
3310
+ import path4 from "path";
3311
+ import os2 from "os";
3312
+ var PACKAGE_NAME = "@kenkaiiii/gg-boss";
3313
+ var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
3314
+ var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
3315
+ var FETCH_TIMEOUT_MS = 1e4;
3316
+ function getStateFilePath() {
3317
+ return path4.join(os2.homedir(), ".gg", "boss", "update-state.json");
3318
+ }
3319
+ function readState() {
3320
+ try {
3321
+ const raw = fs4.readFileSync(getStateFilePath(), "utf-8");
3322
+ return JSON.parse(raw);
3323
+ } catch {
3324
+ return null;
3325
+ }
3326
+ }
3327
+ function writeState(state) {
3328
+ try {
3329
+ const dir = path4.dirname(getStateFilePath());
3330
+ fs4.mkdirSync(dir, { recursive: true, mode: 448 });
3331
+ fs4.writeFileSync(getStateFilePath(), JSON.stringify(state));
3332
+ } catch {
3333
+ }
3334
+ }
3335
+ function compareVersions(a, b) {
3336
+ const pa = a.split(".").map(Number);
3337
+ const pb = b.split(".").map(Number);
3338
+ for (let i = 0; i < 3; i++) {
3339
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
3340
+ if (diff !== 0) return diff;
3341
+ }
2360
3342
  return 0;
2361
3343
  }
2362
3344
  function detectInstallInfo() {
@@ -2489,29 +3471,30 @@ function stopPeriodicUpdateCheck() {
2489
3471
  }
2490
3472
 
2491
3473
  // src/orchestrator-app.tsx
2492
- var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
3474
+ var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
2493
3475
  function BossApp(props) {
2494
3476
  const theme = loadTheme("dark");
2495
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TerminalSizeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AnimationProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BossAppInner, { ...props }) }) }) });
3477
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TerminalSizeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimationProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(BossAppInner, { ...props }) }) }) });
2496
3478
  }
2497
- function BossAppInner({ boss, resetUI }) {
3479
+ function BossAppInner({ boss, resetUI, terminalHistoryPrinter }) {
2498
3480
  const state = useBossState();
3481
+ const theme = useTheme();
2499
3482
  const { exit } = use_app_default();
2500
- const { stdout } = use_stdout_default();
2501
- const { resizeKey, columns, rows } = useTerminalSize();
2502
- const runStartRef = (0, import_react10.useRef)(null);
3483
+ const { stdout, write: writeStdout } = use_stdout_default();
3484
+ const { columns, rows } = useTerminalSize();
3485
+ const runStartRef = (0, import_react15.useRef)(null);
2503
3486
  runStartRef.current = state.runStartMs;
2504
- const charCountRef = (0, import_react10.useRef)(0);
3487
+ const charCountRef = (0, import_react15.useRef)(0);
2505
3488
  charCountRef.current = state.streaming?.text.length ?? 0;
2506
- const realTokensAccumRef = (0, import_react10.useRef)(0);
3489
+ const realTokensAccumRef = (0, import_react15.useRef)(0);
2507
3490
  realTokensAccumRef.current = state.bossInputTokens;
2508
- const [lastUserMessage, setLastUserMessage] = (0, import_react10.useState)("");
3491
+ const [lastUserMessage, setLastUserMessage] = (0, import_react15.useState)("");
2509
3492
  const overlay = state.overlay;
2510
- const [currentRadio, setCurrentRadio] = (0, import_react10.useState)(() => getCurrentStation());
2511
- const [updatePending, setUpdatePending] = (0, import_react10.useState)(
3493
+ const [currentRadio, setCurrentRadio] = (0, import_react15.useState)(() => getCurrentStation());
3494
+ const [updatePending, setUpdatePending] = (0, import_react15.useState)(
2512
3495
  () => getPendingUpdate(VERSION) !== null
2513
3496
  );
2514
- (0, import_react10.useEffect)(() => {
3497
+ (0, import_react15.useEffect)(() => {
2515
3498
  startPeriodicUpdateCheck(VERSION, (msg) => {
2516
3499
  bossStore.appendUpdateNotice(msg);
2517
3500
  setUpdatePending(true);
@@ -2519,8 +3502,8 @@ function BossAppInner({ boss, resetUI }) {
2519
3502
  return () => stopPeriodicUpdateCheck();
2520
3503
  }, []);
2521
3504
  const workersRunning = state.workers.filter((w) => w.status === "working").length;
2522
- const titlePrevRef = (0, import_react10.useRef)("");
2523
- (0, import_react10.useEffect)(() => {
3505
+ const titlePrevRef = (0, import_react15.useRef)("");
3506
+ (0, import_react15.useEffect)(() => {
2524
3507
  if (!stdout) return;
2525
3508
  let title;
2526
3509
  if (workersRunning > 0) {
@@ -2536,45 +3519,83 @@ function BossAppInner({ boss, resetUI }) {
2536
3519
  stdout.write(`\x1B]0;${title}\x1B\\`);
2537
3520
  }
2538
3521
  }, [stdout, workersRunning, state.phase]);
2539
- (0, import_react10.useEffect)(() => {
3522
+ (0, import_react15.useEffect)(() => {
2540
3523
  return () => {
2541
3524
  stdout?.write(`\x1B]0;GG Boss\x1B\\`);
2542
3525
  };
2543
3526
  }, [stdout]);
2544
- const staticItems = (0, import_react10.useMemo)(
2545
- () => [{ kind: "banner", id: "banner" }, ...state.history],
2546
- [state.history]
3527
+ const liveItems = state.liveItems;
3528
+ const terminalHistoryPrinterRef = (0, import_react15.useRef)(
3529
+ terminalHistoryPrinter ?? createBossTerminalHistoryPrinter({ stream: stdout })
2547
3530
  );
2548
- const openOverlay = (0, import_react10.useCallback)(
3531
+ const terminalHistoryContext = {
3532
+ theme,
3533
+ columns,
3534
+ version: VERSION,
3535
+ model: state.bossModel,
3536
+ provider: state.bossProvider,
3537
+ cwd: process.cwd()
3538
+ };
3539
+ const printedHistoryIdsRef = (0, import_react15.useRef)(/* @__PURE__ */ new Set());
3540
+ (0, import_react15.useEffect)(() => {
3541
+ const printer = terminalHistoryPrinterRef.current;
3542
+ const pending = state.history.filter((item) => !printedHistoryIdsRef.current.has(item.id));
3543
+ if (pending.length === 0) return;
3544
+ printer.print(pending, terminalHistoryContext, { write: writeStdout });
3545
+ for (const item of pending) printedHistoryIdsRef.current.add(item.id);
3546
+ }, [columns, state.bossModel, state.bossProvider, state.history, theme, writeStdout]);
3547
+ const overlayResetTimerRef = (0, import_react15.useRef)(null);
3548
+ (0, import_react15.useEffect)(() => {
3549
+ return () => {
3550
+ if (overlayResetTimerRef.current) clearTimeout(overlayResetTimerRef.current);
3551
+ };
3552
+ }, []);
3553
+ const scheduleOverlayReset = (0, import_react15.useCallback)(() => {
3554
+ if (!resetUI) return;
3555
+ if (overlayResetTimerRef.current) clearTimeout(overlayResetTimerRef.current);
3556
+ overlayResetTimerRef.current = setTimeout(() => {
3557
+ overlayResetTimerRef.current = null;
3558
+ resetUI();
3559
+ }, 0);
3560
+ }, [resetUI]);
3561
+ const openOverlay = (0, import_react15.useCallback)(
2549
3562
  (next) => {
2550
3563
  bossStore.setOverlay(next);
2551
- if (resetUI) resetUI();
3564
+ scheduleOverlayReset();
2552
3565
  },
2553
- [resetUI]
3566
+ [scheduleOverlayReset]
2554
3567
  );
2555
- const closeOverlay = (0, import_react10.useCallback)(() => {
3568
+ const closeOverlay = (0, import_react15.useCallback)(() => {
2556
3569
  bossStore.setOverlay(null);
2557
- if (resetUI) resetUI();
2558
- }, [resetUI]);
3570
+ scheduleOverlayReset();
3571
+ }, [scheduleOverlayReset]);
2559
3572
  void stdout;
2560
3573
  const handleDoubleExit = useDoublePress(
2561
3574
  (pending) => bossStore.setExitPending(pending),
2562
3575
  () => exit()
2563
3576
  );
2564
- (0, import_react10.useEffect)(() => {
3577
+ (0, import_react15.useEffect)(() => {
2565
3578
  if (state.pendingFlush.length > 0) {
2566
3579
  bossStore.commitPendingFlush();
2567
3580
  }
2568
3581
  }, [state.flushGeneration, state.pendingFlush.length]);
3582
+ const handleAbort = (0, import_react15.useCallback)(() => {
3583
+ if (state.phase === "working") {
3584
+ boss.abort();
3585
+ return;
3586
+ }
3587
+ handleDoubleExit();
3588
+ }, [boss, handleDoubleExit, state.phase]);
2569
3589
  use_input_default((input, key) => {
3590
+ if (key.ctrl && input === "c" && overlay) {
3591
+ handleAbort();
3592
+ return;
3593
+ }
2570
3594
  if (key.ctrl && input === "t") {
2571
3595
  if (overlay === "tasks") closeOverlay();
2572
3596
  else openOverlay("tasks");
2573
3597
  return;
2574
3598
  }
2575
- if (key.escape && state.phase === "working") {
2576
- boss.abort();
2577
- }
2578
3599
  });
2579
3600
  const handleSlashCommand = async (value) => {
2580
3601
  const parsed = parseSlash(value);
@@ -2591,7 +3612,7 @@ function BossAppInner({ boss, resetUI }) {
2591
3612
  return true;
2592
3613
  case "clear":
2593
3614
  bossStore.clearHistory();
2594
- resetUI?.();
3615
+ resetUI?.("session-clear");
2595
3616
  await boss.resetConversation();
2596
3617
  bossStore.appendInfo("Session cleared.", "info");
2597
3618
  return true;
@@ -2622,11 +3643,11 @@ function BossAppInner({ boss, resetUI }) {
2622
3643
  }
2623
3644
  const provider = value.slice(0, colon);
2624
3645
  const model = value.slice(colon + 1);
2625
- if (overlay === "model-boss") {
2626
- void boss.switchBossModel(provider, model);
2627
- } else if (overlay === "model-workers") {
2628
- void boss.switchWorkerModel(provider, model);
2629
- }
3646
+ const switchPromise = overlay === "model-boss" ? boss.switchBossModel(provider, model) : overlay === "model-workers" ? boss.switchWorkerModel(provider, model) : Promise.resolve();
3647
+ void switchPromise.catch((err) => {
3648
+ const message = err instanceof Error ? err.message : String(err);
3649
+ bossStore.appendInfo(`Model switch failed: ${message}`, "error");
3650
+ });
2630
3651
  closeOverlay();
2631
3652
  };
2632
3653
  const handleSubmit = (value) => {
@@ -2636,141 +3657,117 @@ function BossAppInner({ boss, resetUI }) {
2636
3657
  void handleSlashCommand(trimmed);
2637
3658
  return;
2638
3659
  }
2639
- bossStore.appendUser(trimmed);
2640
- setLastUserMessage(trimmed);
2641
- const scoped = scopePrefix2(state.scope) + trimmed;
2642
- boss.enqueueUserMessage(scoped);
2643
- };
2644
- const handleAbort = () => {
2645
- if (state.phase === "working") {
2646
- boss.abort();
2647
- return;
2648
- }
2649
- handleDoubleExit();
2650
- };
2651
- if (rows < 14) {
2652
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", width: columns, paddingX: 1, marginTop: 1, children: [
2653
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, color: COLORS.accent, children: "Terminal too small" }),
2654
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.primary, children: `Resize to at least 14 rows to use GG Boss (currently ${rows}).` })
2655
- ] });
2656
- }
2657
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", width: columns, children: [
2658
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Static, { items: staticItems, style: { width: "100%" }, children: (item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexDirection: "column", paddingRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StaticRowView, { row: item }) }, item.id) }, resizeKey),
2659
- overlay === "tasks" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BossTasksOverlay, { boss, workers: state.workers, onClose: closeOverlay }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2660
- state.streaming && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StreamingTurnView, { turn: state.streaming, isRunning: state.phase === "working" }),
2661
- state.phase === "working" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2662
- ActivityIndicator,
2663
- {
2664
- phase: state.activityPhase,
2665
- elapsedMs: state.runStartMs ? Date.now() - state.runStartMs : 0,
2666
- runStartRef,
2667
- thinkingMs: state.streaming?.thinkingMs ?? 0,
2668
- isThinking: state.activityPhase === "thinking",
2669
- tokenEstimate: state.bossInputTokens,
2670
- charCountRef,
2671
- realTokensAccumRef,
2672
- userMessage: lastUserMessage,
2673
- activeToolNames: (state.streaming?.tools ?? []).filter((t) => t.status === "running").map((t) => t.name),
2674
- retryInfo: state.retryInfo,
2675
- phrases: BOSS_PHRASES,
2676
- pulseColors: PULSE_COLORS
2677
- }
2678
- ) }),
2679
- state.compaction?.state === "running" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CompactionSpinner, {}),
2680
- state.compaction?.state === "done" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2681
- CompactionDone,
2682
- {
2683
- originalCount: state.compaction.originalCount,
2684
- newCount: state.compaction.newCount,
2685
- tokensBefore: state.compaction.tokensBefore,
2686
- tokensAfter: state.compaction.tokensAfter
2687
- }
2688
- ),
2689
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2690
- InputArea,
2691
- {
2692
- onSubmit: handleSubmit,
2693
- onAbort: handleAbort,
2694
- disabled: state.phase === "working",
2695
- isActive: !overlay,
2696
- cwd: process.cwd(),
2697
- commands: BOSS_SLASH_COMMANDS,
2698
- scopeBadge: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScopePill, { scope: state.scope }),
2699
- disableMouseTracking: true,
2700
- onTab: () => bossStore.cycleScope(),
2701
- onShiftTab: () => {
2702
- const next = state.bossThinkingLevel ? void 0 : "medium";
2703
- void boss.setBossThinking(next);
2704
- }
2705
- }
2706
- ),
2707
- overlay === "model-boss" || overlay === "model-workers" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2708
- ModelSelector,
2709
- {
2710
- onSelect: handleModelSelect,
2711
- onCancel: closeOverlay,
2712
- loggedInProviders: state.loggedInProviders,
2713
- currentModel: overlay === "model-boss" ? state.bossModel : state.workerModel,
2714
- currentProvider: overlay === "model-boss" ? state.bossProvider : state.workerProvider
2715
- }
2716
- ) : overlay === "radio" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2717
- RadioPicker,
2718
- {
2719
- currentStationId: currentRadio,
2720
- onCancel: closeOverlay,
2721
- onSelect: (value) => {
2722
- if (value === "off") {
2723
- stopRadio();
2724
- setCurrentRadio(null);
2725
- bossStore.appendInfo("Radio off.", "info");
2726
- } else {
2727
- const result = playRadio(value);
2728
- if (result.ok) {
2729
- setCurrentRadio(value);
2730
- const station = RADIO_STATIONS.find((s) => s.id === value);
2731
- bossStore.appendInfo(`Now playing: ${station?.name ?? value}`, "info");
2732
- } else {
2733
- bossStore.appendInfo(result.error ?? "Radio failed to start.", "warning");
2734
- }
2735
- }
2736
- closeOverlay();
3660
+ const userItem = bossStore.createUserItem(trimmed);
3661
+ terminalHistoryPrinterRef.current.print([userItem], terminalHistoryContext, {
3662
+ write: writeStdout
3663
+ });
3664
+ printedHistoryIdsRef.current.add(userItem.id);
3665
+ bossStore.queueSubmittedUserItem(userItem);
3666
+ setLastUserMessage(trimmed);
3667
+ const scoped = scopePrefix2(state.scope) + trimmed;
3668
+ boss.enqueueUserMessage(scoped);
3669
+ };
3670
+ const activityVisible = state.phase === "working" && state.activityPhase !== "idle";
3671
+ const stallStatusVisible = false;
3672
+ const doneStatus = null;
3673
+ const statusSlotVisible = activityVisible || stallStatusVisible || !!doneStatus;
3674
+ const controlsRows = 7 + (statusSlotVisible ? 1 : 0) + (state.exitPending ? 0 : 1);
3675
+ const availableLiveRows = Math.max(1, rows - controlsRows);
3676
+ if (rows < 14) {
3677
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "column", width: columns, paddingX: 1, marginTop: 1, children: [
3678
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { bold: true, color: COLORS.accent, children: "Terminal too small" }),
3679
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: COLORS.primary, children: `Resize to at least 14 rows to use GG Boss (currently ${rows}).` })
3680
+ ] });
3681
+ }
3682
+ const lastPendingHistoryItem = state.pendingFlush[state.pendingFlush.length - 1];
3683
+ const lastHistoryItem = state.history[state.history.length - 1];
3684
+ const livePane = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3685
+ BossStreamingTurnView,
3686
+ {
3687
+ turn: state.streaming,
3688
+ isRunning: state.phase === "working",
3689
+ liveItems,
3690
+ lastPendingHistoryItem,
3691
+ lastHistoryItem,
3692
+ availableTerminalHeight: availableLiveRows
3693
+ }
3694
+ );
3695
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3696
+ BossChatScreen,
3697
+ {
3698
+ boss,
3699
+ columns,
3700
+ state,
3701
+ overlay,
3702
+ livePane,
3703
+ theme,
3704
+ statusSlotVisible,
3705
+ activityVisible,
3706
+ stallStatusVisible,
3707
+ doneStatus,
3708
+ elapsedMs: state.runStartMs ? Date.now() - state.runStartMs : 0,
3709
+ runStartRef,
3710
+ charCountRef,
3711
+ realTokensAccumRef,
3712
+ lastUserMessage,
3713
+ activeToolNames: (state.streaming?.tools ?? []).filter((tool) => tool.status === "running").map((tool) => tool.name),
3714
+ inputActive: !overlay,
3715
+ isRunning: state.phase === "working",
3716
+ onSubmit: handleSubmit,
3717
+ onAbort: handleAbort,
3718
+ onTab: () => bossStore.cycleScope(),
3719
+ onShiftTab: () => {
3720
+ const next = getNextThinkingLevel(
3721
+ state.bossProvider,
3722
+ state.bossModel,
3723
+ state.bossThinkingLevel
3724
+ );
3725
+ void boss.setBossThinking(next);
3726
+ },
3727
+ commands: BOSS_SLASH_COMMANDS,
3728
+ scopeBadge: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ScopePill, { scope: state.scope }),
3729
+ onCloseOverlay: closeOverlay,
3730
+ onModelSelect: handleModelSelect,
3731
+ currentRadio,
3732
+ onRadioSelect: (value) => {
3733
+ if (value === "off") {
3734
+ stopRadio();
3735
+ setCurrentRadio(null);
3736
+ bossStore.appendInfo("Radio off.", "info");
3737
+ } else {
3738
+ const result = playRadio(value);
3739
+ if (result.ok) {
3740
+ setCurrentRadio(value);
3741
+ const station = RADIO_STATIONS.find((stationInfo) => stationInfo.id === value);
3742
+ bossStore.appendInfo(`Now playing: ${station?.name ?? value}`, "info");
3743
+ } else {
3744
+ bossStore.appendInfo(result.error ?? "Radio failed to start.", "warning");
2737
3745
  }
2738
3746
  }
2739
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2740
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2741
- BossFooter,
2742
- {
2743
- bossModel: state.bossModel,
2744
- workerModel: state.workerModel,
2745
- tokensIn: state.bossInputTokens,
2746
- exitPending: state.exitPending,
2747
- bossThinkingLevel: state.bossThinkingLevel,
2748
- updatePending,
2749
- currentRadioStationId: currentRadio
2750
- }
2751
- ),
2752
- !state.exitPending && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2753
- WorkerStatusBar,
2754
- {
2755
- workers: state.workers,
2756
- pendingMessages: state.pendingUserMessages
2757
- }
2758
- )
2759
- ] })
2760
- ] })
2761
- ] });
3747
+ closeOverlay();
3748
+ },
3749
+ bossModel: state.bossModel,
3750
+ workerModel: state.workerModel,
3751
+ updatePending,
3752
+ currentRadioStationId: currentRadio,
3753
+ radioStations: RADIO_STATIONS,
3754
+ workers: state.workers,
3755
+ pendingMessages: state.pendingUserMessages,
3756
+ formatDuration: formatBossDuration
3757
+ }
3758
+ );
2762
3759
  }
2763
3760
  function ScopePill({ scope }) {
2764
3761
  const theme = useTheme();
2765
3762
  const isAll = scope === "all";
2766
3763
  const bg = isAll ? COLORS.accent : projectColor(scope);
2767
3764
  const label = isAll ? "All" : scope;
2768
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
2769
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: "Project " }),
2770
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: "black", backgroundColor: bg, bold: true, children: ` ${label} ` }),
2771
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
3765
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text, { children: [
3766
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: theme.textDim, children: "Project " }),
3767
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "black", backgroundColor: bg, bold: true, children: ` ${label} ` }),
3768
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text, { color: theme.textDim, children: [
2772
3769
  " ",
2773
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.primary, children: "Tab" }),
3770
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: theme.primary, children: "Tab" }),
2774
3771
  " to switch"
2775
3772
  ] })
2776
3773
  ] });
@@ -2779,396 +3776,89 @@ function scopePrefix2(scope) {
2779
3776
  if (scope === "all") return "[scope:all] ";
2780
3777
  return `[scope:${scope}] `;
2781
3778
  }
2782
- var SHIMMER_WIDTH = 3;
2783
- function formatElapsed(ms) {
2784
- const total = Math.floor(ms / 1e3);
2785
- const m = Math.floor(total / 60);
2786
- const s = total % 60;
2787
- return `${m}:${s.toString().padStart(2, "0")}`;
2788
- }
2789
- function AnimationActiveSentinel() {
2790
- useAnimationActive();
2791
- return null;
2792
- }
2793
- function ShimmerName({
2794
- name,
2795
- color,
2796
- tick
2797
- }) {
2798
- const cycle = name.length + SHIMMER_WIDTH * 2;
2799
- const shimmerPos = tick % cycle - SHIMMER_WIDTH;
2800
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: name.split("").map((ch, i) => {
2801
- const isBright = Math.abs(i - shimmerPos) <= SHIMMER_WIDTH;
2802
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color, bold: isBright, dimColor: !isBright, children: ch }, i);
2803
- }) });
2804
- }
2805
- function WorkerStatusBar({
2806
- workers,
2807
- pendingMessages
2808
- }) {
2809
- const theme = useTheme();
2810
- const { columns } = useTerminalSize();
2811
- const working = workers.filter((w) => w.status === "working");
2812
- const errored = workers.filter((w) => w.status === "error");
2813
- const idleCount = workers.length - working.length - errored.length;
2814
- const anyWorking = working.length > 0;
2815
- const tick = useAnimationTick();
2816
- const now = Date.now();
2817
- if (workers.length === 0) return null;
2818
- const slots = [];
2819
- for (const w of working) {
2820
- const projectHue = projectColor(w.name);
2821
- const elapsed = w.workStartedAt ? formatElapsed(now - w.workStartedAt) : null;
2822
- slots.push(
2823
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react10.default.Fragment, { children: [
2824
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ShimmerName, { name: w.name, color: projectHue, tick }),
2825
- elapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
2826
- " ",
2827
- elapsed
2828
- ] })
2829
- ] }, `w-${w.name}`)
2830
- );
2831
- }
2832
- for (const w of errored) {
2833
- slots.push(
2834
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react10.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.error, children: [
2835
- "\u2717 ",
2836
- w.name
2837
- ] }) }, `e-${w.name}`)
2838
- );
2839
- }
2840
- if (idleCount > 0) {
2841
- slots.push(
2842
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react10.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
2843
- "\u25CB ",
2844
- idleCount,
2845
- " idle"
2846
- ] }) }, "idle")
2847
- );
2848
- }
2849
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { paddingX: 1, width: columns, flexShrink: 1, children: [
2850
- anyWorking && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AnimationActiveSentinel, {}),
2851
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "truncate", children: [
2852
- slots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react10.default.Fragment, { children: [
2853
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.border, children: " \u2502 " }),
2854
- slot
2855
- ] }, i)),
2856
- pendingMessages > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2857
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " " }),
2858
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.warning, children: [
2859
- pendingMessages,
2860
- " message",
2861
- pendingMessages === 1 ? "" : "s",
2862
- " queued"
2863
- ] })
2864
- ] })
2865
- ] })
2866
- ] });
2867
- }
2868
- function StaticRowView({ row }) {
2869
- if (row.kind === "banner") {
2870
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BossBanner, { subtitle: "Orchestrator", showShortcuts: true }) });
2871
- }
2872
- if (row.kind === "user") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(UserMessage, { text: row.text });
2873
- if (row.kind === "assistant") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AssistantRow, { item: row });
2874
- if (row.kind === "tool") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolHistoryRow, { item: row });
2875
- if (row.kind === "worker_event") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WorkerEventRow, { item: row });
2876
- if (row.kind === "worker_error") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WorkerErrorRow, { item: row });
2877
- if (row.kind === "info") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InfoRow, { text: row.text, level: row.level ?? "info" });
2878
- if (row.kind === "task_dispatch") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TaskDispatchRow, { tasks: row.tasks });
2879
- if (row.kind === "update_notice") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(UpdateNoticeRow, { text: row.text });
2880
- return null;
2881
- }
2882
- function UpdateNoticeRow({ text }) {
2883
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { marginTop: 1, flexShrink: 1, borderStyle: "round", borderColor: COLORS.accent, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "wrap", children: [
2884
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.accent, bold: true, children: "\u2728 " }),
2885
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.primary, bold: true, children: text })
2886
- ] }) });
2887
- }
2888
- function TaskDispatchRow({
2889
- tasks
2890
- }) {
2891
- const theme = useTheme();
2892
- const count = tasks.length;
2893
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
2894
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
2895
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.primary, bold: true, children: "\u23FA " }),
2896
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.text, bold: true, children: [
2897
- "Running ",
2898
- count,
2899
- " task",
2900
- count === 1 ? "" : "s",
2901
- ":"
2902
- ] })
2903
- ] }),
2904
- tasks.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
2905
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \u2022 " }),
2906
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: projectColor(t.project), bold: true, children: t.project }),
2907
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ": " }),
2908
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: t.title })
2909
- ] }, `${t.project}-${i}`))
2910
- ] });
2911
- }
2912
- var SHORTCUT_PATTERNS = [
2913
- // Modifier+Key combos: Ctrl+T, Shift+Tab, Cmd+K, Ctrl+Shift+P, Ctrl+C
2914
- /\b(?:Ctrl|Cmd|Alt|Option|Opt|Shift|Meta|Win|Super)(?:\s*\+\s*(?:Ctrl|Cmd|Alt|Option|Opt|Shift|Meta|Win|Super))*\s*\+\s*(?:Tab|Enter|Esc|Escape|Space|Backspace|Delete|Del|Home|End|PageUp|PageDown|Up|Down|Left|Right|F[1-9]|F1[0-2]|[A-Z0-9]|\/|\?|\.|,|;|=|-)\b/g,
2915
- // Bare named keys (only when surrounded by clear key context)
2916
- /\b(?:Ctrl-[A-Z]|F[1-9]|F1[0-2])\b/g
2917
- ];
2918
- function highlightShortcuts(text) {
2919
- if (!text) return text;
2920
- const SENTINEL = "\uE000";
2921
- const masks = [];
2922
- let masked = text.replace(/```[\s\S]*?```|`[^`]+`/g, (m) => {
2923
- const idx = masks.push(m) - 1;
2924
- return `${SENTINEL}${idx}${SENTINEL}`;
2925
- });
2926
- for (const re of SHORTCUT_PATTERNS) {
2927
- masked = masked.replace(re, (m) => `\`${m}\``);
2928
- }
2929
- return masked.replace(
2930
- new RegExp(`${SENTINEL}(\\d+)${SENTINEL}`, "g"),
2931
- (_, i) => masks[Number(i)]
2932
- );
2933
- }
2934
- function AssistantRow({ item }) {
2935
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2936
- AssistantMessage,
2937
- {
2938
- text: highlightShortcuts(item.text),
2939
- thinking: item.thinking,
2940
- thinkingMs: item.thinkingMs
2941
- }
2942
- );
3779
+ function formatBossDuration(durationMs) {
3780
+ const total = Math.max(0, Math.floor(durationMs / 1e3));
3781
+ const minutes = Math.floor(total / 60);
3782
+ const seconds = total % 60;
3783
+ return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
2943
3784
  }
2944
- function ToolHistoryRow({ item }) {
2945
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2946
- ToolExecution,
2947
- {
2948
- status: "done",
2949
- name: item.name,
2950
- args: item.args,
2951
- result: item.result,
2952
- isError: item.isError,
2953
- details: item.details,
2954
- formatters: bossToolFormatters
3785
+ var INK_OPTIONS = {
3786
+ // Match ggcoder's keyboard setup: enable kitty keyboard so Ink can decode
3787
+ // enhanced key events, but keep exitOnCtrlC false so our handlers receive it.
3788
+ kittyKeyboard: {
3789
+ mode: "enabled",
3790
+ flags: ["disambiguateEscapeCodes"]
3791
+ },
3792
+ exitOnCtrlC: false
3793
+ };
3794
+ var DISABLE_MODIFY_OTHER_KEYS = "\x1B[>4;0m";
3795
+ var DISABLE_FOCUS_REPORTING = "\x1B[?1004l";
3796
+ var SCREEN_CLEAR = DISABLE_MODIFY_OTHER_KEYS + "\x1B[2J\x1B[3J\x1B[H";
3797
+ var VIEWPORT_CLEAR = DISABLE_MODIFY_OTHER_KEYS + "\x1B[2J\x1B[H";
3798
+ function renderBossApp(opts) {
3799
+ const terminalHistoryPrinter = createBossTerminalHistoryPrinter({ stream: process.stdout });
3800
+ process.stdout.write(SCREEN_CLEAR);
3801
+ const onProcessExit = () => {
3802
+ try {
3803
+ process.stdout.write(DISABLE_MODIFY_OTHER_KEYS + DISABLE_FOCUS_REPORTING);
3804
+ } catch {
2955
3805
  }
2956
- );
2957
- }
2958
- function parseStatusGrade(text) {
2959
- const matches = [...text.matchAll(/^\s*Status:\s*(DONE|UNVERIFIED|PARTIAL|BLOCKED|INFO)\b/gim)];
2960
- const last = matches[matches.length - 1];
2961
- if (!last) return null;
2962
- return last[1].toUpperCase();
2963
- }
2964
- function parseWorkerTrailer(text) {
2965
- const out = {};
2966
- const grab = (label) => {
2967
- const re = new RegExp(
2968
- `^\\s*${label}:\\s*([\\s\\S]*?)(?=^\\s*(?:Changed|Skipped|Verified|Notes|Status):|$)`,
2969
- "im"
2970
- );
2971
- const m = re.exec(text);
2972
- if (!m) return void 0;
2973
- const v = m[1].replace(/```[\s\S]*?```/g, "[code]").replace(/`([^`]+)`/g, "$1").replace(/\s+/g, " ").trim();
2974
- return v.length > 0 ? v : void 0;
2975
3806
  };
2976
- out.changed = grab("Changed");
2977
- out.skipped = grab("Skipped");
2978
- out.verified = grab("Verified");
2979
- out.notes = grab("Notes");
2980
- return out;
2981
- }
2982
- function clip(text, maxLen) {
2983
- return text.length <= maxLen ? text : text.slice(0, Math.max(1, maxLen - 1)) + "\u2026";
2984
- }
2985
- function summarizeFinalText(text, maxLen) {
2986
- if (!text) return "";
2987
- const trailer = parseWorkerTrailer(text);
2988
- const parts = [];
2989
- if (trailer.changed) parts.push(`Changed: ${trailer.changed}`);
2990
- if (trailer.verified) parts.push(`Verified: ${trailer.verified}`);
2991
- if (trailer.skipped) parts.push(`Skipped: ${trailer.skipped}`);
2992
- if (trailer.notes) parts.push(`Notes: ${trailer.notes}`);
2993
- if (parts.length > 0) return clip(parts.join(" \xB7 "), maxLen);
2994
- const beforeSummary = text.split(/^Changed:|^Skipped:|^Verified:|^Notes:|^Status:/im)[0];
2995
- const stripped = beforeSummary.replace(/```[\s\S]*?```/g, "[code]").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/^\s*[-*]\s+/gm, "").replace(/^#+\s+/gm, "").replace(/\s+/g, " ").trim();
2996
- if (!stripped) return "";
2997
- const firstSentence = stripped.match(/^[^.!?\n]+[.!?]/);
2998
- return clip(firstSentence ? firstSentence[0] : stripped, maxLen);
2999
- }
3000
- function statusGradeColor(grade, theme) {
3001
- switch (grade) {
3002
- case "DONE":
3003
- return theme.success;
3004
- case "UNVERIFIED":
3005
- case "PARTIAL":
3006
- return theme.warning;
3007
- case "BLOCKED":
3008
- return theme.error;
3009
- case "INFO":
3010
- return theme.textDim;
3011
- default:
3012
- return theme.textDim;
3013
- }
3014
- }
3015
- function WorkerEventRow({ item }) {
3016
- const theme = useTheme();
3017
- const { columns } = useTerminalSize();
3018
- const failedCount = item.toolsUsed.filter((t) => !t.ok).length;
3019
- const total = item.toolsUsed.length;
3020
- const grade = parseStatusGrade(item.finalText);
3021
- const loaderStatus = grade === "BLOCKED" || failedCount > 0 ? "error" : grade === "UNVERIFIED" || grade === "PARTIAL" ? "queued" : "done";
3022
- const headerColor = loaderStatus === "error" ? theme.toolError : projectColor(item.project);
3023
- const toolSummary = total === 0 ? "no tools" : failedCount > 0 ? `${total} tools (${failedCount} failed)` : `${total} tool${total === 1 ? "" : "s"}`;
3024
- const fieldMaxLen = Math.max(20, columns - 14);
3025
- const trailer = parseWorkerTrailer(item.finalText);
3026
- const hasTrailer = !!(trailer.changed || trailer.skipped || trailer.verified || trailer.notes);
3027
- const fallbackSummary = hasTrailer ? "" : summarizeFinalText(item.finalText, fieldMaxLen);
3028
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
3029
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "row", children: [
3030
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolUseLoader, { status: loaderStatus }),
3031
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "wrap", children: [
3032
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: headerColor, bold: true, children: item.project }),
3033
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: ` turn ${item.turnIndex}` }),
3034
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${toolSummary}` }),
3035
- grade && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3036
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
3037
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: statusGradeColor(grade, theme), bold: true, children: grade })
3038
- ] })
3039
- ] }) })
3040
- ] }),
3041
- hasTrailer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3042
- trailer.changed && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrailerLine, { label: "Changed", value: trailer.changed, maxLen: fieldMaxLen }),
3043
- trailer.verified && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3044
- TrailerLine,
3045
- {
3046
- label: "Verified",
3047
- value: trailer.verified,
3048
- maxLen: fieldMaxLen,
3049
- labelColor: theme.success
3050
- }
3051
- ),
3052
- trailer.skipped && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3053
- TrailerLine,
3054
- {
3055
- label: "Skipped",
3056
- value: trailer.skipped,
3057
- maxLen: fieldMaxLen,
3058
- labelColor: theme.warning
3059
- }
3060
- ),
3061
- trailer.notes && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrailerLine, { label: "Notes", value: trailer.notes, maxLen: fieldMaxLen })
3062
- ] }) : fallbackSummary && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: fallbackSummary }) })
3063
- ] });
3064
- }
3065
- function TrailerLine({
3066
- label,
3067
- value,
3068
- maxLen,
3069
- labelColor
3070
- }) {
3071
- const theme = useTheme();
3072
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "truncate", children: [
3073
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: labelColor ?? theme.textDim, bold: true, children: [
3074
- label,
3075
- ":"
3076
- ] }),
3077
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: ` ${clip(value, maxLen - label.length - 2)}` })
3078
- ] }) });
3079
- }
3080
- function WorkerErrorRow({ item }) {
3081
- const theme = useTheme();
3082
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
3083
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "row", children: [
3084
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolUseLoader, { status: "error" }),
3085
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "wrap", children: [
3086
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.toolError, bold: true, children: item.project }),
3087
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " worker error" })
3088
- ] }) })
3089
- ] }),
3090
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.error, wrap: "wrap", children: item.message }) })
3091
- ] });
3092
- }
3093
- function InfoRow({
3094
- text,
3095
- level
3096
- }) {
3097
- if (level === "info") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AssistantMessage, { text });
3098
- const theme = useTheme();
3099
- const color = level === "error" ? theme.error : theme.warning;
3100
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { marginTop: 1, flexDirection: "row", children: [
3101
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolUseLoader, { status: level === "error" ? "error" : "queued" }),
3102
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color, wrap: "wrap", children: text }) })
3103
- ] });
3104
- }
3105
- function StreamingTurnView({
3106
- turn,
3107
- isRunning
3108
- }) {
3109
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
3110
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3111
- StreamingArea,
3112
- {
3113
- isRunning,
3114
- streamingText: turn.text,
3115
- streamingThinking: turn.thinking,
3116
- thinkingMs: turn.thinkingMs
3117
- }
3118
- ),
3119
- turn.tools.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StreamingToolRow, { tool: t }, t.toolCallId))
3120
- ] });
3121
- }
3122
- function StreamingToolRow({ tool }) {
3123
- if (tool.status === "running") {
3124
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3125
- ToolExecution,
3126
- {
3127
- status: "running",
3128
- name: tool.name,
3129
- args: tool.args,
3130
- formatters: bossToolFormatters
3131
- }
3132
- );
3133
- }
3134
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3135
- ToolExecution,
3136
- {
3137
- status: "done",
3138
- name: tool.name,
3139
- args: tool.args,
3140
- result: tool.result ?? "",
3141
- isError: tool.status === "error",
3142
- details: tool.details,
3143
- formatters: bossToolFormatters
3144
- }
3145
- );
3146
- }
3147
- function renderBossApp(opts) {
3807
+ process.on("exit", onProcessExit);
3148
3808
  const ref = { instance: null };
3149
- const resetUI = () => {
3809
+ const resetUI = (reason = "viewport") => {
3150
3810
  const old = ref.instance;
3151
3811
  if (!old) return;
3152
- process.stdout.write("\x1B[2J\x1B[H");
3153
3812
  old.unmount();
3154
- ref.instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BossApp, { boss: opts.boss, resetUI }), {
3155
- exitOnCtrlC: false
3156
- });
3813
+ if (reason === "resize-redraw") {
3814
+ terminalHistoryPrinter.resetPrinted();
3815
+ process.stdout.write(SCREEN_CLEAR);
3816
+ const snapshot = getBossState();
3817
+ if (snapshot.history.length > 0) {
3818
+ terminalHistoryPrinter.print(snapshot.history, {
3819
+ theme: loadTheme("dark"),
3820
+ columns: Math.max(40, process.stdout.columns ?? 80),
3821
+ version: VERSION,
3822
+ model: snapshot.bossModel,
3823
+ provider: snapshot.bossProvider,
3824
+ cwd: process.cwd()
3825
+ });
3826
+ }
3827
+ } else if (reason === "session-clear") {
3828
+ terminalHistoryPrinter.clear();
3829
+ process.stdout.write(SCREEN_CLEAR);
3830
+ } else {
3831
+ process.stdout.write(VIEWPORT_CLEAR);
3832
+ }
3833
+ ref.instance = render_default(
3834
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3835
+ BossApp,
3836
+ {
3837
+ boss: opts.boss,
3838
+ resetUI,
3839
+ terminalHistoryPrinter
3840
+ }
3841
+ ),
3842
+ INK_OPTIONS
3843
+ );
3157
3844
  };
3158
- const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BossApp, { boss: opts.boss, resetUI }), {
3159
- // Disable Ink's built-in exit-on-Ctrl+C we need our own double-press
3160
- // handler in BossApp to drive the "Press Ctrl+C again to exit" footer
3161
- // message. With this flag true (the default), Ink kills the process on
3162
- // the very first Ctrl+C and InputArea's onAbort never runs.
3163
- exitOnCtrlC: false
3164
- });
3845
+ const instance = render_default(
3846
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(BossApp, { boss: opts.boss, resetUI, terminalHistoryPrinter }),
3847
+ INK_OPTIONS
3848
+ );
3165
3849
  ref.instance = instance;
3166
3850
  let resizeTimer = null;
3851
+ let resizeListenerEnabled = false;
3852
+ const enableResizeListener = setTimeout(() => {
3853
+ resizeListenerEnabled = true;
3854
+ }, 1e3);
3167
3855
  const onTerminalResize = () => {
3856
+ if (!resizeListenerEnabled) return;
3168
3857
  if (resizeTimer) clearTimeout(resizeTimer);
3169
3858
  resizeTimer = setTimeout(() => {
3170
3859
  resizeTimer = null;
3171
- resetUI();
3860
+ if (getBossState().phase === "working") return;
3861
+ resetUI("resize-redraw");
3172
3862
  }, 250);
3173
3863
  };
3174
3864
  process.stdout.on("resize", onTerminalResize);
@@ -3183,21 +3873,30 @@ function renderBossApp(opts) {
3183
3873
  const current = ref.instance;
3184
3874
  if (!current) {
3185
3875
  process.stdout.off("resize", onTerminalResize);
3876
+ process.off("exit", onProcessExit);
3877
+ clearTimeout(enableResizeListener);
3186
3878
  if (resizeTimer) clearTimeout(resizeTimer);
3879
+ onProcessExit();
3187
3880
  return;
3188
3881
  }
3189
3882
  await current.waitUntilExit();
3190
3883
  if (ref.instance === current) {
3191
3884
  ref.instance = null;
3192
3885
  process.stdout.off("resize", onTerminalResize);
3886
+ process.off("exit", onProcessExit);
3887
+ clearTimeout(enableResizeListener);
3193
3888
  if (resizeTimer) clearTimeout(resizeTimer);
3889
+ onProcessExit();
3194
3890
  return;
3195
3891
  }
3196
3892
  }
3197
3893
  },
3198
3894
  unmount: () => {
3199
3895
  process.stdout.off("resize", onTerminalResize);
3896
+ process.off("exit", onProcessExit);
3897
+ clearTimeout(enableResizeListener);
3200
3898
  if (resizeTimer) clearTimeout(resizeTimer);
3899
+ onProcessExit();
3201
3900
  ref.instance?.unmount();
3202
3901
  }
3203
3902
  };
@@ -3205,8 +3904,8 @@ function renderBossApp(opts) {
3205
3904
 
3206
3905
  // src/splash.tsx
3207
3906
  init_esm_shims();
3208
- var import_react11 = __toESM(require_react(), 1);
3209
- var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
3907
+ var import_react16 = __toESM(require_react(), 1);
3908
+ var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
3210
3909
  var SPLASH_LINES = [
3211
3910
  " \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ",
3212
3911
  " \u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588 \u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588 ",
@@ -3224,33 +3923,33 @@ function colorForLine(lineIdx, totalLines, offset) {
3224
3923
  return GRADIENT[idx];
3225
3924
  }
3226
3925
  function SplashLogo({ offset }) {
3227
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", children: SPLASH_LINES.map((line, i) => {
3926
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { flexDirection: "column", children: SPLASH_LINES.map((line, i) => {
3228
3927
  const hue = colorForLine(i, SPLASH_LINES.length, offset);
3229
3928
  const segments = [];
3230
3929
  let buf = "";
3231
3930
  let bufDim = false;
3232
3931
  for (const ch of line) {
3233
- const dim = ch === "\u2591";
3932
+ const dim2 = ch === "\u2591";
3234
3933
  if (segments.length === 0 && buf.length === 0) {
3235
3934
  buf = ch;
3236
- bufDim = dim;
3935
+ bufDim = dim2;
3237
3936
  continue;
3238
3937
  }
3239
- if (dim === bufDim) {
3938
+ if (dim2 === bufDim) {
3240
3939
  buf += ch;
3241
3940
  } else {
3242
3941
  segments.push({ text: buf, dim: bufDim });
3243
3942
  buf = ch;
3244
- bufDim = dim;
3943
+ bufDim = dim2;
3245
3944
  }
3246
3945
  }
3247
3946
  if (buf) segments.push({ text: buf, dim: bufDim });
3248
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: segments.map((seg, j) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: hue, dimColor: seg.dim, children: seg.text }, j)) }, i);
3947
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { children: segments.map((seg, j) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { color: hue, dimColor: seg.dim, children: seg.text }, j)) }, i);
3249
3948
  }) });
3250
3949
  }
3251
3950
  function SplashScreen({ caption }) {
3252
- const [offset, setOffset] = (0, import_react11.useState)(0);
3253
- (0, import_react11.useEffect)(() => {
3951
+ const [offset, setOffset] = (0, import_react16.useState)(0);
3952
+ (0, import_react16.useEffect)(() => {
3254
3953
  const timer = setInterval(() => {
3255
3954
  setOffset((o) => o + 1);
3256
3955
  }, 120);
@@ -3258,11 +3957,11 @@ function SplashScreen({ caption }) {
3258
3957
  clearInterval(timer);
3259
3958
  };
3260
3959
  }, []);
3261
- const [size, setSize] = (0, import_react11.useState)(() => ({
3960
+ const [size, setSize] = (0, import_react16.useState)(() => ({
3262
3961
  columns: process.stdout.columns ?? 80,
3263
3962
  rows: process.stdout.rows ?? 24
3264
3963
  }));
3265
- (0, import_react11.useEffect)(() => {
3964
+ (0, import_react16.useEffect)(() => {
3266
3965
  const handler = () => setSize({
3267
3966
  columns: process.stdout.columns ?? 80,
3268
3967
  rows: process.stdout.rows ?? 24
@@ -3274,27 +3973,27 @@ function SplashScreen({ caption }) {
3274
3973
  }, []);
3275
3974
  const SPLASH_BLOCK_HEIGHT = SPLASH_LINES.length + 3;
3276
3975
  const verticalPad = Math.max(0, Math.floor((size.rows - SPLASH_BLOCK_HEIGHT) / 2));
3277
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", width: size.columns, height: size.rows, alignItems: "center", children: [
3278
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { height: verticalPad, flexShrink: 0 }),
3279
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", alignItems: "flex-start", flexShrink: 0, children: [
3280
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SplashLogo, { offset }),
3281
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: SPLASH_WIDTH, marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
3282
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.text, bold: true, children: BRAND }),
3283
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: COLORS.textDim, children: [
3976
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Box_default, { flexDirection: "column", width: size.columns, height: size.rows, alignItems: "center", children: [
3977
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { height: verticalPad, flexShrink: 0 }),
3978
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Box_default, { flexDirection: "column", alignItems: "flex-start", flexShrink: 0, children: [
3979
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SplashLogo, { offset }),
3980
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { width: SPLASH_WIDTH, marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Text, { children: [
3981
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { color: COLORS.text, bold: true, children: BRAND }),
3982
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Text, { color: COLORS.textDim, children: [
3284
3983
  " v",
3285
3984
  VERSION
3286
3985
  ] }),
3287
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.textDim, children: " \xB7 By " }),
3288
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.text, bold: true, children: AUTHOR })
3986
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { color: COLORS.textDim, children: " \xB7 By " }),
3987
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { color: COLORS.text, bold: true, children: AUTHOR })
3289
3988
  ] }) }),
3290
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: SPLASH_WIDTH, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.textDim, children: caption ?? "Spinning up the orchestrator\u2026" }) })
3989
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { width: SPLASH_WIDTH, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { color: COLORS.textDim, children: caption ?? "Spinning up the orchestrator\u2026" }) })
3291
3990
  ] })
3292
3991
  ] });
3293
3992
  }
3294
3993
  function showSplash(opts) {
3295
3994
  const start = Date.now();
3296
3995
  void playSplashAudio();
3297
- const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SplashScreen, { caption: opts.caption }));
3996
+ const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SplashScreen, { caption: opts.caption }));
3298
3997
  const audioDurationMs = getSplashAudioDurationMs();
3299
3998
  const defaultMinMs = audioDurationMs + 200;
3300
3999
  return {
@@ -3353,12 +4052,12 @@ function parseArgs(argv) {
3353
4052
  return args;
3354
4053
  }
3355
4054
  function printHelpAndExit() {
3356
- const c = (color, text) => source_default.hex(color)(text);
4055
+ const c = (color2, text) => source_default.hex(color2)(text);
3357
4056
  process.stdout.write(
3358
4057
  "\n" + c(COLORS.primary, "GG Boss") + c(COLORS.textDim, " \u2014 orchestrator that drives multiple ggcoder workers from one chat.\n\n") + c(COLORS.text, "Usage\n") + " " + c(COLORS.accent, "ggboss") + c(
3359
4058
  COLORS.textDim,
3360
4059
  " start orchestrator using linked projects\n"
3361
- ) + " " + c(COLORS.accent, "ggboss link") + c(COLORS.textDim, " pick which projects to link (interactive)\n") + " " + c(COLORS.accent, "ggboss telegram") + c(COLORS.textDim, " configure Telegram bot integration\n") + " " + c(COLORS.accent, "ggboss serve") + c(COLORS.textDim, " run the boss over Telegram (no TUI)\n") + " " + c(COLORS.accent, "ggboss continue") + c(COLORS.textDim, " resume the most recent boss session\n") + " " + c(COLORS.accent, "ggboss --resume <id>") + c(COLORS.textDim, " resume a specific boss session\n") + " " + c(COLORS.accent, "ggboss --project <spec> [...]") + c(COLORS.textDim, " override links with explicit project(s)\n\n") + c(COLORS.text, "Options\n") + " " + c(COLORS.primary, "--project, -p <spec>") + c(COLORS.textDim, ' project to manage. spec is "cwd" or "name=cwd". repeatable.\n') + " " + c(COLORS.primary, "--boss-model <id>") + c(COLORS.textDim, " model for the orchestrator (default: claude-opus-4-7)\n") + " " + c(COLORS.primary, "--worker-model <id>") + c(COLORS.textDim, " model for workers (default: claude-sonnet-4-6)\n") + " " + c(COLORS.primary, "--help, -h") + c(COLORS.textDim, " show this help\n\n") + c(COLORS.textDim, "Talk to the boss at the prompt. Press ") + c(COLORS.accent, "Ctrl+C") + c(COLORS.textDim, " twice to exit.\n\n")
4060
+ ) + " " + c(COLORS.accent, "ggboss link") + c(COLORS.textDim, " pick which projects to link (interactive)\n") + " " + c(COLORS.accent, "ggboss telegram") + c(COLORS.textDim, " configure Telegram bot integration\n") + " " + c(COLORS.accent, "ggboss serve") + c(COLORS.textDim, " run the boss over Telegram (no TUI)\n") + " " + c(COLORS.accent, "ggboss continue") + c(COLORS.textDim, " resume the most recent boss session\n") + " " + c(COLORS.accent, "ggboss --resume <id>") + c(COLORS.textDim, " resume a specific boss session\n") + " " + c(COLORS.accent, "ggboss --project <spec> [...]") + c(COLORS.textDim, " override links with explicit project(s)\n\n") + c(COLORS.text, "Options\n") + " " + c(COLORS.primary, "--project, -p <spec>") + c(COLORS.textDim, ' project to manage. spec is "cwd" or "name=cwd". repeatable.\n') + " " + c(COLORS.primary, "--boss-model <id>") + c(COLORS.textDim, " model for the orchestrator (default: claude-opus-4-8)\n") + " " + c(COLORS.primary, "--worker-model <id>") + c(COLORS.textDim, " model for workers (default: claude-sonnet-4-6)\n") + " " + c(COLORS.primary, "--help, -h") + c(COLORS.textDim, " show this help\n\n") + c(COLORS.textDim, "Talk to the boss at the prompt. Press ") + c(COLORS.accent, "Ctrl+C") + c(COLORS.textDim, " twice to exit.\n\n")
3362
4061
  );
3363
4062
  process.exit(0);
3364
4063
  }
@@ -3394,7 +4093,7 @@ async function runServeSubcommand(argv) {
3394
4093
  }
3395
4094
  const settings = await loadSettings();
3396
4095
  const bossProvider = settings.bossProvider ?? "anthropic";
3397
- const bossModel = cliBossModel ?? settings.bossModel ?? "claude-opus-4-7";
4096
+ const bossModel = cliBossModel ?? settings.bossModel ?? "claude-opus-4-8";
3398
4097
  const workerProvider = settings.workerProvider ?? "anthropic";
3399
4098
  const workerModel = cliWorkerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
3400
4099
  await runBossServeMode({
@@ -3423,7 +4122,7 @@ async function runOrchestrator(args) {
3423
4122
  });
3424
4123
  const settings = await loadSettings();
3425
4124
  const finalBossProvider = args.bossProvider ?? settings.bossProvider ?? "anthropic";
3426
- const finalBossModel = args.bossModel ?? settings.bossModel ?? "claude-opus-4-7";
4125
+ const finalBossModel = args.bossModel ?? settings.bossModel ?? "claude-opus-4-8";
3427
4126
  const finalWorkerProvider = args.workerProvider ?? settings.workerProvider ?? "anthropic";
3428
4127
  const finalWorkerModel = args.workerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
3429
4128
  initLogger({