@wrongstack/tui 0.8.4 → 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) {
@@ -2066,11 +2239,11 @@ function stringOf(v) {
2066
2239
  function numOf(v) {
2067
2240
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2068
2241
  }
2069
- function tryParseJson(s) {
2070
- const t = s.trimStart();
2242
+ function tryParseJson(s2) {
2243
+ const t = s2.trimStart();
2071
2244
  if (!t.startsWith("{") && !t.startsWith("[")) return void 0;
2072
2245
  try {
2073
- return JSON.parse(s);
2246
+ return JSON.parse(s2);
2074
2247
  } catch {
2075
2248
  return void 0;
2076
2249
  }
@@ -2101,9 +2274,9 @@ function fmtBytes2(n) {
2101
2274
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
2102
2275
  return `${(n / (1024 * 1024)).toFixed(1)}MB`;
2103
2276
  }
2104
- function truncMid(s, max) {
2105
- if (s.length <= max) return s;
2106
- 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`;
2107
2280
  }
2108
2281
  function isHomeEnd(data) {
2109
2282
  if (data === "\x1B[H" || data === "\x1B[1~" || data === "\x1BOH" || data === "\x1B[7~")
@@ -2177,12 +2350,12 @@ function Input({
2177
2350
  hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
2178
2351
  ] });
2179
2352
  }
2180
- function fmtElapsed2(ms) {
2353
+ function fmtElapsed4(ms) {
2181
2354
  if (ms < 1e3) return `${ms}ms`;
2182
2355
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2183
2356
  const m = Math.floor(ms / 6e4);
2184
- const s = Math.floor(ms % 6e4 / 1e3);
2185
- 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`;
2186
2359
  }
2187
2360
  function fmtBytes3(n) {
2188
2361
  if (n < 1024) return `${n}B`;
@@ -2192,7 +2365,7 @@ function fmtRecentTool2(tool) {
2192
2365
  const status = tool.ok === false ? "fail" : "ok";
2193
2366
  const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
2194
2367
  const parts = [status, name];
2195
- if (typeof tool.durationMs === "number") parts.push(fmtElapsed2(tool.durationMs));
2368
+ if (typeof tool.durationMs === "number") parts.push(fmtElapsed4(tool.durationMs));
2196
2369
  if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes3(tool.outputBytes));
2197
2370
  if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
2198
2371
  return parts.join(" ");
@@ -2213,7 +2386,7 @@ function LiveActivityStrip({
2213
2386
  running.map((e) => {
2214
2387
  const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
2215
2388
  const taskElapsed = now - e.startedAt;
2216
- 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";
2217
2390
  const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
2218
2391
  const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
2219
2392
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
@@ -2227,7 +2400,7 @@ function LiveActivityStrip({
2227
2400
  "it ",
2228
2401
  e.toolCalls,
2229
2402
  "tc \xB7 ",
2230
- fmtElapsed2(taskElapsed)
2403
+ fmtElapsed4(taskElapsed)
2231
2404
  ] }),
2232
2405
  recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
2233
2406
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
@@ -2386,10 +2559,10 @@ async function walk(root, rel, depth, out) {
2386
2559
  }
2387
2560
  }
2388
2561
  }
2389
- function score(s, query) {
2390
- if (!query) return s.length;
2562
+ function score(s2, query) {
2563
+ if (!query) return s2.length;
2391
2564
  const ql = query.toLowerCase();
2392
- const sl = s.toLowerCase();
2565
+ const sl = s2.toLowerCase();
2393
2566
  let si = 0;
2394
2567
  let firstHit = -1;
2395
2568
  let lastHit = -1;
@@ -2402,7 +2575,7 @@ function score(s, query) {
2402
2575
  si++;
2403
2576
  }
2404
2577
  const span = lastHit - firstHit;
2405
- return span * 100 + firstHit * 2 + s.length;
2578
+ return span * 100 + firstHit * 2 + s2.length;
2406
2579
  }
2407
2580
  async function searchFiles(root, query, limit = 8) {
2408
2581
  const all = await loadIndex(root);
@@ -2556,8 +2729,8 @@ function renderList(queue) {
2556
2729
  }
2557
2730
  return lines.join("\n");
2558
2731
  }
2559
- function oneLine(s, max) {
2560
- const collapsed = s.replace(/\s+/g, " ").trim();
2732
+ function oneLine(s2, max) {
2733
+ const collapsed = s2.replace(/\s+/g, " ").trim();
2561
2734
  return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;
2562
2735
  }
2563
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";
@@ -3224,6 +3397,61 @@ function reducer(state, action) {
3224
3397
  case "goalSummary": {
3225
3398
  return { ...state, goalSummary: action.summary };
3226
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
+ }
3227
3455
  }
3228
3456
  }
3229
3457
  var PASTE_THRESHOLD_CHARS = 200;
@@ -3234,7 +3462,7 @@ function buildSteeringPreamble(snapshot, newDirection) {
3234
3462
  ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
3235
3463
  }
3236
3464
  if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
3237
- 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(", ");
3238
3466
  ctx.push(
3239
3467
  `- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
3240
3468
  );
@@ -3279,6 +3507,7 @@ function App({
3279
3507
  getParallelEngine,
3280
3508
  subscribeEternalIteration,
3281
3509
  subscribeEternalStage,
3510
+ subscribeAutoPhase,
3282
3511
  getSDDContext,
3283
3512
  onSDDOutput,
3284
3513
  appVersion,
@@ -3395,7 +3624,8 @@ function App({
3395
3624
  checkpoints: [],
3396
3625
  rewindOverlay: null,
3397
3626
  eternalStage: null,
3398
- goalSummary: null
3627
+ goalSummary: null,
3628
+ autoPhase: null
3399
3629
  });
3400
3630
  const builderRef = useRef(null);
3401
3631
  if (builderRef.current === null) {
@@ -3794,9 +4024,9 @@ function App({
3794
4024
  if (!text) {
3795
4025
  return { message: "Usage: /steer <new direction>" };
3796
4026
  }
3797
- const s = stateRef.current;
3798
- const runningTools = Array.from(s.runningTools.values()).map((t) => t.name);
3799
- 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 }));
3800
4030
  const subagentsTerminated = subagents.length;
3801
4031
  const partialAssistantText = streamingTextRef.current.slice(-1500);
3802
4032
  activeCtrlRef.current?.abort();
@@ -3804,7 +4034,7 @@ function App({
3804
4034
  type: "steerStart",
3805
4035
  snapshot: { runningTools, subagents, subagentsTerminated, partialAssistantText }
3806
4036
  });
3807
- const droppedCount = s.queue.length;
4037
+ const droppedCount = s2.queue.length;
3808
4038
  if (droppedCount > 0) dispatch({ type: "queueClear" });
3809
4039
  if (director && subagentsTerminated > 0) {
3810
4040
  const cap = new Promise((resolve) => {
@@ -3851,8 +4081,8 @@ function App({
3851
4081
  handleRewindTo(idx);
3852
4082
  return {};
3853
4083
  }
3854
- const s = stateRef.current;
3855
- if (s.checkpoints.length === 0) {
4084
+ const s2 = stateRef.current;
4085
+ if (s2.checkpoints.length === 0) {
3856
4086
  return { message: "No checkpoints in this session yet." };
3857
4087
  }
3858
4088
  dispatch({ type: "rewindOverlayOpen" });
@@ -4201,6 +4431,69 @@ function App({
4201
4431
  offRewound();
4202
4432
  };
4203
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]);
4204
4497
  useEffect(() => {
4205
4498
  const offFired = events.on("compaction.fired", (e) => {
4206
4499
  const { level, tokens, load, maxContext: maxContext2, report } = e;
@@ -4302,16 +4595,16 @@ function App({
4302
4595
  streamFlushTimer = null;
4303
4596
  };
4304
4597
  const status = d.status();
4305
- for (const s of status.subagents) {
4306
- const meta = d.getSubagentMeta(s.id);
4598
+ for (const s2 of status.subagents) {
4599
+ const meta = d.getSubagentMeta(s2.id);
4307
4600
  dispatch({
4308
4601
  type: "fleetSpawn",
4309
- id: s.id,
4310
- name: meta?.name ?? s.name,
4602
+ id: s2.id,
4603
+ name: meta?.name ?? s2.name,
4311
4604
  provider: meta?.provider,
4312
4605
  model: meta?.model
4313
4606
  });
4314
- labelFor(s.id, meta?.name ?? s.name);
4607
+ labelFor(s2.id, meta?.name ?? s2.name);
4315
4608
  }
4316
4609
  dispatch({ type: "fleetCost", cost: d.snapshot().total.cost, input: d.snapshot().total.input, output: d.snapshot().total.output });
4317
4610
  const seen = new Set(Object.keys(status.subagents));
@@ -4726,6 +5019,10 @@ function App({
4726
5019
  }
4727
5020
  return;
4728
5021
  }
5022
+ if (key.ctrl && input === "p") {
5023
+ dispatch({ type: "autoPhaseMonitorToggle" });
5024
+ return;
5025
+ }
4729
5026
  if (state.autonomyPicker.open) {
4730
5027
  if (key.escape) {
4731
5028
  dispatch({ type: "autonomyPickerClose" });
@@ -4947,6 +5244,15 @@ function App({
4947
5244
  if (state.historyIndex > 0) dispatch({ type: "historyDown" });
4948
5245
  return;
4949
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
+ }
4950
5256
  if (key.ctrl && input === "a") {
4951
5257
  setDraft(buffer, 0);
4952
5258
  return;
@@ -5194,6 +5500,10 @@ function App({
5194
5500
  if (res?.message) {
5195
5501
  dispatch({ type: "addEntry", entry: { kind: "info", text: res.message } });
5196
5502
  }
5503
+ if (res?.metadata?.autoPhaseInit) {
5504
+ const m = res.metadata.autoPhaseInit;
5505
+ dispatch({ type: "autoPhaseInit", title: m.title });
5506
+ }
5197
5507
  const ctxModel = agent.ctx.model;
5198
5508
  if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
5199
5509
  const ctxProviderId = agent.ctx.provider?.id;
@@ -5433,6 +5743,15 @@ User message:
5433
5743
  totalTokens: state.fleetTokens,
5434
5744
  nowTick
5435
5745
  }
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
+ }
5436
5755
  ) : state.monitorOpen ? /* @__PURE__ */ jsx(
5437
5756
  FleetMonitor,
5438
5757
  {
@@ -5441,7 +5760,15 @@ User message:
5441
5760
  totalTokens: state.fleetTokens,
5442
5761
  nowTick
5443
5762
  }
5444
- ) : 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
5445
5772
  ] });
5446
5773
  }
5447
5774
  function renderRunningTools(running) {
@@ -5500,9 +5827,9 @@ async function runTui(opts) {
5500
5827
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
5501
5828
  const swallow = () => {
5502
5829
  };
5503
- for (const s of swallowSignals) {
5830
+ for (const s2 of swallowSignals) {
5504
5831
  try {
5505
- process.on(s, swallow);
5832
+ process.on(s2, swallow);
5506
5833
  } catch {
5507
5834
  }
5508
5835
  }
@@ -5521,13 +5848,13 @@ async function runTui(opts) {
5521
5848
  const signals = ["SIGTERM", "SIGHUP", "SIGINT"];
5522
5849
  const signalHandler = () => cleanup();
5523
5850
  const exitHandler = () => cleanup();
5524
- for (const s of signals) process.on(s, signalHandler);
5851
+ for (const s2 of signals) process.on(s2, signalHandler);
5525
5852
  process.on("exit", exitHandler);
5526
5853
  const detachListeners = () => {
5527
- for (const s of signals) process.off(s, signalHandler);
5528
- for (const s of swallowSignals) {
5854
+ for (const s2 of signals) process.off(s2, signalHandler);
5855
+ for (const s2 of swallowSignals) {
5529
5856
  try {
5530
- process.off(s, swallow);
5857
+ process.off(s2, swallow);
5531
5858
  } catch {
5532
5859
  }
5533
5860
  }
@@ -5570,6 +5897,7 @@ async function runTui(opts) {
5570
5897
  getParallelEngine: opts.getParallelEngine,
5571
5898
  subscribeEternalIteration: opts.subscribeEternalIteration,
5572
5899
  subscribeEternalStage: opts.subscribeEternalStage,
5900
+ subscribeAutoPhase: opts.subscribeAutoPhase,
5573
5901
  appVersion: opts.appVersion,
5574
5902
  provider: opts.provider,
5575
5903
  family: opts.family,