@wrongstack/tui 0.77.0 → 0.84.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
- import { Box, Text, render, useApp, useStdout, measureElement, Static, useInput, useStdin } from 'ink';
4
- import React6, { useState, useEffect, useReducer, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
3
+ import { Box, Text, render, useApp, useStdout, Static, useInput, useStdin } from 'ink';
4
+ import React6, { useState, useEffect, useReducer, useRef, useMemo, useCallback } from 'react';
5
5
  import * as fs2 from 'fs/promises';
6
6
  import * as path2 from 'path';
7
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
8
8
  import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
9
9
  import { readClipboardImage } from '@wrongstack/runtime/clipboard';
10
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
+ import { expectDefined as expectDefined$1 } from '@wrongstack/core/utils';
11
12
  import { spawn } from 'child_process';
12
13
 
13
14
  // src/run-tui.ts
@@ -34,6 +35,12 @@ var theme = Object.freeze({
34
35
  diffAddBg: "greenBright",
35
36
  diffDelBg: "redBright"
36
37
  });
38
+ function expectDefined(value) {
39
+ if (value === null || value === void 0) {
40
+ throw new Error("Expected value to be defined");
41
+ }
42
+ return value;
43
+ }
37
44
  var MODE_ICONS = {
38
45
  teach: "\u{1F9D1}\u200D\u{1F3EB}",
39
46
  brief: "\u26A1",
@@ -55,7 +62,7 @@ function modeIcon(label) {
55
62
  var COMPACT_THRESHOLD = 50;
56
63
  var COMFORTABLE_THRESHOLD = 90;
57
64
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
58
- var SPINNER_INTERVAL_MS = 130;
65
+ var SPINNER_INTERVAL_MS = 250;
59
66
  function StatusBar({
60
67
  model,
61
68
  version,
@@ -65,7 +72,7 @@ function StatusBar({
65
72
  queueCount = 0,
66
73
  yolo = false,
67
74
  autonomy,
68
- elapsedMs,
75
+ startedAt,
69
76
  todos,
70
77
  plan,
71
78
  fleet,
@@ -98,6 +105,12 @@ function StatusBar({
98
105
  const usage = tokenCounter?.total();
99
106
  const cost = tokenCounter?.estimateCost();
100
107
  const cache2 = tokenCounter?.cacheStats();
108
+ const [elapsedMs, setElapsedMs] = useState(startedAt ? Date.now() - startedAt : 0);
109
+ useEffect(() => {
110
+ if (startedAt == null) return;
111
+ const t = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
112
+ return () => clearInterval(t);
113
+ }, [startedAt]);
101
114
  const [spinnerIdx, setSpinnerIdx] = useState(0);
102
115
  useEffect(() => {
103
116
  if (state === "idle" || state === "aborting") return;
@@ -107,10 +120,11 @@ function StatusBar({
107
120
  );
108
121
  return () => clearInterval(t);
109
122
  }, [state]);
110
- const spinner = SPINNER_FRAMES[spinnerIdx];
123
+ const spinner = expectDefined(SPINNER_FRAMES[spinnerIdx]);
111
124
  const { label: stateLabel, color: stateColor } = stateChip(state, fleet?.running ?? 0);
112
125
  const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : spinner;
113
- const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel;
126
+ const thinking = state === "running" || state === "streaming";
127
+ const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel;
114
128
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
115
129
  const hasBrainActivity = !!brain && brain.state !== "idle";
116
130
  const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity;
@@ -128,7 +142,7 @@ function StatusBar({
128
142
  /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: isCompact ? (
129
143
  // Ultra-compact: state · model
130
144
  /* @__PURE__ */ jsxs(Fragment, { children: [
131
- /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
145
+ thinking ? /* @__PURE__ */ jsx(WaveText, { text: `${statePrefix}${stateLabel}`, phase: spinnerIdx }) : /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
132
146
  statePrefix,
133
147
  stateLabel
134
148
  ] }),
@@ -148,7 +162,7 @@ function StatusBar({
148
162
  ] }),
149
163
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" })
150
164
  ] }) : null,
151
- /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
165
+ thinking ? /* @__PURE__ */ jsx(WaveText, { text: `${statePrefix} ${stateLabel}`, phase: spinnerIdx }) : /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
152
166
  statePrefix,
153
167
  " ",
154
168
  stateLabel
@@ -205,7 +219,7 @@ function StatusBar({
205
219
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
206
220
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint })
207
221
  ] }) : null,
208
- indexState && indexState.indexing ? /* @__PURE__ */ jsxs(Fragment, { children: [
222
+ indexState?.indexing ? /* @__PURE__ */ jsxs(Fragment, { children: [
209
223
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
210
224
  /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
211
225
  "\u2699 indexing ",
@@ -244,14 +258,14 @@ function StatusBar({
244
258
  ] })
245
259
  ] }) : null,
246
260
  projectName ? /* @__PURE__ */ jsxs(Fragment, { children: [
247
- yolo || elapsedMs !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
261
+ yolo || startedAt != null ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
248
262
  /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
249
263
  "\u{1F4C1} ",
250
264
  projectName
251
265
  ] })
252
266
  ] }) : null,
253
267
  goalSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
254
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
268
+ yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
255
269
  /* @__PURE__ */ jsxs(
256
270
  Text,
257
271
  {
@@ -271,11 +285,11 @@ function StatusBar({
271
285
  )
272
286
  ] }) : null,
273
287
  modeLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
274
- yolo || autonomy && autonomy !== "off" || eternalStage || elapsedMs !== void 0 || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
288
+ yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
275
289
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: modeIcon(modeLabel) })
276
290
  ] }) : null,
277
291
  git ? /* @__PURE__ */ jsxs(Fragment, { children: [
278
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
292
+ yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
279
293
  /* @__PURE__ */ jsxs(Text, { children: [
280
294
  /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
281
295
  "\u2387 ",
@@ -375,7 +389,7 @@ function StatusBar({
375
389
  /* @__PURE__ */ jsxs(Text, { children: [
376
390
  /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
377
391
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
378
- /* @__PURE__ */ jsx(Text, { color: a.running ? "yellow" : void 0, dimColor: !a.running, children: a.running ? "\u25B6" : "\xB7" }),
392
+ /* @__PURE__ */ jsx(Text, { dimColor: !a.running, ...a.running ? { color: "yellow" } : {}, children: a.running ? "\u25B6" : "\xB7" }),
379
393
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
380
394
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed(a.elapsedMs) }),
381
395
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
@@ -504,6 +518,26 @@ function stateChip(state, fleetRunning) {
504
518
  if (state === "aborting") return { label: "aborting\u2026", color: "yellow" };
505
519
  return { label: "thinking\u2026", color: "green" };
506
520
  }
521
+ var WAVE_COLORS = [
522
+ "#ff5f5f",
523
+ "#ff8f3f",
524
+ "#ffd23f",
525
+ "#bce84a",
526
+ "#6bcb77",
527
+ "#3dd9c0",
528
+ "#3fb6ff",
529
+ "#5f8bff",
530
+ "#845ef7",
531
+ "#b15bff",
532
+ "#f06595",
533
+ "#ff5fa2"
534
+ ];
535
+ function WaveText({ text, phase }) {
536
+ return /* @__PURE__ */ jsx(Text, { bold: true, children: Array.from(text).map((ch, i) => (
537
+ // biome-ignore lint/suspicious/noArrayIndexKey: glyph order is positional and re-rendered each tick
538
+ /* @__PURE__ */ jsx(Text, { color: WAVE_COLORS[(i + phase) % WAVE_COLORS.length], children: ch }, i)
539
+ )) });
540
+ }
507
541
  var FILLED = "\u2588";
508
542
  var EMPTY = "\u2591";
509
543
  function renderProgress(ratio, width) {
@@ -565,7 +599,7 @@ function bucketActivity(recentTools, now, bins = 12, binMs = 2e3) {
565
599
  let idx = Math.floor((t.at - windowStart) / binMs);
566
600
  if (idx < 0) idx = 0;
567
601
  if (idx >= bins) idx = bins - 1;
568
- out[idx]++;
602
+ out[idx] = (out[idx] ?? 0) + 1;
569
603
  }
570
604
  return out;
571
605
  }
@@ -694,7 +728,7 @@ function FleetMonitor({
694
728
  // biome-ignore lint/suspicious/noArrayIndexKey: timeline is rebuilt per render
695
729
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
696
730
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${fmtElapsed(Math.max(0, nowTick - ev.at))} ago`.padEnd(10) }),
697
- /* @__PURE__ */ jsx(Text, { color: ev.color, children: ev.icon }),
731
+ /* @__PURE__ */ jsx(Text, { ...ev.color ? { color: ev.color } : {}, children: ev.icon }),
698
732
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: ev.text })
699
733
  ] }, i)
700
734
  )) }) : null
@@ -1021,8 +1055,8 @@ function AutonomyPicker({
1021
1055
  options.map((opt, i) => /* @__PURE__ */ jsxs(
1022
1056
  Text,
1023
1057
  {
1024
- color: i === selected ? opt.color : void 0,
1025
1058
  inverse: i === selected,
1059
+ ...i === selected ? { color: opt.color } : {},
1026
1060
  children: [
1027
1061
  i === selected ? "\u203A " : " ",
1028
1062
  /* @__PURE__ */ jsx(Text, { bold: true, children: opt.label.padEnd(12) }),
@@ -1147,8 +1181,8 @@ function CheckpointTimeline({
1147
1181
  const isSelected = i === selected;
1148
1182
  const label = `[${cp.promptIndex}] ${cp.promptPreview}`;
1149
1183
  return /* @__PURE__ */ jsxs(Box, { children: [
1150
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : void 0, bold: isSelected, children: isSelected ? "\u25B8 " : " " }),
1151
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : void 0, bold: isSelected, children: label }),
1184
+ /* @__PURE__ */ jsx(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: isSelected ? "\u25B8 " : " " }),
1185
+ /* @__PURE__ */ jsx(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: label }),
1152
1186
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1153
1187
  " ",
1154
1188
  new Date(cp.ts).toLocaleTimeString()
@@ -1193,7 +1227,7 @@ function hasDiff(input) {
1193
1227
  }
1194
1228
  function renderDiffLine(line) {
1195
1229
  const prefix = line.startsWith("+") ? "green" : line.startsWith("-") ? "red" : line.startsWith("@@") ? "cyan" : void 0;
1196
- return /* @__PURE__ */ jsxs(Text, { color: prefix, children: [
1230
+ return /* @__PURE__ */ jsxs(Text, { ...prefix ? { color: prefix } : {}, children: [
1197
1231
  line,
1198
1232
  "\n"
1199
1233
  ] }, line);
@@ -1318,7 +1352,7 @@ function FilePicker({ query, matches, selected }) {
1318
1352
  query || "\u2026",
1319
1353
  " \u2014 \u2191/\u2193 select, Enter attach, Esc cancel"
1320
1354
  ] }),
1321
- matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
1355
+ matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
1322
1356
  i === selected ? "\u203A " : " ",
1323
1357
  highlight(m)
1324
1358
  ] }, m))
@@ -1392,9 +1426,8 @@ function FleetPanel({
1392
1426
  ] }) : null
1393
1427
  ] });
1394
1428
  }
1395
- function helpSections(opts) {
1429
+ function helpSections() {
1396
1430
  const nav = [];
1397
- if (opts.managed) nav.push({ keys: "PgUp/PgDn", desc: "scroll chat history" });
1398
1431
  nav.push(
1399
1432
  { keys: "\u2191/\u2193", desc: "previous / next input (empty prompt)" },
1400
1433
  { keys: "?", desc: "open this help (empty prompt)" }
@@ -1437,10 +1470,8 @@ function helpSections(opts) {
1437
1470
  }
1438
1471
  ];
1439
1472
  }
1440
- function HelpOverlay({
1441
- managed
1442
- }) {
1443
- const sections = helpSections({ managed });
1473
+ function HelpOverlay() {
1474
+ const sections = helpSections();
1444
1475
  const keyWidth = Math.max(...sections.flatMap((s2) => s2.entries.map((e) => e.keys.length)), 0);
1445
1476
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1446
1477
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
@@ -1903,8 +1934,6 @@ function detectLang(fenceInfo) {
1903
1934
  const tag = fenceInfo.trim().toLowerCase().split(/\s+/)[0] ?? "";
1904
1935
  return LANG_ALIASES[tag] ?? "plain";
1905
1936
  }
1906
-
1907
- // src/markdown-table.ts
1908
1937
  var ROW_RE = /^\s*\|.*\|\s*$/;
1909
1938
  var SEP_RE = /^\s*\|[\s\-:|]+\|\s*$/;
1910
1939
  function detectTable(lines, start) {
@@ -1988,14 +2017,14 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
1988
2017
  const stripped = stripInlineMarkers(cell);
1989
2018
  const w = longestWord(stripped);
1990
2019
  const total = strWidth(stripped);
1991
- natural[c] = Math.max(natural[c], w, total);
2020
+ natural[c] = Math.max(expectDefined$1(natural[c]), w, total);
1992
2021
  }
1993
2022
  }
1994
2023
  if (sepWidths) {
1995
2024
  for (let c = 0; c < cols && c < sepWidths.length; c++) {
1996
2025
  const sepW = sepWidths[c];
1997
2026
  if (sepW != null) {
1998
- natural[c] = Math.max(natural[c], sepW);
2027
+ natural[c] = Math.max(expectDefined$1(natural[c]), sepW);
1999
2028
  }
2000
2029
  }
2001
2030
  }
@@ -2007,14 +2036,14 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
2007
2036
  let maxIdx = -1;
2008
2037
  let maxVal = MIN_COL_WIDTH;
2009
2038
  for (let i = 0; i < cols; i++) {
2010
- const w = widths[i];
2039
+ const w = expectDefined$1(widths[i]);
2011
2040
  if (w > maxVal) {
2012
2041
  maxVal = w;
2013
2042
  maxIdx = i;
2014
2043
  }
2015
2044
  }
2016
2045
  if (maxIdx < 0) break;
2017
- widths[maxIdx]--;
2046
+ widths[maxIdx] = (widths[maxIdx] ?? 0) - 1;
2018
2047
  sum--;
2019
2048
  }
2020
2049
  return widths;
@@ -2067,7 +2096,7 @@ function strWidth(s2) {
2067
2096
  if (i < len) i++;
2068
2097
  continue;
2069
2098
  }
2070
- const code = s2.codePointAt(i);
2099
+ const code = expectDefined$1(s2.codePointAt(i));
2071
2100
  const cpLen = code > 65535 ? 2 : 1;
2072
2101
  if (code === 8205 || // ZWJ — Zero Width Joiner (emoji sequences)
2073
2102
  code === 8203 || // ZWSP — Zero Width Space
@@ -2098,7 +2127,6 @@ function strWidth(s2) {
2098
2127
  code >= 8960 && code <= 9215 || // Miscellaneous Technical
2099
2128
  code >= 11088 && code <= 11093 || // Stars and similar
2100
2129
  code >= 10548 && code <= 10549 || // Arrow forms
2101
- code >= 8592 && code <= 8703 || // Arrows
2102
2130
  code >= 9632 && code <= 9727 || // Geometric Shapes
2103
2131
  code >= 9664 && code <= 9726 || // More Geometric Shapes (includes ▶)
2104
2132
  code >= 9984 && code <= 10175) {
@@ -2310,10 +2338,10 @@ function InlineLine({ tokens, dim }) {
2310
2338
  Text,
2311
2339
  {
2312
2340
  color: t.code ? theme.accent : "white",
2313
- bold: t.bold,
2314
- italic: t.italic,
2315
- strikethrough: t.strike,
2316
- dimColor: dim,
2341
+ bold: Boolean(t.bold),
2342
+ italic: Boolean(t.italic),
2343
+ strikethrough: Boolean(t.strike),
2344
+ dimColor: Boolean(dim),
2317
2345
  children: t.text
2318
2346
  },
2319
2347
  j
@@ -2938,7 +2966,22 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
2938
2966
  }
2939
2967
  var MAX_STREAM_DISPLAY_CHARS = 480;
2940
2968
  var MAX_STREAM_LINES = 8;
2941
- var ToolStreamBox = React6.memo(function ToolStreamBox2({
2969
+ function streamBoxRows(text, maxLines, contentWidth) {
2970
+ const trunc = (line) => line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
2971
+ const lines = text.split("\n");
2972
+ const totalLines = lines.length;
2973
+ const hidden = Math.max(0, totalLines - maxLines);
2974
+ const rows = [];
2975
+ if (hidden > 0) {
2976
+ rows.push({ text: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above`, italic: true });
2977
+ for (const line of lines.slice(totalLines - (maxLines - 1))) rows.push({ text: trunc(line) });
2978
+ } else {
2979
+ for (let i = 0; i < maxLines - totalLines; i++) rows.push({ text: "" });
2980
+ for (const line of lines) rows.push({ text: trunc(line) });
2981
+ }
2982
+ return rows;
2983
+ }
2984
+ React6.memo(function ToolStreamBox2({
2942
2985
  name,
2943
2986
  text,
2944
2987
  startedAt,
@@ -2950,11 +2993,10 @@ var ToolStreamBox = React6.memo(function ToolStreamBox2({
2950
2993
  return () => clearInterval(t);
2951
2994
  }, []);
2952
2995
  const elapsedMs = Date.now() - startedAt;
2953
- const lines = text.split("\n");
2954
- const totalLines = lines.length;
2996
+ const totalLines = text.split("\n").length;
2955
2997
  const hidden = Math.max(0, totalLines - MAX_STREAM_LINES);
2956
- const visible = hidden > 0 ? lines.slice(hidden) : lines;
2957
2998
  const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
2999
+ const rows = streamBoxRows(text, MAX_STREAM_LINES, contentWidth);
2958
3000
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0, children: [
2959
3001
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
2960
3002
  /* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u25C6 " }),
@@ -2962,13 +3004,10 @@ var ToolStreamBox = React6.memo(function ToolStreamBox2({
2962
3004
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration(elapsedMs)}` }),
2963
3005
  hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
2964
3006
  ] }),
2965
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
2966
- hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above` }) : null,
2967
- visible.map((line, i) => {
2968
- const trimmed = line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
2969
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: trimmed || " " }, i);
2970
- })
2971
- ] })
3007
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 2, children: rows.map((r, i) => (
3008
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-height block, index is the row
3009
+ /* @__PURE__ */ jsx(Text, { dimColor: true, italic: Boolean(r.italic), children: r.text || " " }, i)
3010
+ )) })
2972
3011
  ] });
2973
3012
  });
2974
3013
  function tailForDisplay(text, maxChars) {
@@ -3014,9 +3053,15 @@ function CodeBlock({
3014
3053
  // biome-ignore lint/suspicious/noArrayIndexKey: code lines are positional
3015
3054
  /* @__PURE__ */ jsxs(Text, { children: [
3016
3055
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${String(i + 1).padStart(gutterW, " ")} ` }),
3017
- tokens.length === 0 ? " " : tokens.map((t, j) => (
3018
- // biome-ignore lint/suspicious/noArrayIndexKey: token order is stable per line
3019
- /* @__PURE__ */ jsx(Text, { color: t.color, dimColor: t.dim, bold: t.bold, children: t.text }, j)
3056
+ tokens.length === 0 ? " " : tokens.map((t, j) => /* @__PURE__ */ jsx(
3057
+ Text,
3058
+ {
3059
+ dimColor: Boolean(t.dim),
3060
+ bold: Boolean(t.bold),
3061
+ ...t.color ? { color: t.color } : {},
3062
+ children: t.text
3063
+ },
3064
+ j
3020
3065
  ))
3021
3066
  ] }, i)
3022
3067
  )),
@@ -3170,7 +3215,22 @@ function AssistantBody({
3170
3215
  )
3171
3216
  ) });
3172
3217
  }
3173
- function AssistantTail({ text }) {
3218
+ var ASSISTANT_TAIL_LINES = 8;
3219
+ function assistantTailRows(text, tailLines, contentWidth) {
3220
+ const tail = text.split("\n").slice(-tailLines);
3221
+ const rows = [];
3222
+ for (let i = 0; i < tailLines - tail.length; i++) rows.push("");
3223
+ for (const line of tail) {
3224
+ rows.push(line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line);
3225
+ }
3226
+ return rows;
3227
+ }
3228
+ function AssistantTail({
3229
+ text,
3230
+ termWidth
3231
+ }) {
3232
+ const contentWidth = Math.max(20, termWidth - 3);
3233
+ const rows = assistantTailRows(text, ASSISTANT_TAIL_LINES, contentWidth);
3174
3234
  return /* @__PURE__ */ jsxs(
3175
3235
  Box,
3176
3236
  {
@@ -3187,7 +3247,10 @@ function AssistantTail({ text }) {
3187
3247
  /* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }),
3188
3248
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (streaming\u2026)" })
3189
3249
  ] }),
3190
- /* @__PURE__ */ jsx(Text, { color: "white", children: text })
3250
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: rows.map((r, i) => (
3251
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-height block, index is the row
3252
+ /* @__PURE__ */ jsx(Text, { color: "white", children: r || " " }, i)
3253
+ )) })
3191
3254
  ]
3192
3255
  }
3193
3256
  );
@@ -3350,8 +3413,8 @@ var Entry = React6.memo(function Entry2({
3350
3413
  /* @__PURE__ */ jsx(
3351
3414
  Text,
3352
3415
  {
3353
- color: !entry.ok || line.startsWith("!") ? "red" : void 0,
3354
3416
  dimColor: entry.ok && !line.startsWith("!"),
3417
+ ...!entry.ok || line.startsWith("!") ? { color: "red" } : {},
3355
3418
  children: line
3356
3419
  }
3357
3420
  )
@@ -3484,19 +3547,9 @@ function History({ entries, streamingText, toolStream }) {
3484
3547
  }, [stdout]);
3485
3548
  const termWidth = termSize.columns;
3486
3549
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
3487
- const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
3488
3550
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3489
3551
  /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
3490
- tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail }) : null,
3491
- toolTail ? /* @__PURE__ */ jsx(
3492
- ToolStreamBox,
3493
- {
3494
- name: toolStream.name,
3495
- text: toolTail,
3496
- startedAt: toolStream.startedAt,
3497
- termWidth
3498
- }
3499
- ) : null
3552
+ tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null
3500
3553
  ] });
3501
3554
  }
3502
3555
 
@@ -3602,6 +3655,12 @@ function layoutInputRows(prompt, value, cursor, width) {
3602
3655
  if (row.length > 0 || rows.length === 0) rows.push(row);
3603
3656
  return rows;
3604
3657
  }
3658
+ function expectDefined3(value) {
3659
+ if (value === null || value === void 0) {
3660
+ throw new Error("Expected value to be defined");
3661
+ }
3662
+ return value;
3663
+ }
3605
3664
  function renderRow2(cells, rowKey, promptColor) {
3606
3665
  const out = [];
3607
3666
  let run = "";
@@ -3655,6 +3714,14 @@ function isBackspaceOrDelete(data) {
3655
3714
  if (data === "\x1B[3~") return "delete";
3656
3715
  return null;
3657
3716
  }
3717
+ function parseMouseWheel(data) {
3718
+ const m = data.match(new RegExp(`^${String.fromCharCode(27)}\\[<(\\d+);(\\d+);(\\d+)([Mm])$`, "u"));
3719
+ if (!m) return null;
3720
+ const cb = Number.parseInt(expectDefined3(m[1]), 10);
3721
+ if (cb === 64) return 1;
3722
+ if (cb === 65) return -1;
3723
+ return null;
3724
+ }
3658
3725
  var EMPTY_KEY = {
3659
3726
  upArrow: false,
3660
3727
  downArrow: false,
@@ -3708,6 +3775,11 @@ function Input({
3708
3775
  onKey("", { ...EMPTY_KEY, delete: true });
3709
3776
  return;
3710
3777
  }
3778
+ const wheelDelta = parseMouseWheel(s2);
3779
+ if (wheelDelta !== null) {
3780
+ onKey("", { ...EMPTY_KEY, wheelDeltaY: wheelDelta });
3781
+ return;
3782
+ }
3711
3783
  const fn = fnKey(s2);
3712
3784
  if (fn !== null) onKey("", { ...EMPTY_KEY, fn });
3713
3785
  };
@@ -3742,46 +3814,6 @@ function Input({
3742
3814
  hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
3743
3815
  ] });
3744
3816
  }
3745
- function hintsFor(ctx) {
3746
- if (ctx.confirm) {
3747
- return [
3748
- { key: "y", label: "yes" },
3749
- { key: "n", label: "no" },
3750
- { key: "a", label: "always" },
3751
- { key: "d", label: "deny" }
3752
- ];
3753
- }
3754
- if (ctx.picker) {
3755
- return [
3756
- { key: "\u2191\u2193", label: "move" },
3757
- { key: "\u21B5", label: "select" },
3758
- { key: "Esc", label: "cancel" }
3759
- ];
3760
- }
3761
- if (ctx.monitor) {
3762
- return [
3763
- { key: "Esc", label: "close" },
3764
- { key: "^F", label: "fleet" },
3765
- { key: "^G", label: "agents" },
3766
- { key: "^T", label: "worktrees" },
3767
- { key: "F6", label: "todos" }
3768
- ];
3769
- }
3770
- const base = [{ key: "?", label: "help" }];
3771
- if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" }, { key: "F5", label: "Settings" });
3772
- base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
3773
- return base;
3774
- }
3775
- function KeyHintBar({ context }) {
3776
- const hints = hintsFor(context);
3777
- return /* @__PURE__ */ jsx(Box, { flexDirection: "row", paddingX: 1, children: hints.map((h, i) => (
3778
- // biome-ignore lint/suspicious/noArrayIndexKey: hints are positional + stable
3779
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginRight: 2, children: [
3780
- /* @__PURE__ */ jsx(Text, { color: theme.accent, children: h.key }),
3781
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${h.label}` })
3782
- ] }, i)
3783
- )) });
3784
- }
3785
3817
  function fmtElapsed2(ms) {
3786
3818
  if (ms < 1e3) return `${ms}ms`;
3787
3819
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
@@ -3816,7 +3848,7 @@ var LiveActivityStrip = React6.memo(function LiveActivityStrip2({
3816
3848
  const running = Object.values(entries).filter((e) => e.status === "running").sort((a, b) => a.startedAt - b.startedAt).slice(0, maxRows);
3817
3849
  if (running.length === 0) {
3818
3850
  if (Object.keys(entries).length === 0) return null;
3819
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: Array.from({ length: maxRows }, (_, i) => /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }, `empty-${i}`)) });
3851
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: Array.from({ length: maxRows }, (_, slot) => slot).map((slot) => /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }, `empty-${slot}`)) });
3820
3852
  }
3821
3853
  const now = Date.now();
3822
3854
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
@@ -3890,7 +3922,7 @@ function ModelPicker({
3890
3922
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3891
3923
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch model \u2014 Step 1/2: Pick provider \u2501\u2501" }),
3892
3924
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
3893
- providerOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : providerOptions.map((p, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
3925
+ providerOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : providerOptions.map((p, i) => /* @__PURE__ */ jsxs(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
3894
3926
  i === selected ? "\u203A " : " ",
3895
3927
  /* @__PURE__ */ jsx(Text, { bold: true, children: p.id.padEnd(28) }),
3896
3928
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -3933,8 +3965,8 @@ function ModelPicker({
3933
3965
  return /* @__PURE__ */ jsxs(
3934
3966
  Text,
3935
3967
  {
3936
- color: absoluteIndex === selected ? "cyan" : void 0,
3937
3968
  inverse: absoluteIndex === selected,
3969
+ ...absoluteIndex === selected ? { color: "cyan" } : {},
3938
3970
  children: [
3939
3971
  absoluteIndex === selected ? "\u203A " : " ",
3940
3972
  id
@@ -4173,102 +4205,6 @@ function QueuePanel({ items }) {
4173
4205
  ] })
4174
4206
  ] });
4175
4207
  }
4176
- var MAX_MOUNTED = 500;
4177
- function scrollbarThumb(rows, offset, total) {
4178
- const scrollable = total > rows;
4179
- if (!scrollable) return { top: 0, size: rows, scrollable: false };
4180
- const windowTop = Math.max(0, total - rows - offset);
4181
- const size = Math.max(1, Math.round(rows / total * rows));
4182
- const rawTop = Math.round(windowTop / total * rows);
4183
- const top = Math.max(0, Math.min(rawTop, rows - size));
4184
- return { top, size, scrollable: true };
4185
- }
4186
- function Scrollbar({
4187
- rows,
4188
- offset,
4189
- total
4190
- }) {
4191
- const { top: thumbTop, size: thumbSize, scrollable } = scrollbarThumb(rows, offset, total);
4192
- const cells = [];
4193
- for (let i = 0; i < rows; i++) {
4194
- cells.push(i >= thumbTop && i < thumbTop + thumbSize ? "\u2588" : "\u2502");
4195
- }
4196
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 1, flexShrink: 0, children: cells.map((c, i) => /* @__PURE__ */ jsx(
4197
- Text,
4198
- {
4199
- color: scrollable ? theme.accent : void 0,
4200
- dimColor: !scrollable || c === "\u2502",
4201
- children: c
4202
- },
4203
- i
4204
- )) });
4205
- }
4206
- function ScrollableHistory({
4207
- entries,
4208
- streamingText,
4209
- toolStream,
4210
- scrollOffset,
4211
- viewportRows,
4212
- totalLines,
4213
- onMeasure,
4214
- maxWidth
4215
- }) {
4216
- const { stdout } = useStdout();
4217
- const rawWidth = stdout?.columns ?? 80;
4218
- const termWidth = maxWidth ? Math.min(rawWidth, maxWidth) : rawWidth;
4219
- const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
4220
- const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
4221
- const hiddenCount = Math.max(0, entries.length - MAX_MOUNTED);
4222
- const shown = hiddenCount > 0 ? entries.slice(-MAX_MOUNTED) : entries;
4223
- const contentRef = useRef(null);
4224
- const lastReported = useRef(-1);
4225
- useLayoutEffect(() => {
4226
- const node = contentRef.current;
4227
- if (!node) return;
4228
- const { height } = measureElement(node);
4229
- if (height !== lastReported.current) {
4230
- lastReported.current = height;
4231
- onMeasure(height);
4232
- }
4233
- }, [onMeasure]);
4234
- const vp = Math.max(1, viewportRows);
4235
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
4236
- /* @__PURE__ */ jsx(
4237
- Box,
4238
- {
4239
- flexDirection: "column",
4240
- flexGrow: 1,
4241
- height: vp,
4242
- overflowY: "hidden",
4243
- justifyContent: "flex-end",
4244
- children: /* @__PURE__ */ jsxs(
4245
- Box,
4246
- {
4247
- ref: contentRef,
4248
- flexDirection: "column",
4249
- marginBottom: Math.max(0, scrollOffset),
4250
- flexShrink: 0,
4251
- children: [
4252
- hiddenCount > 0 ? /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2191 ${hiddenCount} earlier ${hiddenCount === 1 ? "entry" : "entries"} (scroll lives in this session; full log on disk)` }) }) : null,
4253
- shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id)),
4254
- tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail }) : null,
4255
- toolTail ? /* @__PURE__ */ jsx(
4256
- ToolStreamBox,
4257
- {
4258
- name: toolStream.name,
4259
- text: toolTail,
4260
- startedAt: toolStream.startedAt,
4261
- termWidth
4262
- }
4263
- ) : null
4264
- ]
4265
- }
4266
- )
4267
- }
4268
- ),
4269
- /* @__PURE__ */ jsx(Scrollbar, { rows: vp, offset: Math.max(0, scrollOffset), total: totalLines })
4270
- ] });
4271
- }
4272
4208
  var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
4273
4209
  var SETTINGS_MODES = ["off", "suggest", "auto"];
4274
4210
  var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
@@ -4425,7 +4361,7 @@ function SettingsPicker({
4425
4361
  ];
4426
4362
  const fieldRowIndex = [];
4427
4363
  for (let i = 0; i < rows.length; i++) {
4428
- if (!rows[i].section) fieldRowIndex.push(i);
4364
+ if (!rows[i]?.section) fieldRowIndex.push(i);
4429
4365
  }
4430
4366
  const VISIBLE_FIELDS = 8;
4431
4367
  const totalFields = fieldRowIndex.length;
@@ -4436,7 +4372,7 @@ function SettingsPicker({
4436
4372
  const sectionFields = [];
4437
4373
  let curHeader = -1;
4438
4374
  for (let i = 0; i < rows.length; i++) {
4439
- if (rows[i].section) curHeader = i;
4375
+ if (rows[i]?.section) curHeader = i;
4440
4376
  else if (curHeader >= 0) {
4441
4377
  const fieldIdx = fieldRowIndex.indexOf(i);
4442
4378
  if (fieldIdx === -1) continue;
@@ -4465,18 +4401,18 @@ function SettingsPicker({
4465
4401
  "\u2500\u2500 ",
4466
4402
  row.section,
4467
4403
  " \u2500\u2500"
4468
- ] }, `section-${i}`);
4404
+ ] }, `section-${row.section ?? i}`);
4469
4405
  }
4470
4406
  return null;
4471
4407
  }
4472
4408
  if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
4473
4409
  const selected = fieldAtRow === field;
4474
- return /* @__PURE__ */ jsxs(Text, { color: selected ? "yellow" : void 0, inverse: selected, children: [
4410
+ return /* @__PURE__ */ jsxs(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
4475
4411
  selected ? "\u203A " : " ",
4476
4412
  /* @__PURE__ */ jsx(Text, { bold: true, children: (row.label ?? "").padEnd(26) }),
4477
4413
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
4478
4414
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail ?? "" })
4479
- ] }, `row-${i}`);
4415
+ ] }, `row-${row.label ?? fieldAtRow}`);
4480
4416
  }),
4481
4417
  hasBelow ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below` }) : null,
4482
4418
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Persisted to ~/.wrongstack/config.json" }),
@@ -4485,31 +4421,98 @@ function SettingsPicker({
4485
4421
  }
4486
4422
  function SlashMenu({ query, matches, selected }) {
4487
4423
  const placeholder = query ? `/${query}` : "/";
4424
+ const { stdout } = useStdout();
4425
+ const termRows = stdout?.rows ?? 24;
4426
+ const rows = [];
4427
+ let lastCategory = "";
4428
+ for (let i = 0; i < matches.length; i++) {
4429
+ const m = matches[i];
4430
+ if (m.category !== lastCategory) {
4431
+ lastCategory = m.category;
4432
+ rows.push({ type: "header", category: m.category });
4433
+ }
4434
+ rows.push({ type: "item", match: m, index: i });
4435
+ }
4436
+ const overhead = 1 + 2 + 2 + 2 + 6;
4437
+ const maxBodyRows = Math.max(4, termRows - overhead);
4438
+ const selectedRowIdx = rows.findIndex((r) => r.type === "item" && r.index === selected);
4439
+ const visible = windowRows(rows, selectedRowIdx < 0 ? 0 : selectedRowIdx, maxBodyRows);
4440
+ const hiddenAbove = visible.start;
4441
+ const hiddenBelow = rows.length - visible.end;
4488
4442
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4489
4443
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4490
4444
  placeholder || "/",
4491
- " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close"
4445
+ " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close",
4446
+ matches.length > 0 ? ` (${selected + 1}/${matches.length})` : ""
4447
+ ] }),
4448
+ hiddenAbove > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4449
+ " ",
4450
+ "\u2191 ",
4451
+ hiddenAbove,
4452
+ " more"
4453
+ ] }),
4454
+ visible.contextHeader && /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", dimColor: true, children: [
4455
+ " ",
4456
+ visible.contextHeader
4457
+ ] }),
4458
+ visible.rows.map((row) => {
4459
+ if (row.type === "header") {
4460
+ return /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", dimColor: true, children: [
4461
+ " ",
4462
+ row.category
4463
+ ] }, `cat-${row.category}`);
4464
+ }
4465
+ const { match: m, index: i } = row;
4466
+ return /* @__PURE__ */ jsxs(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
4467
+ i === selected ? "\u203A " : " ",
4468
+ /* @__PURE__ */ jsx(Text, { bold: true, children: m.name }),
4469
+ m.argsHint ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4470
+ " ",
4471
+ m.argsHint
4472
+ ] }) : null,
4473
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4474
+ " \u2014 ",
4475
+ m.description
4476
+ ] })
4477
+ ] }, m.name);
4478
+ }),
4479
+ hiddenBelow > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4480
+ " ",
4481
+ "\u2193 ",
4482
+ hiddenBelow,
4483
+ " more"
4492
4484
  ] }),
4493
- matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
4494
- i === selected ? "\u203A " : " ",
4495
- /* @__PURE__ */ jsx(Text, { bold: true, children: m.name }),
4496
- m.argsHint ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4497
- " ",
4498
- m.argsHint
4499
- ] }) : null,
4500
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4501
- " \u2014 ",
4502
- m.description
4503
- ] })
4504
- ] }, m.name)),
4505
4485
  matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
4506
4486
  ] });
4507
4487
  }
4488
+ function windowRows(rows, focus, max) {
4489
+ if (rows.length <= max) {
4490
+ return { rows, start: 0, end: rows.length, contextHeader: null };
4491
+ }
4492
+ let start = focus - Math.floor(max / 2);
4493
+ if (start < 0) start = 0;
4494
+ let end = start + max;
4495
+ if (end > rows.length) {
4496
+ end = rows.length;
4497
+ start = end - max;
4498
+ }
4499
+ let contextHeader = null;
4500
+ if (start > 0) {
4501
+ const first = rows[start];
4502
+ if (first && first.type === "item") {
4503
+ for (let i = start - 1; i >= 0; i--) {
4504
+ const r = rows[i];
4505
+ if (r && r.type === "header") {
4506
+ contextHeader = r.category;
4507
+ break;
4508
+ }
4509
+ }
4510
+ }
4511
+ }
4512
+ return { rows: rows.slice(start, end), start, end, contextHeader };
4513
+ }
4508
4514
  function TodosMonitor({ todos }) {
4509
4515
  const { stdout } = useStdout();
4510
- useInput((_input, key) => {
4511
- if (key.escape) ;
4512
- });
4513
4516
  const done = todos.filter((t) => t.status === "completed").length;
4514
4517
  const inProgress = todos.filter((t) => t.status === "in_progress").length;
4515
4518
  const pending = todos.filter((t) => t.status === "pending").length;
@@ -4949,7 +4952,15 @@ function useBrainEvents(events, dispatch) {
4949
4952
  const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
4950
4953
  dispatch({ type: "brainStatus", state: status, source: p.request.source, risk: p.request.risk, summary: decision });
4951
4954
  if (status === "ask_human") {
4952
- dispatch({ type: "brainPromptSet", prompt: { requestId: p.request.id, source: p.request.source, risk: p.request.risk, question: p.request.question, context: p.request.context, options: p.request.options } });
4955
+ const prompt = {
4956
+ requestId: p.request.id,
4957
+ source: p.request.source,
4958
+ risk: p.request.risk,
4959
+ question: p.request.question
4960
+ };
4961
+ if (p.request.context !== void 0) prompt.context = p.request.context;
4962
+ if (p.request.options !== void 0) prompt.options = p.request.options;
4963
+ dispatch({ type: "brainPromptSet", prompt });
4953
4964
  } else {
4954
4965
  dispatch({ type: "brainPromptClear" });
4955
4966
  }
@@ -4969,6 +4980,12 @@ function useBrainEvents(events, dispatch) {
4969
4980
  };
4970
4981
  }, [events, dispatch]);
4971
4982
  }
4983
+ function expectDefined4(value) {
4984
+ if (value === null || value === void 0) {
4985
+ throw new Error("Expected value to be defined");
4986
+ }
4987
+ return value;
4988
+ }
4972
4989
  var STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
4973
4990
  function labelFor(labelsRef, id, name) {
4974
4991
  const m = labelsRef.current;
@@ -4977,7 +4994,7 @@ function labelFor(labelsRef, id, name) {
4977
4994
  const n = m.size + 1;
4978
4995
  const v = {
4979
4996
  label: name && name !== id ? name : `AGENT#${n}`,
4980
- color: STREAM_COLORS[(n - 1) % STREAM_COLORS.length]
4997
+ color: expectDefined4(STREAM_COLORS[(n - 1) % STREAM_COLORS.length])
4981
4998
  };
4982
4999
  m.set(id, v);
4983
5000
  return v;
@@ -5324,6 +5341,12 @@ function buildSteeringPreamble(snapshot, newDirection) {
5324
5341
  }
5325
5342
 
5326
5343
  // src/app-reducer.ts
5344
+ function expectDefined5(value) {
5345
+ if (value === null || value === void 0) {
5346
+ throw new Error("Expected value to be defined");
5347
+ }
5348
+ return value;
5349
+ }
5327
5350
  function reducer(state, action) {
5328
5351
  switch (action.type) {
5329
5352
  case "addEntry": {
@@ -5688,13 +5711,13 @@ function reducer(state, action) {
5688
5711
  const i = SETTINGS_MODES.indexOf(sp.mode);
5689
5712
  const base = i < 0 ? 0 : i;
5690
5713
  const next = (base + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
5691
- return { ...state, settingsPicker: { ...sp, mode: SETTINGS_MODES[next], hint: void 0 } };
5714
+ return { ...state, settingsPicker: { ...sp, mode: expectDefined5(SETTINGS_MODES[next]), hint: void 0 } };
5692
5715
  }
5693
5716
  if (f === 1) {
5694
5717
  const j = DELAY_PRESETS_MS.indexOf(sp.delayMs);
5695
5718
  const base = j < 0 ? 0 : j;
5696
5719
  const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
5697
- return { ...state, settingsPicker: { ...sp, delayMs: DELAY_PRESETS_MS[next], hint: void 0 } };
5720
+ return { ...state, settingsPicker: { ...sp, delayMs: expectDefined5(DELAY_PRESETS_MS[next]), hint: void 0 } };
5698
5721
  }
5699
5722
  if (f === 2) return { ...state, settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 } };
5700
5723
  if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
@@ -5712,26 +5735,26 @@ function reducer(state, action) {
5712
5735
  const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
5713
5736
  const base = i < 0 ? 0 : i;
5714
5737
  const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
5715
- return { ...state, settingsPicker: { ...sp, contextStrategy: COMPACTOR_STRATEGIES[next], hint: void 0 } };
5738
+ return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined5(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
5716
5739
  }
5717
5740
  if (f === 15) {
5718
5741
  const i = LOG_LEVELS.indexOf(sp.logLevel);
5719
5742
  const base = i < 0 ? 0 : i;
5720
5743
  const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
5721
- return { ...state, settingsPicker: { ...sp, logLevel: LOG_LEVELS[next], hint: void 0 } };
5744
+ return { ...state, settingsPicker: { ...sp, logLevel: expectDefined5(LOG_LEVELS[next]), hint: void 0 } };
5722
5745
  }
5723
5746
  if (f === 16) {
5724
5747
  const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
5725
5748
  const base = i < 0 ? 0 : i;
5726
5749
  const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
5727
- return { ...state, settingsPicker: { ...sp, auditLevel: AUDIT_LEVELS[next], hint: void 0 } };
5750
+ return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined5(AUDIT_LEVELS[next]), hint: void 0 } };
5728
5751
  }
5729
5752
  if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
5730
5753
  {
5731
5754
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
5732
5755
  const base = j < 0 ? 0 : j;
5733
5756
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
5734
- return { ...state, settingsPicker: { ...sp, maxIterations: MAX_ITERATIONS_PRESETS[next], hint: void 0 } };
5757
+ return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined5(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
5735
5758
  }
5736
5759
  }
5737
5760
  case "settingsHint":
@@ -6379,7 +6402,12 @@ function reducer(state, action) {
6379
6402
  }
6380
6403
  }
6381
6404
  }
6382
- var MIN_VIEWPORT = 3;
6405
+ function expectDefined6(value) {
6406
+ if (value === null || value === void 0) {
6407
+ throw new Error("Expected value to be defined");
6408
+ }
6409
+ return value;
6410
+ }
6383
6411
  var INPUT_PROMPT = "\u203A ";
6384
6412
  function selectedSlashCommandLine(picker) {
6385
6413
  if (!picker.open || picker.matches.length === 0) return null;
@@ -6388,15 +6416,16 @@ function selectedSlashCommandLine(picker) {
6388
6416
  }
6389
6417
  function rehydrateHistory(messages, startId) {
6390
6418
  const entries = [];
6419
+ let nextId = startId;
6391
6420
  for (const msg of messages) {
6392
6421
  if (msg.role === "system") continue;
6393
6422
  const text = typeof msg.content === "string" ? msg.content : msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
6394
6423
  const trimmed = text.trim();
6395
6424
  if (!trimmed) continue;
6396
6425
  if (msg.role === "user") {
6397
- entries.push({ id: startId++, kind: "user", text: trimmed });
6426
+ entries.push({ id: nextId++, kind: "user", text: trimmed });
6398
6427
  } else if (msg.role === "assistant") {
6399
- entries.push({ id: startId++, kind: "assistant", text: trimmed });
6428
+ entries.push({ id: nextId++, kind: "assistant", text: trimmed });
6400
6429
  }
6401
6430
  }
6402
6431
  return entries;
@@ -6450,7 +6479,6 @@ function App({
6450
6479
  initialGoal,
6451
6480
  initialAsk,
6452
6481
  sessionsDir,
6453
- managed = false,
6454
6482
  modeLabel,
6455
6483
  getModeLabel
6456
6484
  }) {
@@ -6467,18 +6495,6 @@ function App({
6467
6495
  setIndexState(getIndexState());
6468
6496
  return onIndexStateChange((next) => setIndexState(next));
6469
6497
  }, []);
6470
- const { stdout } = useStdout();
6471
- const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
6472
- useEffect(() => {
6473
- const onResize = () => {
6474
- setTermRows(process.stdout.rows ?? 24);
6475
- };
6476
- process.stdout.on("resize", onResize);
6477
- return () => {
6478
- process.stdout.off("resize", onResize);
6479
- };
6480
- }, []);
6481
- const [managedLive, setManagedLive] = useState(managed);
6482
6498
  useEffect(() => {
6483
6499
  setHiddenItems(statuslineHiddenItems);
6484
6500
  }, [statuslineHiddenItems]);
@@ -6628,19 +6644,6 @@ function App({
6628
6644
  stateRef.current = state;
6629
6645
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
6630
6646
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
6631
- const bottomRef = useRef(null);
6632
- React6.useLayoutEffect(() => {
6633
- if (!managedLive) return;
6634
- const node = bottomRef.current;
6635
- if (!node) return;
6636
- const { height } = measureElement(node);
6637
- const s2 = stateRef.current;
6638
- const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
6639
- const vp = Math.max(MIN_VIEWPORT, termRows - height - affordance - 1);
6640
- if (vp !== s2.viewportRows) {
6641
- dispatch({ type: "setViewportRows", rows: vp });
6642
- }
6643
- }, [managedLive, termRows]);
6644
6647
  const handleKeyRef = useRef(null);
6645
6648
  const handleRewindTo = React6.useCallback(
6646
6649
  async (checkpointIndex) => {
@@ -6663,10 +6666,21 @@ function App({
6663
6666
  const startedAtRef = useRef(Date.now());
6664
6667
  const [nowTick, setNowTick] = React6.useState(Date.now());
6665
6668
  useEffect(() => {
6666
- const t = setInterval(() => setNowTick(Date.now()), 1e3);
6669
+ const t = setInterval(() => setNowTick(Date.now()), 1e4);
6667
6670
  return () => clearInterval(t);
6668
6671
  }, []);
6669
- const elapsedMs = nowTick - startedAtRef.current;
6672
+ const todosRef = useRef(JSON.stringify([]));
6673
+ useEffect(() => {
6674
+ const poll = () => {
6675
+ const snap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
6676
+ if (snap !== todosRef.current) {
6677
+ todosRef.current = snap;
6678
+ setNowTick(Date.now());
6679
+ }
6680
+ };
6681
+ const t = setInterval(poll, 2e3);
6682
+ return () => clearInterval(t);
6683
+ }, [agent.ctx.todos]);
6670
6684
  const [gitInfo, setGitInfo] = React6.useState(null);
6671
6685
  useEffect(() => {
6672
6686
  let cancelled = false;
@@ -6744,7 +6758,7 @@ function App({
6744
6758
  const n = m.size + 1;
6745
6759
  const v = {
6746
6760
  label: name && name !== id ? name : `AGENT#${n}`,
6747
- color: STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length]
6761
+ color: STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length] ?? "cyan"
6748
6762
  };
6749
6763
  m.set(id, v);
6750
6764
  return v;
@@ -6826,7 +6840,7 @@ function App({
6826
6840
  }, [eraseLiveRegion]);
6827
6841
  React6.useLayoutEffect(() => {
6828
6842
  if (state.enhanceBusy || state.enhance != null) eraseLiveRegion();
6829
- }, [nowTick, state.enhanceBusy, state.enhance, eraseLiveRegion]);
6843
+ }, [state.enhanceBusy, state.enhance, eraseLiveRegion]);
6830
6844
  useEffect(() => {
6831
6845
  const detected = detectAtToken(state.buffer, state.cursor);
6832
6846
  if (!detected) {
@@ -6858,16 +6872,22 @@ function App({
6858
6872
  }
6859
6873
  const query = trimmed.slice(1).toLowerCase();
6860
6874
  const allCommands = slashRegistry.listWithOwner();
6875
+ const CATEGORY_ORDER = ["Run", "Session", "Inspect", "Agent", "Config", "App"];
6861
6876
  const matches = allCommands.filter(({ cmd }) => {
6862
6877
  const name = cmd.name.toLowerCase();
6863
6878
  const aliases = cmd.aliases ?? [];
6864
6879
  return name.includes(query) || aliases.some((a) => a.toLowerCase().includes(query));
6865
- }).slice(0, 12).map(({ cmd, owner }) => ({
6880
+ }).map(({ cmd, owner }) => ({
6866
6881
  name: cmd.name,
6867
6882
  description: cmd.description,
6868
6883
  argsHint: cmd.argsHint,
6869
- isBuiltin: owner === "core"
6870
- }));
6884
+ isBuiltin: owner === "core",
6885
+ category: cmd.category ?? "App"
6886
+ })).sort((a, b) => {
6887
+ const catDiff = CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category);
6888
+ if (catDiff !== 0) return catDiff;
6889
+ return a.name.localeCompare(b.name);
6890
+ });
6871
6891
  if (!state.slashPicker.open) {
6872
6892
  dispatch({ type: "slashPickerOpen", query, matches });
6873
6893
  } else if (state.slashPicker.query !== query) {
@@ -6999,44 +7019,6 @@ function App({
6999
7019
  getProcessRegistry().killAll();
7000
7020
  };
7001
7021
  }, []);
7002
- useEffect(() => {
7003
- const ALT_OFF = "\x1B[?1049l";
7004
- const ALT_ON = "\x1B[?1049h";
7005
- const cmd = {
7006
- name: "altscreen",
7007
- description: "Toggle the alt-screen buffer. Default is OFF (native scroll); /altscreen on for full-screen mode.",
7008
- async run(args) {
7009
- const arg = args.trim().toLowerCase();
7010
- if (arg === "off") {
7011
- try {
7012
- writeOut(ALT_OFF);
7013
- } catch {
7014
- return { message: "Failed to exit alt-screen." };
7015
- }
7016
- setManagedLive(false);
7017
- return {
7018
- message: "Alt-screen disabled. New entries will land in normal scrollback (mouse wheel / Shift+PgUp work). On-screen history rendered before this command is no longer reachable via terminal scroll. Resize may now leak the live region \u2014 `/altscreen on` to re-enable."
7019
- };
7020
- }
7021
- if (arg === "on") {
7022
- try {
7023
- writeOut(ALT_ON);
7024
- } catch {
7025
- return { message: "Failed to re-enter alt-screen." };
7026
- }
7027
- setManagedLive(true);
7028
- return {
7029
- message: "Alt-screen re-enabled. Managed scroll (PgUp/PgDn) is now active; native scroll is off."
7030
- };
7031
- }
7032
- return { message: "Usage: /altscreen on|off" };
7033
- }
7034
- };
7035
- slashRegistry.register(cmd);
7036
- return () => {
7037
- slashRegistry.unregister("altscreen");
7038
- };
7039
- }, [slashRegistry]);
7040
7022
  useEffect(() => {
7041
7023
  const cmd = {
7042
7024
  name: "steer",
@@ -8138,26 +8120,7 @@ function App({
8138
8120
  if (key.escape || input === "?" || input === "q") dispatch({ type: "toggleHelp" });
8139
8121
  return;
8140
8122
  }
8141
- state.todosMonitorOpen || state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || !!state.autoPhase?.monitorOpen;
8142
8123
  if (inputGateRef.current) return;
8143
- if (managedLive) {
8144
- if (key.pageUp) {
8145
- dispatch({ type: "scrollPage", dir: "up" });
8146
- return;
8147
- }
8148
- if (key.pageDown) {
8149
- dispatch({ type: "scrollPage", dir: "down" });
8150
- return;
8151
- }
8152
- if (key.ctrl && key.home) {
8153
- dispatch({ type: "scrollToTop" });
8154
- return;
8155
- }
8156
- if (key.ctrl && key.end) {
8157
- dispatch({ type: "scrollToBottom" });
8158
- return;
8159
- }
8160
- }
8161
8124
  if (key.escape) {
8162
8125
  const now = Date.now();
8163
8126
  if (state.buffer.length > 0 && now - lastEscAtRef.current < ESC_DOUBLE_PRESS_MS) {
@@ -8692,19 +8655,11 @@ function App({
8692
8655
  return;
8693
8656
  }
8694
8657
  if (key.home) {
8695
- if (managedLive && buffer.length === 0) {
8696
- dispatch({ type: "scrollToTop" });
8697
- } else {
8698
- setDraft(buffer, 0);
8699
- }
8658
+ setDraft(buffer, 0);
8700
8659
  return;
8701
8660
  }
8702
8661
  if (key.end) {
8703
- if (managedLive && buffer.length === 0) {
8704
- dispatch({ type: "scrollToBottom" });
8705
- } else {
8706
- setDraft(buffer, buffer.length);
8707
- }
8662
+ setDraft(buffer, buffer.length);
8708
8663
  return;
8709
8664
  }
8710
8665
  if (key.upArrow) {
@@ -9178,21 +9133,9 @@ User message:
9178
9133
  if (state.picker.open) return "";
9179
9134
  return "";
9180
9135
  }, [state.buffer, state.status, state.picker.open]);
9181
- const affordanceShown = managedLive && state.scrollOffset > 0 && state.pendingNewLines > 0;
9182
9136
  const enhanceActive = state.enhanceBusy || state.enhance != null;
9183
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", height: managedLive ? termRows : void 0, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9184
- managedLive ? /* @__PURE__ */ jsx(
9185
- ScrollableHistory,
9186
- {
9187
- entries: state.entries,
9188
- streamingText: state.streamingText,
9189
- toolStream: state.toolStream,
9190
- scrollOffset: state.scrollOffset,
9191
- viewportRows: state.viewportRows || Math.max(MIN_VIEWPORT, termRows - 8),
9192
- totalLines: state.totalLines,
9193
- onMeasure: (total) => dispatch({ type: "setMeasuredLines", totalLines: total })
9194
- }
9195
- ) : /* @__PURE__ */ jsx(
9137
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9138
+ /* @__PURE__ */ jsx(
9196
9139
  History,
9197
9140
  {
9198
9141
  entries: state.entries,
@@ -9200,8 +9143,7 @@ User message:
9200
9143
  toolStream: state.toolStream
9201
9144
  }
9202
9145
  ),
9203
- affordanceShown ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${state.pendingNewLines} new line${state.pendingNewLines === 1 ? "" : "s"} \u2014 PgDn or click to jump to bottom` }) : null,
9204
- /* @__PURE__ */ jsxs(Box, { ref: managedLive ? bottomRef : void 0, flexDirection: "column", flexShrink: 0, children: [
9146
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
9205
9147
  /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
9206
9148
  /* @__PURE__ */ jsx(
9207
9149
  Input,
@@ -9277,16 +9219,22 @@ User message:
9277
9219
  hint: state.settingsPicker.hint
9278
9220
  }
9279
9221
  ) : null,
9280
- state.rewindOverlay ? /* @__PURE__ */ jsx(
9281
- CheckpointTimeline,
9282
- {
9283
- checkpoints: state.rewindOverlay.checkpoints,
9284
- selected: state.rewindOverlay.selected,
9285
- onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - state.rewindOverlay.selected }),
9286
- onConfirm: (i) => handleRewindTo(state.rewindOverlay.checkpoints[i].promptIndex),
9287
- onClose: () => dispatch({ type: "rewindOverlayClose" })
9288
- }
9289
- ) : null,
9222
+ state.rewindOverlay ? (() => {
9223
+ const overlay = state.rewindOverlay;
9224
+ return /* @__PURE__ */ jsx(
9225
+ CheckpointTimeline,
9226
+ {
9227
+ checkpoints: overlay.checkpoints,
9228
+ selected: overlay.selected,
9229
+ onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - overlay.selected }),
9230
+ onConfirm: (i) => {
9231
+ const checkpoint = overlay.checkpoints[i];
9232
+ if (checkpoint) handleRewindTo(checkpoint.promptIndex);
9233
+ },
9234
+ onClose: () => dispatch({ type: "rewindOverlayClose" })
9235
+ }
9236
+ );
9237
+ })() : null,
9290
9238
  state.brainPrompt ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx(
9291
9239
  BrainDecisionPrompt,
9292
9240
  {
@@ -9298,7 +9246,7 @@ User message:
9298
9246
  }
9299
9247
  ) }) : null,
9300
9248
  state.confirmQueue.length > 0 && (() => {
9301
- const head = state.confirmQueue[0];
9249
+ const head = expectDefined6(state.confirmQueue[0]);
9302
9250
  let resolved = false;
9303
9251
  const onDecision = (decision) => {
9304
9252
  if (resolved) return;
@@ -9346,7 +9294,7 @@ User message:
9346
9294
  queueCount: state.queue.length,
9347
9295
  yolo: yoloLive,
9348
9296
  autonomy: autonomyLive,
9349
- elapsedMs,
9297
+ startedAt: startedAtRef.current,
9350
9298
  todos,
9351
9299
  plan: planCounts ?? void 0,
9352
9300
  fleet: fleetCounts,
@@ -9363,18 +9311,7 @@ User message:
9363
9311
  modeLabel: liveModeLabel || void 0
9364
9312
  }
9365
9313
  ),
9366
- managedLive ? /* @__PURE__ */ jsx(
9367
- KeyHintBar,
9368
- {
9369
- context: {
9370
- confirm: state.confirmQueue.length > 0,
9371
- picker: state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || !!state.rewindOverlay,
9372
- monitor: state.agentsMonitorOpen || state.monitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || !!state.autoPhase?.monitorOpen,
9373
- managed: managedLive
9374
- }
9375
- }
9376
- ) : null,
9377
- state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive }) : null,
9314
+ state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
9378
9315
  state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
9379
9316
  AgentsMonitor,
9380
9317
  {
@@ -9401,7 +9338,7 @@ User message:
9401
9338
  nowTick,
9402
9339
  onClose: () => dispatch({ type: "worktreeMonitorToggle" })
9403
9340
  }
9404
- ) : state.todosMonitorOpen && !managedLive ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
9341
+ ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
9405
9342
  FleetMonitor,
9406
9343
  {
9407
9344
  entries: state.fleet,
@@ -9531,9 +9468,6 @@ function startTerminalTitle(opts) {
9531
9468
  // src/run-tui.ts
9532
9469
  var BRACKETED_PASTE_ON = "\x1B[?2004h";
9533
9470
  var BRACKETED_PASTE_OFF = "\x1B[?2004l";
9534
- var ALT_SCREEN_ON = "\x1B[?1049h";
9535
- var ALT_SCREEN_OFF = "\x1B[?1049l";
9536
- var CURSOR_HOME = "\x1B[H";
9537
9471
  async function runTui(opts) {
9538
9472
  const stdout = process.stdout;
9539
9473
  const stdin = process.stdin;
@@ -9543,12 +9477,8 @@ async function runTui(opts) {
9543
9477
  );
9544
9478
  return 2;
9545
9479
  }
9546
- const useAltScreen = opts.altScreen === true;
9547
- if (useAltScreen) {
9548
- stdout.write(ALT_SCREEN_ON);
9549
- stdout.write(CURSOR_HOME);
9550
- }
9551
9480
  stdout.write(BRACKETED_PASTE_ON);
9481
+ stdout.write("\x1B[2J\x1B[H");
9552
9482
  const inkStdin = stdin;
9553
9483
  const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({ stdout, events: opts.events, model: opts.model }) : (() => {
9554
9484
  });
@@ -9571,9 +9501,6 @@ async function runTui(opts) {
9571
9501
  }
9572
9502
  try {
9573
9503
  stdout.write(BRACKETED_PASTE_OFF);
9574
- if (useAltScreen) {
9575
- stdout.write(ALT_SCREEN_OFF);
9576
- }
9577
9504
  } catch {
9578
9505
  }
9579
9506
  };
@@ -9600,12 +9527,6 @@ async function runTui(opts) {
9600
9527
  const settle = (code) => {
9601
9528
  cleanup();
9602
9529
  detachListeners();
9603
- if (useAltScreen && opts.onAfterExit) {
9604
- try {
9605
- opts.onAfterExit();
9606
- } catch {
9607
- }
9608
- }
9609
9530
  resolve(code);
9610
9531
  };
9611
9532
  let instance;
@@ -9641,7 +9562,7 @@ async function runTui(opts) {
9641
9562
  onExit,
9642
9563
  director: opts.director ?? null,
9643
9564
  fleetRoster: opts.fleetRoster,
9644
- onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory(dispatch) : void 0,
9565
+ onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory?.(dispatch) : void 0,
9645
9566
  fleetStreamController: opts.fleetStreamController,
9646
9567
  enhanceController: opts.enhanceController,
9647
9568
  enhanceEnabled: opts.enhanceController?.enabled ?? true,
@@ -9657,9 +9578,6 @@ async function runTui(opts) {
9657
9578
  getSettings: opts.getSettings,
9658
9579
  saveSettings: opts.saveSettings,
9659
9580
  predictNext: opts.predictNext,
9660
- // Managed viewport (in-app scroll + collapsibility) follows
9661
- // alt-screen: it owns the screen, so there's no native-scrollback leak.
9662
- managed: useAltScreen,
9663
9581
  chime: opts.chime,
9664
9582
  confirmExit: opts.confirmExit,
9665
9583
  modeLabel: opts.modeLabel,
@@ -9676,16 +9594,14 @@ async function runTui(opts) {
9676
9594
  return;
9677
9595
  }
9678
9596
  let detachResize = null;
9679
- if (!useAltScreen) {
9680
- const onResize = () => {
9681
- try {
9682
- stdout.write("\x1B[J");
9683
- } catch {
9684
- }
9685
- };
9686
- stdout.on("resize", onResize);
9687
- detachResize = () => stdout.off("resize", onResize);
9688
- }
9597
+ const onResize = () => {
9598
+ try {
9599
+ stdout.write("\x1B[J");
9600
+ } catch {
9601
+ }
9602
+ };
9603
+ stdout.on("resize", onResize);
9604
+ detachResize = () => stdout.off("resize", onResize);
9689
9605
  instance.waitUntilExit().then(() => {
9690
9606
  detachResize?.();
9691
9607
  settle(exitCode);