@wrongstack/tui 0.8.2 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,8 +16,8 @@ function stringifyInput(input) {
16
16
  const obj = input;
17
17
  return Object.entries(obj).filter(([k]) => k !== "content" && k !== "new_string").map(([k, v]) => `${k}: ${truncate(JSON.stringify(v), 80)}`).join(" ");
18
18
  }
19
- function truncate(s, max) {
20
- return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
19
+ function truncate(s2, max) {
20
+ return s2.length <= max ? s2 : `${s2.slice(0, max - 1)}\u2026`;
21
21
  }
22
22
  function hasDiff(input) {
23
23
  return Boolean(
@@ -677,11 +677,11 @@ function fmtElapsed(ms) {
677
677
  const totalSec = Math.floor(ms / 1e3);
678
678
  const h = Math.floor(totalSec / 3600);
679
679
  const m = Math.floor(totalSec % 3600 / 60);
680
- const s = totalSec % 60;
680
+ const s2 = totalSec % 60;
681
681
  if (h > 0) {
682
- return `${h}:${pad2(m)}:${pad2(s)}`;
682
+ return `${h}:${pad2(m)}:${pad2(s2)}`;
683
683
  }
684
- return `${pad2(m)}:${pad2(s)}`;
684
+ return `${pad2(m)}:${pad2(s2)}`;
685
685
  }
686
686
  function pad2(n) {
687
687
  return n < 10 ? `0${n}` : String(n);
@@ -751,11 +751,11 @@ function FleetMonitor({
751
751
  for (const e of all) {
752
752
  events.push({ at: e.startedAt, icon: "\u25CF", color: "cyan", text: `${e.name} spawned` });
753
753
  if (e.status !== "running" && e.status !== "idle") {
754
- const s = STATUS[e.status];
754
+ const s2 = STATUS[e.status];
755
755
  events.push({
756
756
  at: e.lastEventAt,
757
- icon: s.icon,
758
- color: s.color,
757
+ icon: s2.icon,
758
+ color: s2.color,
759
759
  text: `${e.name} ${e.status} (${e.toolCalls}t)`
760
760
  });
761
761
  }
@@ -824,12 +824,12 @@ function FleetMonitor({
824
824
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "cost" })
825
825
  ] }),
826
826
  shown.map((e) => {
827
- const s = STATUS[e.status];
827
+ const s2 = STATUS[e.status];
828
828
  const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : fmtElapsed(Math.max(0, nowTick - e.lastEventAt)) + " ago";
829
829
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
830
- /* @__PURE__ */ jsx(Text, { color: s.color, children: s.icon }),
830
+ /* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.icon }),
831
831
  /* @__PURE__ */ jsx(Text, { children: e.name.padEnd(16).slice(0, 16) }),
832
- /* @__PURE__ */ jsx(Text, { color: s.color, children: e.status.padEnd(10) }),
832
+ /* @__PURE__ */ jsx(Text, { color: s2.color, children: e.status.padEnd(10) }),
833
833
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: `L${e.iterations} ${e.toolCalls}t`.padEnd(8) }),
834
834
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(8).slice(0, 8) }),
835
835
  /* @__PURE__ */ jsx(Text, { color: "yellow", children: fmtCost2(e.cost) }),
@@ -870,8 +870,8 @@ function fmtTokens2(n) {
870
870
  if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
871
871
  return `${(n / 1e6).toFixed(1)}M`;
872
872
  }
873
- function snippet(s, max = 72) {
874
- const oneLine2 = s.replace(/\s+/g, " ").trim();
873
+ function snippet(s2, max = 72) {
874
+ const oneLine2 = s2.replace(/\s+/g, " ").trim();
875
875
  if (oneLine2.length <= max) return oneLine2;
876
876
  return `${oneLine2.slice(0, max - 1)}\u2026`;
877
877
  }
@@ -931,7 +931,7 @@ function AgentsMonitor({
931
931
  ] }),
932
932
  shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No live agents \u2014 spawn with /spawn or /fleet dispatch." }) : null,
933
933
  shown.map((e) => {
934
- const s = STATUS2[e.status];
934
+ const s2 = STATUS2[e.status];
935
935
  const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : e.status;
936
936
  const spark = sparkline(bucketActivity(e.recentTools, nowTick));
937
937
  const lastTool = e.recentTools[e.recentTools.length - 1];
@@ -940,7 +940,7 @@ function AgentsMonitor({
940
940
  const toolElapsed = e.currentTool ? Math.max(0, nowTick - e.currentTool.startedAt) : 0;
941
941
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
942
942
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
943
- /* @__PURE__ */ jsx(Text, { color: s.color, bold: true, children: s.icon }),
943
+ /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
944
944
  /* @__PURE__ */ jsx(Text, { bold: true, children: e.name }),
945
945
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
946
946
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed }),
@@ -999,6 +999,179 @@ function AgentsMonitor({
999
999
  })
1000
1000
  ] });
1001
1001
  }
1002
+ var fmtElapsed2 = (ms) => {
1003
+ const s2 = Math.floor(ms / 1e3);
1004
+ const m = Math.floor(s2 / 60);
1005
+ const h = Math.floor(m / 60);
1006
+ if (h > 0) return `${h}:${String(m % 60).padStart(2, "0")}:${String(s2 % 60).padStart(2, "0")}`;
1007
+ if (m > 0) return `${m}:${String(s2 % 60).padStart(2, "0")}`;
1008
+ return `${s2}s`;
1009
+ };
1010
+ var PHASE_STATUS = {
1011
+ pending: { icon: "\u23F3", color: "gray", label: "pending" },
1012
+ ready: { icon: "\u{1F51C}", color: "cyan", label: "ready" },
1013
+ running: { icon: "\u25B6", color: "yellow", label: "running" },
1014
+ paused: { icon: "\u23F8", color: "magenta", label: "paused" },
1015
+ completed: { icon: "\u2713", color: "green", label: "done" },
1016
+ failed: { icon: "\u2717", color: "red", label: "failed" },
1017
+ skipped: { icon: "\u23ED", color: "gray", label: "skipped" }
1018
+ };
1019
+ function fmtPhase(s2) {
1020
+ return PHASE_STATUS[s2] ?? { icon: "?", color: "white", label: s2 };
1021
+ }
1022
+ function PhaseMonitor({
1023
+ phases,
1024
+ runningPhaseIds,
1025
+ elapsedMs,
1026
+ nowTick,
1027
+ onClose
1028
+ }) {
1029
+ useInput((_, key) => {
1030
+ if (key.escape) onClose();
1031
+ });
1032
+ const phaseList = Object.values(phases);
1033
+ const running = phaseList.filter((p) => runningPhaseIds.includes(Object.keys(phases).find((k) => phases[k] === p) ?? ""));
1034
+ const done = phaseList.filter((p) => p.status === "completed" || p.status === "skipped");
1035
+ const failed = phaseList.filter((p) => p.status === "failed");
1036
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
1037
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginBottom: 1, children: [
1038
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "PHASE MONITOR" }),
1039
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1040
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1041
+ "\u23F1 ",
1042
+ fmtElapsed2(elapsedMs)
1043
+ ] }),
1044
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1045
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1046
+ "\u25B6",
1047
+ running.length
1048
+ ] }),
1049
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1050
+ /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1051
+ "\u2713",
1052
+ done.length
1053
+ ] }),
1054
+ failed.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1055
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1056
+ /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1057
+ "\u2717",
1058
+ failed.length
1059
+ ] })
1060
+ ] }) : null,
1061
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+P / Esc to close" })
1062
+ ] }),
1063
+ phaseList.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No phases active. Use /autophase start [title] to begin." }) : phaseList.map((phase, i) => {
1064
+ const s2 = fmtPhase(phase.status);
1065
+ const phaseKey = Object.keys(phases).find((k) => phases[k] === phase) ?? String(i);
1066
+ const isRunning = runningPhaseIds.includes(phaseKey);
1067
+ const elapsed = phase.startedAt ? fmtElapsed2(nowTick - phase.startedAt) : "\u2014";
1068
+ const progress = phase.totalTasks > 0 ? `${phase.completedTasks}/${phase.totalTasks}` : "\u2014";
1069
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1070
+ /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
1071
+ /* @__PURE__ */ jsx(Text, { bold: true, children: phase.name }),
1072
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1073
+ /* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.label }),
1074
+ isRunning ? /* @__PURE__ */ jsxs(Fragment, { children: [
1075
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1076
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1077
+ "elapsed ",
1078
+ elapsed
1079
+ ] })
1080
+ ] }) : null,
1081
+ phase.totalTasks > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1082
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1083
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1084
+ "tasks ",
1085
+ progress
1086
+ ] })
1087
+ ] })
1088
+ ] }) }, phaseKey);
1089
+ }),
1090
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate phases \xB7 Esc close" }) })
1091
+ ] });
1092
+ }
1093
+ var fmtElapsed3 = (ms) => {
1094
+ const s2 = Math.floor(ms / 1e3);
1095
+ const m = Math.floor(s2 / 60);
1096
+ const h = Math.floor(m / 60);
1097
+ if (h > 0) return `${h}:${String(m % 60).padStart(2, "0")}:${String(s2 % 60).padStart(2, "0")}`;
1098
+ if (m > 0) return `${m}:${String(s2 % 60).padStart(2, "0")}`;
1099
+ return `${s2}s`;
1100
+ };
1101
+ var STATUS3 = {
1102
+ pending: { icon: "\u25CB", color: "gray" },
1103
+ ready: { icon: "\u25D0", color: "cyan" },
1104
+ running: { icon: "\u25CF", color: "yellow" },
1105
+ paused: { icon: "\u23F8", color: "magenta" },
1106
+ completed: { icon: "\u2713", color: "green" },
1107
+ failed: { icon: "\u2717", color: "red" },
1108
+ skipped: { icon: "\u2298", color: "gray" }
1109
+ };
1110
+ function s(entry) {
1111
+ return STATUS3[entry] ?? { icon: "?", color: "white" };
1112
+ }
1113
+ function PhasePanel({ phases, runningPhaseIds, nowTick }) {
1114
+ const list = Object.values(phases);
1115
+ if (list.length === 0) return null;
1116
+ const done = list.filter((p) => p.status === "completed" || p.status === "skipped").length;
1117
+ const running = list.filter((p) => p.status === "running").length;
1118
+ const failed = list.filter((p) => p.status === "failed").length;
1119
+ return /* @__PURE__ */ jsxs(
1120
+ Box,
1121
+ {
1122
+ flexDirection: "column",
1123
+ paddingX: 1,
1124
+ borderStyle: "single",
1125
+ borderTop: false,
1126
+ borderBottom: false,
1127
+ borderLeft: false,
1128
+ borderRight: false,
1129
+ children: [
1130
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1131
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Phases" }),
1132
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1133
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1134
+ "\u25B6",
1135
+ running
1136
+ ] }),
1137
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1138
+ "\u2713",
1139
+ done
1140
+ ] }),
1141
+ failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1142
+ "\u2717",
1143
+ failed
1144
+ ] }) : null,
1145
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1146
+ "\xB7 ",
1147
+ list.length,
1148
+ " total"
1149
+ ] }),
1150
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+P for details" })
1151
+ ] }),
1152
+ list.map((phase, i) => {
1153
+ const phaseKey = Object.keys(phases).find((k) => phases[k] === phase) ?? String(i);
1154
+ const st = s(phase.status);
1155
+ const progress = phase.totalTasks > 0 ? `${phase.completedTasks}/${phase.totalTasks}` : "";
1156
+ const elapsed = phase.startedAt ? fmtElapsed3(nowTick - phase.startedAt) : "";
1157
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1158
+ /* @__PURE__ */ jsx(Text, { color: st.color, children: st.icon }),
1159
+ /* @__PURE__ */ jsx(Text, { children: phase.name.slice(0, 14).padEnd(14) }),
1160
+ /* @__PURE__ */ jsx(Text, { color: st.color, dimColor: true, children: st.icon === "\u25CF" ? phase.status : "" }),
1161
+ progress ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1162
+ " ",
1163
+ progress
1164
+ ] }) : null,
1165
+ elapsed && st.icon === "\u25CF" ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1166
+ " ",
1167
+ elapsed
1168
+ ] }) : null
1169
+ ] }, phaseKey);
1170
+ })
1171
+ ]
1172
+ }
1173
+ );
1174
+ }
1002
1175
 
1003
1176
  // src/markdown-table.ts
1004
1177
  function renderMarkdownTables(text, maxWidth) {
@@ -1095,7 +1268,7 @@ function computeWidths(allRows, cols, maxWidth) {
1095
1268
  if (w > natural[c]) natural[c] = Math.min(total + 1, w);
1096
1269
  }
1097
1270
  }
1098
- const sumNatural = natural.reduce((s, n) => s + n, 0);
1271
+ const sumNatural = natural.reduce((s2, n) => s2 + n, 0);
1099
1272
  if (sumNatural <= avail) return natural;
1100
1273
  const widths = natural.slice();
1101
1274
  let sum = sumNatural;
@@ -1116,9 +1289,9 @@ function computeWidths(allRows, cols, maxWidth) {
1116
1289
  return widths;
1117
1290
  }
1118
1291
  var MIN_COL_WIDTH = 4;
1119
- function longestWord(s) {
1292
+ function longestWord(s2) {
1120
1293
  let max = 0;
1121
- for (const w of s.split(/\s+/)) if (w.length > max) max = w.length;
1294
+ for (const w of s2.split(/\s+/)) if (w.length > max) max = w.length;
1122
1295
  return max;
1123
1296
  }
1124
1297
  function border(left, mid, right, widths) {
@@ -1180,7 +1353,17 @@ function padCell(text, width, align) {
1180
1353
  }
1181
1354
  function History({ entries, streamingText, toolStream }) {
1182
1355
  const { stdout } = useStdout();
1183
- const termWidth = stdout?.columns ?? 80;
1356
+ const [termSize, setTermSize] = useState({ columns: stdout?.columns ?? 80, rows: stdout?.rows ?? 24 });
1357
+ useEffect(() => {
1358
+ const handleResize = () => {
1359
+ setTermSize({ columns: stdout?.columns ?? 80, rows: stdout?.rows ?? 24 });
1360
+ };
1361
+ process.stdout.on("resize", handleResize);
1362
+ return () => {
1363
+ process.stdout.off("resize", handleResize);
1364
+ };
1365
+ }, [stdout]);
1366
+ const termWidth = termSize.columns;
1184
1367
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
1185
1368
  const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
1186
1369
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -2056,11 +2239,11 @@ function stringOf(v) {
2056
2239
  function numOf(v) {
2057
2240
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2058
2241
  }
2059
- function tryParseJson(s) {
2060
- const t = s.trimStart();
2242
+ function tryParseJson(s2) {
2243
+ const t = s2.trimStart();
2061
2244
  if (!t.startsWith("{") && !t.startsWith("[")) return void 0;
2062
2245
  try {
2063
- return JSON.parse(s);
2246
+ return JSON.parse(s2);
2064
2247
  } catch {
2065
2248
  return void 0;
2066
2249
  }
@@ -2091,9 +2274,9 @@ function fmtBytes2(n) {
2091
2274
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
2092
2275
  return `${(n / (1024 * 1024)).toFixed(1)}MB`;
2093
2276
  }
2094
- function truncMid(s, max) {
2095
- if (s.length <= max) return s;
2096
- return `${s.slice(0, max - 1)}\u2026`;
2277
+ function truncMid(s2, max) {
2278
+ if (s2.length <= max) return s2;
2279
+ return `${s2.slice(0, max - 1)}\u2026`;
2097
2280
  }
2098
2281
  function isHomeEnd(data) {
2099
2282
  if (data === "\x1B[H" || data === "\x1B[1~" || data === "\x1BOH" || data === "\x1B[7~")
@@ -2167,12 +2350,12 @@ function Input({
2167
2350
  hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
2168
2351
  ] });
2169
2352
  }
2170
- function fmtElapsed2(ms) {
2353
+ function fmtElapsed4(ms) {
2171
2354
  if (ms < 1e3) return `${ms}ms`;
2172
2355
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2173
2356
  const m = Math.floor(ms / 6e4);
2174
- const s = Math.floor(ms % 6e4 / 1e3);
2175
- return `${m}m${s.toString().padStart(2, "0")}s`;
2357
+ const s2 = Math.floor(ms % 6e4 / 1e3);
2358
+ return `${m}m${s2.toString().padStart(2, "0")}s`;
2176
2359
  }
2177
2360
  function fmtBytes3(n) {
2178
2361
  if (n < 1024) return `${n}B`;
@@ -2182,7 +2365,7 @@ function fmtRecentTool2(tool) {
2182
2365
  const status = tool.ok === false ? "fail" : "ok";
2183
2366
  const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
2184
2367
  const parts = [status, name];
2185
- if (typeof tool.durationMs === "number") parts.push(fmtElapsed2(tool.durationMs));
2368
+ if (typeof tool.durationMs === "number") parts.push(fmtElapsed4(tool.durationMs));
2186
2369
  if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes3(tool.outputBytes));
2187
2370
  if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
2188
2371
  return parts.join(" ");
@@ -2203,7 +2386,7 @@ function LiveActivityStrip({
2203
2386
  running.map((e) => {
2204
2387
  const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
2205
2388
  const taskElapsed = now - e.startedAt;
2206
- const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed2(toolElapsed)})` : "idle between tools";
2389
+ const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed4(toolElapsed)})` : "idle between tools";
2207
2390
  const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
2208
2391
  const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
2209
2392
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
@@ -2217,7 +2400,7 @@ function LiveActivityStrip({
2217
2400
  "it ",
2218
2401
  e.toolCalls,
2219
2402
  "tc \xB7 ",
2220
- fmtElapsed2(taskElapsed)
2403
+ fmtElapsed4(taskElapsed)
2221
2404
  ] }),
2222
2405
  recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
2223
2406
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
@@ -2376,10 +2559,10 @@ async function walk(root, rel, depth, out) {
2376
2559
  }
2377
2560
  }
2378
2561
  }
2379
- function score(s, query) {
2380
- if (!query) return s.length;
2562
+ function score(s2, query) {
2563
+ if (!query) return s2.length;
2381
2564
  const ql = query.toLowerCase();
2382
- const sl = s.toLowerCase();
2565
+ const sl = s2.toLowerCase();
2383
2566
  let si = 0;
2384
2567
  let firstHit = -1;
2385
2568
  let lastHit = -1;
@@ -2392,7 +2575,7 @@ function score(s, query) {
2392
2575
  si++;
2393
2576
  }
2394
2577
  const span = lastHit - firstHit;
2395
- return span * 100 + firstHit * 2 + s.length;
2578
+ return span * 100 + firstHit * 2 + s2.length;
2396
2579
  }
2397
2580
  async function searchFiles(root, query, limit = 8) {
2398
2581
  const all = await loadIndex(root);
@@ -2546,8 +2729,8 @@ function renderList(queue) {
2546
2729
  }
2547
2730
  return lines.join("\n");
2548
2731
  }
2549
- function oneLine(s, max) {
2550
- const collapsed = s.replace(/\s+/g, " ").trim();
2732
+ function oneLine(s2, max) {
2733
+ const collapsed = s2.replace(/\s+/g, " ").trim();
2551
2734
  return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;
2552
2735
  }
2553
2736
  var USAGE2 = "Usage:\n /kill \u2014 list active processes + breaker state\n /kill list \u2014 same as /kill\n /kill all \u2014 kill all tracked processes (SIGTERM \u2192 SIGKILL)\n /kill force \u2014 kill all with SIGKILL immediately\n /kill reset \u2014 reset the circuit breaker to closed\n /kill <pid> \u2014 kill a specific process by PID";
@@ -3214,6 +3397,61 @@ function reducer(state, action) {
3214
3397
  case "goalSummary": {
3215
3398
  return { ...state, goalSummary: action.summary };
3216
3399
  }
3400
+ case "autoPhaseInit": {
3401
+ return {
3402
+ ...state,
3403
+ autoPhase: {
3404
+ title: action.title,
3405
+ phases: {},
3406
+ runningPhaseIds: [],
3407
+ elapsedMs: 0,
3408
+ monitorOpen: false
3409
+ }
3410
+ };
3411
+ }
3412
+ case "autoPhasePhaseUpdate": {
3413
+ const existing = state.autoPhase ?? {
3414
+ title: "AutoPhase",
3415
+ phases: {},
3416
+ runningPhaseIds: [],
3417
+ elapsedMs: 0,
3418
+ monitorOpen: false
3419
+ };
3420
+ return {
3421
+ ...state,
3422
+ autoPhase: {
3423
+ ...existing,
3424
+ phases: {
3425
+ ...existing.phases,
3426
+ [action.phaseId]: {
3427
+ name: action.name,
3428
+ status: action.status,
3429
+ completedTasks: action.completedTasks,
3430
+ totalTasks: action.totalTasks,
3431
+ startedAt: action.startedAt
3432
+ }
3433
+ }
3434
+ }
3435
+ };
3436
+ }
3437
+ case "autoPhaseRunningPhases": {
3438
+ if (!state.autoPhase) return state;
3439
+ return {
3440
+ ...state,
3441
+ autoPhase: { ...state.autoPhase, runningPhaseIds: action.phaseIds }
3442
+ };
3443
+ }
3444
+ case "autoPhaseElapsed": {
3445
+ if (!state.autoPhase) return state;
3446
+ return { ...state, autoPhase: { ...state.autoPhase, elapsedMs: action.ms } };
3447
+ }
3448
+ case "autoPhaseMonitorToggle": {
3449
+ if (!state.autoPhase) return state;
3450
+ return { ...state, autoPhase: { ...state.autoPhase, monitorOpen: !state.autoPhase.monitorOpen } };
3451
+ }
3452
+ case "autoPhaseReset": {
3453
+ return { ...state, autoPhase: null };
3454
+ }
3217
3455
  }
3218
3456
  }
3219
3457
  var PASTE_THRESHOLD_CHARS = 200;
@@ -3224,7 +3462,7 @@ function buildSteeringPreamble(snapshot, newDirection) {
3224
3462
  ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
3225
3463
  }
3226
3464
  if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
3227
- const subDetails = snapshot.subagents.map((s) => `${s.label}${s.tool ? ` (was running: ${s.tool})` : ""}`).join(", ");
3465
+ const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
3228
3466
  ctx.push(
3229
3467
  `- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
3230
3468
  );
@@ -3269,6 +3507,7 @@ function App({
3269
3507
  getParallelEngine,
3270
3508
  subscribeEternalIteration,
3271
3509
  subscribeEternalStage,
3510
+ subscribeAutoPhase,
3272
3511
  getSDDContext,
3273
3512
  onSDDOutput,
3274
3513
  appVersion,
@@ -3385,7 +3624,8 @@ function App({
3385
3624
  checkpoints: [],
3386
3625
  rewindOverlay: null,
3387
3626
  eternalStage: null,
3388
- goalSummary: null
3627
+ goalSummary: null,
3628
+ autoPhase: null
3389
3629
  });
3390
3630
  const builderRef = useRef(null);
3391
3631
  if (builderRef.current === null) {
@@ -3784,9 +4024,9 @@ function App({
3784
4024
  if (!text) {
3785
4025
  return { message: "Usage: /steer <new direction>" };
3786
4026
  }
3787
- const s = stateRef.current;
3788
- const runningTools = Array.from(s.runningTools.values()).map((t) => t.name);
3789
- const subagents = Object.values(s.fleet).filter((e) => e.status === "running").map((e) => ({ label: e.name, status: e.status, tool: e.currentTool?.name }));
4027
+ const s2 = stateRef.current;
4028
+ const runningTools = Array.from(s2.runningTools.values()).map((t) => t.name);
4029
+ const subagents = Object.values(s2.fleet).filter((e) => e.status === "running").map((e) => ({ label: e.name, status: e.status, tool: e.currentTool?.name }));
3790
4030
  const subagentsTerminated = subagents.length;
3791
4031
  const partialAssistantText = streamingTextRef.current.slice(-1500);
3792
4032
  activeCtrlRef.current?.abort();
@@ -3794,7 +4034,7 @@ function App({
3794
4034
  type: "steerStart",
3795
4035
  snapshot: { runningTools, subagents, subagentsTerminated, partialAssistantText }
3796
4036
  });
3797
- const droppedCount = s.queue.length;
4037
+ const droppedCount = s2.queue.length;
3798
4038
  if (droppedCount > 0) dispatch({ type: "queueClear" });
3799
4039
  if (director && subagentsTerminated > 0) {
3800
4040
  const cap = new Promise((resolve) => {
@@ -3841,8 +4081,8 @@ function App({
3841
4081
  handleRewindTo(idx);
3842
4082
  return {};
3843
4083
  }
3844
- const s = stateRef.current;
3845
- if (s.checkpoints.length === 0) {
4084
+ const s2 = stateRef.current;
4085
+ if (s2.checkpoints.length === 0) {
3846
4086
  return { message: "No checkpoints in this session yet." };
3847
4087
  }
3848
4088
  dispatch({ type: "rewindOverlayOpen" });
@@ -4191,18 +4431,91 @@ function App({
4191
4431
  offRewound();
4192
4432
  };
4193
4433
  }, [events, onClearHistory]);
4434
+ useEffect(() => {
4435
+ if (!subscribeAutoPhase) return;
4436
+ const handler = (event, payload) => {
4437
+ switch (event) {
4438
+ case "phase.started": {
4439
+ const p = payload;
4440
+ dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status: "running", completedTasks: 0, totalTasks: 0, startedAt: Date.now() });
4441
+ break;
4442
+ }
4443
+ case "phase.completed": {
4444
+ const p = payload;
4445
+ dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status: "completed", completedTasks: 0, totalTasks: 0 });
4446
+ break;
4447
+ }
4448
+ case "phase.failed": {
4449
+ const p = payload;
4450
+ dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status: "failed", completedTasks: 0, totalTasks: 0 });
4451
+ break;
4452
+ }
4453
+ case "phase.statusChange": {
4454
+ const p = payload;
4455
+ const status = p.to === "running" ? "running" : p.to;
4456
+ dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status, completedTasks: 0, totalTasks: 0 });
4457
+ break;
4458
+ }
4459
+ case "phase.taskCompleted": {
4460
+ const p = payload;
4461
+ const existing = stateRef.current.autoPhase?.phases[p.phaseId];
4462
+ if (existing) {
4463
+ dispatch({
4464
+ type: "autoPhasePhaseUpdate",
4465
+ phaseId: p.phaseId,
4466
+ name: existing.name,
4467
+ status: existing.status,
4468
+ completedTasks: existing.completedTasks + 1,
4469
+ totalTasks: existing.totalTasks
4470
+ });
4471
+ }
4472
+ break;
4473
+ }
4474
+ case "autonomous.tick": {
4475
+ const p = payload;
4476
+ dispatch({ type: "autoPhaseRunningPhases", phaseIds: p.activePhases.map((ph) => ph.id) });
4477
+ const ap = stateRef.current.autoPhase;
4478
+ if (ap) {
4479
+ const firstPhase = ap.phases[Object.keys(ap.phases)[0] ?? ""];
4480
+ const elapsed = ap.elapsedMs > 0 ? ap.elapsedMs + 1e3 : Date.now() - (firstPhase?.startedAt ?? Date.now());
4481
+ dispatch({ type: "autoPhaseElapsed", ms: elapsed });
4482
+ }
4483
+ break;
4484
+ }
4485
+ case "graph.completed": {
4486
+ dispatch({ type: "autoPhaseReset" });
4487
+ break;
4488
+ }
4489
+ case "graph.failed": {
4490
+ dispatch({ type: "autoPhaseReset" });
4491
+ break;
4492
+ }
4493
+ }
4494
+ };
4495
+ return subscribeAutoPhase(handler);
4496
+ }, [subscribeAutoPhase]);
4194
4497
  useEffect(() => {
4195
4498
  const offFired = events.on("compaction.fired", (e) => {
4196
- const { level, tokens, load, maxContext: maxContext2, report, aggressive } = e;
4499
+ const { level, tokens, load, maxContext: maxContext2, report } = e;
4197
4500
  const pct = (load * 100).toFixed(0);
4198
4501
  const before = report.before;
4199
4502
  const after = report.after;
4200
4503
  const saved = before - after;
4504
+ if (saved <= 0) {
4505
+ dispatch({
4506
+ type: "addEntry",
4507
+ entry: {
4508
+ kind: "info",
4509
+ text: `\u25B8 compaction skipped at ${level} \u2014 load ${pct}% (${tokens.toLocaleString()} of ${maxContext2.toLocaleString()} tok). preserveK protects recent turns; nothing to elide.`
4510
+ }
4511
+ });
4512
+ return;
4513
+ }
4201
4514
  const table = [
4202
- `\u25B8 context compacted at ${level} (${pct}% of ${maxContext2.toLocaleString()} tok)`,
4203
- ` tokens before ${before.toLocaleString().padStart(8)}`,
4204
- ` tokens after ${after.toLocaleString().padStart(8)}`,
4205
- ` saved ${saved.toLocaleString().padStart(8)} (${(saved / before * 100).toFixed(1)}%)`
4515
+ `\u25B8 context compacted at ${level} \u2014 load ${pct}% (${tokens.toLocaleString()} of ${maxContext2.toLocaleString()} tok, full request)`,
4516
+ ` msg tokens before ${before.toLocaleString().padStart(8)}`,
4517
+ ` msg tokens after ${after.toLocaleString().padStart(8)}`,
4518
+ ` saved ${saved.toLocaleString().padStart(8)} (${(saved / before * 100).toFixed(1)}%)`
4206
4519
  ];
4207
4520
  for (const line of table) {
4208
4521
  dispatch({ type: "addEntry", entry: { kind: "info", text: line } });
@@ -4211,7 +4524,7 @@ function App({
4211
4524
  const offFailed = events.on("compaction.failed", (e) => {
4212
4525
  const { level, load, maxContext: maxContext2, fatal } = e;
4213
4526
  const pct = (load * 100).toFixed(0);
4214
- const text = fatal ? `\u2717 compaction failed at ${level} (${pct}% of ${maxContext2.toLocaleString()} tok) \u2014 FATAL` : `\u26A0 compaction failed at ${level} (${pct}% of ${maxContext2.toLocaleString()} tok) \u2014 continuing`;
4527
+ const text = fatal ? `\u2717 compaction failed at ${level} \u2014 load ${pct}% of ${maxContext2.toLocaleString()} tok \u2014 FATAL` : `\u26A0 compaction failed at ${level} \u2014 load ${pct}% of ${maxContext2.toLocaleString()} tok \u2014 continuing`;
4215
4528
  dispatch({ type: "addEntry", entry: { kind: fatal ? "error" : "warn", text } });
4216
4529
  });
4217
4530
  return () => {
@@ -4282,16 +4595,16 @@ function App({
4282
4595
  streamFlushTimer = null;
4283
4596
  };
4284
4597
  const status = d.status();
4285
- for (const s of status.subagents) {
4286
- const meta = d.getSubagentMeta(s.id);
4598
+ for (const s2 of status.subagents) {
4599
+ const meta = d.getSubagentMeta(s2.id);
4287
4600
  dispatch({
4288
4601
  type: "fleetSpawn",
4289
- id: s.id,
4290
- name: meta?.name ?? s.name,
4602
+ id: s2.id,
4603
+ name: meta?.name ?? s2.name,
4291
4604
  provider: meta?.provider,
4292
4605
  model: meta?.model
4293
4606
  });
4294
- labelFor(s.id, meta?.name ?? s.name);
4607
+ labelFor(s2.id, meta?.name ?? s2.name);
4295
4608
  }
4296
4609
  dispatch({ type: "fleetCost", cost: d.snapshot().total.cost, input: d.snapshot().total.input, output: d.snapshot().total.output });
4297
4610
  const seen = new Set(Object.keys(status.subagents));
@@ -4706,6 +5019,10 @@ function App({
4706
5019
  }
4707
5020
  return;
4708
5021
  }
5022
+ if (key.ctrl && input === "p") {
5023
+ dispatch({ type: "autoPhaseMonitorToggle" });
5024
+ return;
5025
+ }
4709
5026
  if (state.autonomyPicker.open) {
4710
5027
  if (key.escape) {
4711
5028
  dispatch({ type: "autonomyPickerClose" });
@@ -4927,6 +5244,15 @@ function App({
4927
5244
  if (state.historyIndex > 0) dispatch({ type: "historyDown" });
4928
5245
  return;
4929
5246
  }
5247
+ if (key.ctrl && input === "p") {
5248
+ if (state.autoPhase) dispatch({ type: "autoPhaseMonitorToggle" });
5249
+ else {
5250
+ slashRegistry.dispatch("/autophase", agent.ctx).then((res) => {
5251
+ if (res?.message) dispatch({ type: "addEntry", entry: { kind: "info", text: res.message } });
5252
+ });
5253
+ }
5254
+ return;
5255
+ }
4930
5256
  if (key.ctrl && input === "a") {
4931
5257
  setDraft(buffer, 0);
4932
5258
  return;
@@ -5174,6 +5500,10 @@ function App({
5174
5500
  if (res?.message) {
5175
5501
  dispatch({ type: "addEntry", entry: { kind: "info", text: res.message } });
5176
5502
  }
5503
+ if (res?.metadata?.autoPhaseInit) {
5504
+ const m = res.metadata.autoPhaseInit;
5505
+ dispatch({ type: "autoPhaseInit", title: m.title });
5506
+ }
5177
5507
  const ctxModel = agent.ctx.model;
5178
5508
  if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
5179
5509
  const ctxProviderId = agent.ctx.provider?.id;
@@ -5413,8 +5743,16 @@ User message:
5413
5743
  totalTokens: state.fleetTokens,
5414
5744
  nowTick
5415
5745
  }
5416
- ) : null,
5417
- state.monitorOpen ? /* @__PURE__ */ jsx(
5746
+ ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
5747
+ PhaseMonitor,
5748
+ {
5749
+ phases: state.autoPhase.phases,
5750
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
5751
+ elapsedMs: state.autoPhase.elapsedMs,
5752
+ nowTick,
5753
+ onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
5754
+ }
5755
+ ) : state.monitorOpen ? /* @__PURE__ */ jsx(
5418
5756
  FleetMonitor,
5419
5757
  {
5420
5758
  entries: state.fleet,
@@ -5422,7 +5760,15 @@ User message:
5422
5760
  totalTokens: state.fleetTokens,
5423
5761
  nowTick
5424
5762
  }
5425
- ) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null
5763
+ ) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null,
5764
+ state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
5765
+ PhasePanel,
5766
+ {
5767
+ phases: state.autoPhase.phases,
5768
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
5769
+ nowTick
5770
+ }
5771
+ ) : null
5426
5772
  ] });
5427
5773
  }
5428
5774
  function renderRunningTools(running) {
@@ -5481,9 +5827,9 @@ async function runTui(opts) {
5481
5827
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
5482
5828
  const swallow = () => {
5483
5829
  };
5484
- for (const s of swallowSignals) {
5830
+ for (const s2 of swallowSignals) {
5485
5831
  try {
5486
- process.on(s, swallow);
5832
+ process.on(s2, swallow);
5487
5833
  } catch {
5488
5834
  }
5489
5835
  }
@@ -5502,13 +5848,13 @@ async function runTui(opts) {
5502
5848
  const signals = ["SIGTERM", "SIGHUP", "SIGINT"];
5503
5849
  const signalHandler = () => cleanup();
5504
5850
  const exitHandler = () => cleanup();
5505
- for (const s of signals) process.on(s, signalHandler);
5851
+ for (const s2 of signals) process.on(s2, signalHandler);
5506
5852
  process.on("exit", exitHandler);
5507
5853
  const detachListeners = () => {
5508
- for (const s of signals) process.off(s, signalHandler);
5509
- for (const s of swallowSignals) {
5854
+ for (const s2 of signals) process.off(s2, signalHandler);
5855
+ for (const s2 of swallowSignals) {
5510
5856
  try {
5511
- process.off(s, swallow);
5857
+ process.off(s2, swallow);
5512
5858
  } catch {
5513
5859
  }
5514
5860
  }
@@ -5551,6 +5897,7 @@ async function runTui(opts) {
5551
5897
  getParallelEngine: opts.getParallelEngine,
5552
5898
  subscribeEternalIteration: opts.subscribeEternalIteration,
5553
5899
  subscribeEternalStage: opts.subscribeEternalStage,
5900
+ subscribeAutoPhase: opts.subscribeAutoPhase,
5554
5901
  appVersion: opts.appVersion,
5555
5902
  provider: opts.provider,
5556
5903
  family: opts.family,