@wrongstack/tui 0.8.4 → 0.8.6
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.d.ts +6 -0
- package/dist/index.js +738 -56
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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(
|
|
20
|
-
return
|
|
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
|
|
680
|
+
const s2 = totalSec % 60;
|
|
681
681
|
if (h > 0) {
|
|
682
|
-
return `${h}:${pad2(m)}:${pad2(
|
|
682
|
+
return `${h}:${pad2(m)}:${pad2(s2)}`;
|
|
683
683
|
}
|
|
684
|
-
return `${pad2(m)}:${pad2(
|
|
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
|
|
754
|
+
const s2 = STATUS[e.status];
|
|
755
755
|
events.push({
|
|
756
756
|
at: e.lastEventAt,
|
|
757
|
-
icon:
|
|
758
|
-
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
|
|
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:
|
|
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:
|
|
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(
|
|
874
|
-
const oneLine2 =
|
|
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
|
|
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:
|
|
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,377 @@ 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 st2 = 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: st2.color, children: st2.icon }),
|
|
1159
|
+
/* @__PURE__ */ jsx(Text, { children: phase.name.slice(0, 14).padEnd(14) }),
|
|
1160
|
+
/* @__PURE__ */ jsx(Text, { color: st2.color, dimColor: true, children: st2.icon === "\u25CF" ? phase.status : "" }),
|
|
1161
|
+
progress ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1162
|
+
" ",
|
|
1163
|
+
progress
|
|
1164
|
+
] }) : null,
|
|
1165
|
+
elapsed && st2.icon === "\u25CF" ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1166
|
+
" ",
|
|
1167
|
+
elapsed
|
|
1168
|
+
] }) : null
|
|
1169
|
+
] }, phaseKey);
|
|
1170
|
+
})
|
|
1171
|
+
]
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
var fmtElapsed4 = (ms) => {
|
|
1176
|
+
const s2 = Math.floor(ms / 1e3);
|
|
1177
|
+
const m = Math.floor(s2 / 60);
|
|
1178
|
+
const h = Math.floor(m / 60);
|
|
1179
|
+
if (h > 0) return `${h}:${String(m % 60).padStart(2, "0")}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1180
|
+
if (m > 0) return `${m}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1181
|
+
return `${s2}s`;
|
|
1182
|
+
};
|
|
1183
|
+
var STATUS4 = {
|
|
1184
|
+
allocating: { icon: "\u25CB", color: "gray" },
|
|
1185
|
+
active: { icon: "\u25CF", color: "yellow" },
|
|
1186
|
+
committing: { icon: "\u25D0", color: "cyan" },
|
|
1187
|
+
merging: { icon: "\u21E1", color: "blue" },
|
|
1188
|
+
merged: { icon: "\u2713", color: "green" },
|
|
1189
|
+
"needs-review": { icon: "\u26A0", color: "magenta" },
|
|
1190
|
+
failed: { icon: "\u2717", color: "red" }
|
|
1191
|
+
};
|
|
1192
|
+
function st(status) {
|
|
1193
|
+
return STATUS4[status] ?? { icon: "?", color: "white" };
|
|
1194
|
+
}
|
|
1195
|
+
function WorktreePanel({
|
|
1196
|
+
worktrees,
|
|
1197
|
+
nowTick
|
|
1198
|
+
}) {
|
|
1199
|
+
const list = Object.values(worktrees);
|
|
1200
|
+
if (list.length === 0) return null;
|
|
1201
|
+
const active = list.filter((w) => w.status === "active" || w.status === "committing" || w.status === "merging").length;
|
|
1202
|
+
const merged = list.filter((w) => w.status === "merged").length;
|
|
1203
|
+
const failed = list.filter((w) => w.status === "failed" || w.status === "needs-review").length;
|
|
1204
|
+
return /* @__PURE__ */ jsxs(
|
|
1205
|
+
Box,
|
|
1206
|
+
{
|
|
1207
|
+
flexDirection: "column",
|
|
1208
|
+
paddingX: 1,
|
|
1209
|
+
borderStyle: "single",
|
|
1210
|
+
borderTop: false,
|
|
1211
|
+
borderBottom: false,
|
|
1212
|
+
borderLeft: false,
|
|
1213
|
+
borderRight: false,
|
|
1214
|
+
children: [
|
|
1215
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
|
|
1216
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Worktrees" }),
|
|
1217
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
1218
|
+
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
1219
|
+
"\u25B6",
|
|
1220
|
+
active
|
|
1221
|
+
] }),
|
|
1222
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
1223
|
+
"\u2713",
|
|
1224
|
+
merged
|
|
1225
|
+
] }),
|
|
1226
|
+
failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1227
|
+
"\u2717",
|
|
1228
|
+
failed
|
|
1229
|
+
] }) : null,
|
|
1230
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1231
|
+
"\xB7 ",
|
|
1232
|
+
list.length,
|
|
1233
|
+
" total \xB7 Ctrl+W for details"
|
|
1234
|
+
] })
|
|
1235
|
+
] }),
|
|
1236
|
+
list.map((w) => {
|
|
1237
|
+
const s2 = st(w.status);
|
|
1238
|
+
const conflict = w.status === "needs-review";
|
|
1239
|
+
const elapsed = w.allocatedAt ? fmtElapsed4(nowTick - w.allocatedAt) : "";
|
|
1240
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1241
|
+
/* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.icon }),
|
|
1242
|
+
/* @__PURE__ */ jsx(Text, { children: w.branch.replace(/^wstack\/ap\//, "").slice(0, 18).padEnd(18) }),
|
|
1243
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: w.ownerLabel.slice(0, 12) }),
|
|
1244
|
+
conflict ? /* @__PURE__ */ jsx(Text, { color: "magenta", children: " CONFLICT" }) : /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1245
|
+
" +",
|
|
1246
|
+
w.insertions,
|
|
1247
|
+
"/-",
|
|
1248
|
+
w.deletions,
|
|
1249
|
+
" ",
|
|
1250
|
+
w.files,
|
|
1251
|
+
"f"
|
|
1252
|
+
] }),
|
|
1253
|
+
elapsed && (w.status === "active" || w.status === "committing") ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1254
|
+
" ",
|
|
1255
|
+
elapsed
|
|
1256
|
+
] }) : null
|
|
1257
|
+
] }, w.branch);
|
|
1258
|
+
})
|
|
1259
|
+
]
|
|
1260
|
+
}
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
var fmtElapsed5 = (ms) => {
|
|
1264
|
+
const s2 = Math.floor(ms / 1e3);
|
|
1265
|
+
const m = Math.floor(s2 / 60);
|
|
1266
|
+
const h = Math.floor(m / 60);
|
|
1267
|
+
if (h > 0) return `${h}:${String(m % 60).padStart(2, "0")}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1268
|
+
if (m > 0) return `${m}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1269
|
+
return `${s2}s`;
|
|
1270
|
+
};
|
|
1271
|
+
var WT_STATUS = {
|
|
1272
|
+
allocating: { icon: "\u25CB", color: "gray", label: "allocating" },
|
|
1273
|
+
active: { icon: "\u25CF", color: "yellow", label: "active" },
|
|
1274
|
+
committing: { icon: "\u25D0", color: "cyan", label: "committing" },
|
|
1275
|
+
merging: { icon: "\u21E1", color: "blue", label: "merging" },
|
|
1276
|
+
merged: { icon: "\u2713", color: "green", label: "merged" },
|
|
1277
|
+
"needs-review": { icon: "\u26A0", color: "magenta", label: "needs-review" },
|
|
1278
|
+
failed: { icon: "\u2717", color: "red", label: "failed" }
|
|
1279
|
+
};
|
|
1280
|
+
function fmt(s2) {
|
|
1281
|
+
return WT_STATUS[s2] ?? { icon: "?", color: "white", label: s2 };
|
|
1282
|
+
}
|
|
1283
|
+
function WorktreeMonitor({
|
|
1284
|
+
worktrees,
|
|
1285
|
+
baseBranch,
|
|
1286
|
+
nowTick,
|
|
1287
|
+
onClose
|
|
1288
|
+
}) {
|
|
1289
|
+
useInput((_, key) => {
|
|
1290
|
+
if (key.escape) onClose();
|
|
1291
|
+
});
|
|
1292
|
+
const list = Object.values(worktrees);
|
|
1293
|
+
const active = list.filter((w) => ["active", "committing", "merging"].includes(w.status)).length;
|
|
1294
|
+
const merged = list.filter((w) => w.status === "merged").length;
|
|
1295
|
+
const failed = list.filter((w) => w.status === "failed" || w.status === "needs-review").length;
|
|
1296
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: [
|
|
1297
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginBottom: 1, children: [
|
|
1298
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "WORKTREE MONITOR" }),
|
|
1299
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
1300
|
+
baseBranch ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1301
|
+
"base ",
|
|
1302
|
+
baseBranch
|
|
1303
|
+
] }) : null,
|
|
1304
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
1305
|
+
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
1306
|
+
"\u25B6",
|
|
1307
|
+
active
|
|
1308
|
+
] }),
|
|
1309
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
1310
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
1311
|
+
"\u2713",
|
|
1312
|
+
merged
|
|
1313
|
+
] }),
|
|
1314
|
+
failed > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1315
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
1316
|
+
/* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1317
|
+
"\u2717",
|
|
1318
|
+
failed
|
|
1319
|
+
] })
|
|
1320
|
+
] }) : null,
|
|
1321
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+W / Esc to close" })
|
|
1322
|
+
] }),
|
|
1323
|
+
list.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No worktrees. They appear when AutoPhase runs with isolation on." }) : list.map((w) => {
|
|
1324
|
+
const s2 = fmt(w.status);
|
|
1325
|
+
const short = w.branch.replace(/^wstack\/ap\//, "");
|
|
1326
|
+
const elapsed = w.allocatedAt ? fmtElapsed5(nowTick - w.allocatedAt) : "\u2014";
|
|
1327
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
1328
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1329
|
+
/* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
|
|
1330
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: short }),
|
|
1331
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
1332
|
+
/* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.label }),
|
|
1333
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1334
|
+
"\xB7 elapsed ",
|
|
1335
|
+
elapsed
|
|
1336
|
+
] })
|
|
1337
|
+
] }),
|
|
1338
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginLeft: 2, children: [
|
|
1339
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1340
|
+
w.baseBranch ?? baseBranch ?? "base",
|
|
1341
|
+
" \u2192 ",
|
|
1342
|
+
short
|
|
1343
|
+
] }),
|
|
1344
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1345
|
+
"\xB7 owner: ",
|
|
1346
|
+
w.ownerLabel
|
|
1347
|
+
] })
|
|
1348
|
+
] }),
|
|
1349
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginLeft: 2, children: [
|
|
1350
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
1351
|
+
"+",
|
|
1352
|
+
w.insertions
|
|
1353
|
+
] }),
|
|
1354
|
+
/* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1355
|
+
"-",
|
|
1356
|
+
w.deletions
|
|
1357
|
+
] }),
|
|
1358
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1359
|
+
"\xB7 ",
|
|
1360
|
+
w.files,
|
|
1361
|
+
" files"
|
|
1362
|
+
] })
|
|
1363
|
+
] }),
|
|
1364
|
+
w.conflictFiles && w.conflictFiles.length > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
|
|
1365
|
+
"conflicts: ",
|
|
1366
|
+
w.conflictFiles.join(", ")
|
|
1367
|
+
] }) }) : null
|
|
1368
|
+
] }, w.branch);
|
|
1369
|
+
}),
|
|
1370
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Esc close \xB7 merge conflicts with /worktree merge <branch>" }) })
|
|
1371
|
+
] });
|
|
1372
|
+
}
|
|
1002
1373
|
|
|
1003
1374
|
// src/markdown-table.ts
|
|
1004
1375
|
function renderMarkdownTables(text, maxWidth) {
|
|
@@ -1095,7 +1466,7 @@ function computeWidths(allRows, cols, maxWidth) {
|
|
|
1095
1466
|
if (w > natural[c]) natural[c] = Math.min(total + 1, w);
|
|
1096
1467
|
}
|
|
1097
1468
|
}
|
|
1098
|
-
const sumNatural = natural.reduce((
|
|
1469
|
+
const sumNatural = natural.reduce((s2, n) => s2 + n, 0);
|
|
1099
1470
|
if (sumNatural <= avail) return natural;
|
|
1100
1471
|
const widths = natural.slice();
|
|
1101
1472
|
let sum = sumNatural;
|
|
@@ -1116,9 +1487,9 @@ function computeWidths(allRows, cols, maxWidth) {
|
|
|
1116
1487
|
return widths;
|
|
1117
1488
|
}
|
|
1118
1489
|
var MIN_COL_WIDTH = 4;
|
|
1119
|
-
function longestWord(
|
|
1490
|
+
function longestWord(s2) {
|
|
1120
1491
|
let max = 0;
|
|
1121
|
-
for (const w of
|
|
1492
|
+
for (const w of s2.split(/\s+/)) if (w.length > max) max = w.length;
|
|
1122
1493
|
return max;
|
|
1123
1494
|
}
|
|
1124
1495
|
function border(left, mid, right, widths) {
|
|
@@ -2066,11 +2437,11 @@ function stringOf(v) {
|
|
|
2066
2437
|
function numOf(v) {
|
|
2067
2438
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
2068
2439
|
}
|
|
2069
|
-
function tryParseJson(
|
|
2070
|
-
const t =
|
|
2440
|
+
function tryParseJson(s2) {
|
|
2441
|
+
const t = s2.trimStart();
|
|
2071
2442
|
if (!t.startsWith("{") && !t.startsWith("[")) return void 0;
|
|
2072
2443
|
try {
|
|
2073
|
-
return JSON.parse(
|
|
2444
|
+
return JSON.parse(s2);
|
|
2074
2445
|
} catch {
|
|
2075
2446
|
return void 0;
|
|
2076
2447
|
}
|
|
@@ -2101,9 +2472,9 @@ function fmtBytes2(n) {
|
|
|
2101
2472
|
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
2102
2473
|
return `${(n / (1024 * 1024)).toFixed(1)}MB`;
|
|
2103
2474
|
}
|
|
2104
|
-
function truncMid(
|
|
2105
|
-
if (
|
|
2106
|
-
return `${
|
|
2475
|
+
function truncMid(s2, max) {
|
|
2476
|
+
if (s2.length <= max) return s2;
|
|
2477
|
+
return `${s2.slice(0, max - 1)}\u2026`;
|
|
2107
2478
|
}
|
|
2108
2479
|
function isHomeEnd(data) {
|
|
2109
2480
|
if (data === "\x1B[H" || data === "\x1B[1~" || data === "\x1BOH" || data === "\x1B[7~")
|
|
@@ -2177,12 +2548,12 @@ function Input({
|
|
|
2177
2548
|
hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
|
|
2178
2549
|
] });
|
|
2179
2550
|
}
|
|
2180
|
-
function
|
|
2551
|
+
function fmtElapsed6(ms) {
|
|
2181
2552
|
if (ms < 1e3) return `${ms}ms`;
|
|
2182
2553
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2183
2554
|
const m = Math.floor(ms / 6e4);
|
|
2184
|
-
const
|
|
2185
|
-
return `${m}m${
|
|
2555
|
+
const s2 = Math.floor(ms % 6e4 / 1e3);
|
|
2556
|
+
return `${m}m${s2.toString().padStart(2, "0")}s`;
|
|
2186
2557
|
}
|
|
2187
2558
|
function fmtBytes3(n) {
|
|
2188
2559
|
if (n < 1024) return `${n}B`;
|
|
@@ -2192,7 +2563,7 @@ function fmtRecentTool2(tool) {
|
|
|
2192
2563
|
const status = tool.ok === false ? "fail" : "ok";
|
|
2193
2564
|
const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
|
|
2194
2565
|
const parts = [status, name];
|
|
2195
|
-
if (typeof tool.durationMs === "number") parts.push(
|
|
2566
|
+
if (typeof tool.durationMs === "number") parts.push(fmtElapsed6(tool.durationMs));
|
|
2196
2567
|
if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes3(tool.outputBytes));
|
|
2197
2568
|
if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
|
|
2198
2569
|
return parts.join(" ");
|
|
@@ -2213,7 +2584,7 @@ function LiveActivityStrip({
|
|
|
2213
2584
|
running.map((e) => {
|
|
2214
2585
|
const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
|
|
2215
2586
|
const taskElapsed = now - e.startedAt;
|
|
2216
|
-
const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${
|
|
2587
|
+
const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed6(toolElapsed)})` : "idle between tools";
|
|
2217
2588
|
const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
|
|
2218
2589
|
const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
|
|
2219
2590
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
@@ -2227,7 +2598,7 @@ function LiveActivityStrip({
|
|
|
2227
2598
|
"it ",
|
|
2228
2599
|
e.toolCalls,
|
|
2229
2600
|
"tc \xB7 ",
|
|
2230
|
-
|
|
2601
|
+
fmtElapsed6(taskElapsed)
|
|
2231
2602
|
] }),
|
|
2232
2603
|
recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2233
2604
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
|
|
@@ -2386,10 +2757,10 @@ async function walk(root, rel, depth, out) {
|
|
|
2386
2757
|
}
|
|
2387
2758
|
}
|
|
2388
2759
|
}
|
|
2389
|
-
function score(
|
|
2390
|
-
if (!query) return
|
|
2760
|
+
function score(s2, query) {
|
|
2761
|
+
if (!query) return s2.length;
|
|
2391
2762
|
const ql = query.toLowerCase();
|
|
2392
|
-
const sl =
|
|
2763
|
+
const sl = s2.toLowerCase();
|
|
2393
2764
|
let si = 0;
|
|
2394
2765
|
let firstHit = -1;
|
|
2395
2766
|
let lastHit = -1;
|
|
@@ -2402,7 +2773,7 @@ function score(s, query) {
|
|
|
2402
2773
|
si++;
|
|
2403
2774
|
}
|
|
2404
2775
|
const span = lastHit - firstHit;
|
|
2405
|
-
return span * 100 + firstHit * 2 +
|
|
2776
|
+
return span * 100 + firstHit * 2 + s2.length;
|
|
2406
2777
|
}
|
|
2407
2778
|
async function searchFiles(root, query, limit = 8) {
|
|
2408
2779
|
const all = await loadIndex(root);
|
|
@@ -2556,8 +2927,8 @@ function renderList(queue) {
|
|
|
2556
2927
|
}
|
|
2557
2928
|
return lines.join("\n");
|
|
2558
2929
|
}
|
|
2559
|
-
function oneLine(
|
|
2560
|
-
const collapsed =
|
|
2930
|
+
function oneLine(s2, max) {
|
|
2931
|
+
const collapsed = s2.replace(/\s+/g, " ").trim();
|
|
2561
2932
|
return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;
|
|
2562
2933
|
}
|
|
2563
2934
|
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 +3595,89 @@ function reducer(state, action) {
|
|
|
3224
3595
|
case "goalSummary": {
|
|
3225
3596
|
return { ...state, goalSummary: action.summary };
|
|
3226
3597
|
}
|
|
3598
|
+
case "autoPhaseInit": {
|
|
3599
|
+
return {
|
|
3600
|
+
...state,
|
|
3601
|
+
autoPhase: {
|
|
3602
|
+
title: action.title,
|
|
3603
|
+
phases: {},
|
|
3604
|
+
runningPhaseIds: [],
|
|
3605
|
+
elapsedMs: 0,
|
|
3606
|
+
monitorOpen: false
|
|
3607
|
+
}
|
|
3608
|
+
};
|
|
3609
|
+
}
|
|
3610
|
+
case "autoPhasePhaseUpdate": {
|
|
3611
|
+
const existing = state.autoPhase ?? {
|
|
3612
|
+
title: "AutoPhase",
|
|
3613
|
+
phases: {},
|
|
3614
|
+
runningPhaseIds: [],
|
|
3615
|
+
elapsedMs: 0,
|
|
3616
|
+
monitorOpen: false
|
|
3617
|
+
};
|
|
3618
|
+
return {
|
|
3619
|
+
...state,
|
|
3620
|
+
autoPhase: {
|
|
3621
|
+
...existing,
|
|
3622
|
+
phases: {
|
|
3623
|
+
...existing.phases,
|
|
3624
|
+
[action.phaseId]: {
|
|
3625
|
+
name: action.name,
|
|
3626
|
+
status: action.status,
|
|
3627
|
+
completedTasks: action.completedTasks,
|
|
3628
|
+
totalTasks: action.totalTasks,
|
|
3629
|
+
startedAt: action.startedAt
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
};
|
|
3634
|
+
}
|
|
3635
|
+
case "autoPhaseRunningPhases": {
|
|
3636
|
+
if (!state.autoPhase) return state;
|
|
3637
|
+
return {
|
|
3638
|
+
...state,
|
|
3639
|
+
autoPhase: { ...state.autoPhase, runningPhaseIds: action.phaseIds }
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3642
|
+
case "autoPhaseElapsed": {
|
|
3643
|
+
if (!state.autoPhase) return state;
|
|
3644
|
+
return { ...state, autoPhase: { ...state.autoPhase, elapsedMs: action.ms } };
|
|
3645
|
+
}
|
|
3646
|
+
case "autoPhaseMonitorToggle": {
|
|
3647
|
+
if (!state.autoPhase) return state;
|
|
3648
|
+
return { ...state, autoPhase: { ...state.autoPhase, monitorOpen: !state.autoPhase.monitorOpen } };
|
|
3649
|
+
}
|
|
3650
|
+
case "autoPhaseReset": {
|
|
3651
|
+
return { ...state, autoPhase: null };
|
|
3652
|
+
}
|
|
3653
|
+
case "worktreeUpsert": {
|
|
3654
|
+
const prev = state.worktrees[action.handleId];
|
|
3655
|
+
const merged = {
|
|
3656
|
+
branch: "",
|
|
3657
|
+
ownerLabel: "",
|
|
3658
|
+
status: "active",
|
|
3659
|
+
insertions: 0,
|
|
3660
|
+
deletions: 0,
|
|
3661
|
+
files: 0,
|
|
3662
|
+
allocatedAt: Date.now(),
|
|
3663
|
+
...prev,
|
|
3664
|
+
...action.row
|
|
3665
|
+
};
|
|
3666
|
+
return {
|
|
3667
|
+
...state,
|
|
3668
|
+
worktrees: { ...state.worktrees, [action.handleId]: merged },
|
|
3669
|
+
worktreeBase: action.baseBranch ?? state.worktreeBase
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
case "worktreeRemove": {
|
|
3673
|
+
if (!state.worktrees[action.handleId]) return state;
|
|
3674
|
+
const next = { ...state.worktrees };
|
|
3675
|
+
delete next[action.handleId];
|
|
3676
|
+
return { ...state, worktrees: next };
|
|
3677
|
+
}
|
|
3678
|
+
case "worktreeMonitorToggle": {
|
|
3679
|
+
return { ...state, worktreeMonitorOpen: !state.worktreeMonitorOpen };
|
|
3680
|
+
}
|
|
3227
3681
|
}
|
|
3228
3682
|
}
|
|
3229
3683
|
var PASTE_THRESHOLD_CHARS = 200;
|
|
@@ -3234,7 +3688,7 @@ function buildSteeringPreamble(snapshot, newDirection) {
|
|
|
3234
3688
|
ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
|
|
3235
3689
|
}
|
|
3236
3690
|
if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
|
|
3237
|
-
const subDetails = snapshot.subagents.map((
|
|
3691
|
+
const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
|
|
3238
3692
|
ctx.push(
|
|
3239
3693
|
`- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
|
|
3240
3694
|
);
|
|
@@ -3279,6 +3733,7 @@ function App({
|
|
|
3279
3733
|
getParallelEngine,
|
|
3280
3734
|
subscribeEternalIteration,
|
|
3281
3735
|
subscribeEternalStage,
|
|
3736
|
+
subscribeAutoPhase,
|
|
3282
3737
|
getSDDContext,
|
|
3283
3738
|
onSDDOutput,
|
|
3284
3739
|
appVersion,
|
|
@@ -3395,7 +3850,10 @@ function App({
|
|
|
3395
3850
|
checkpoints: [],
|
|
3396
3851
|
rewindOverlay: null,
|
|
3397
3852
|
eternalStage: null,
|
|
3398
|
-
goalSummary: null
|
|
3853
|
+
goalSummary: null,
|
|
3854
|
+
autoPhase: null,
|
|
3855
|
+
worktrees: {},
|
|
3856
|
+
worktreeMonitorOpen: false
|
|
3399
3857
|
});
|
|
3400
3858
|
const builderRef = useRef(null);
|
|
3401
3859
|
if (builderRef.current === null) {
|
|
@@ -3794,9 +4252,9 @@ function App({
|
|
|
3794
4252
|
if (!text) {
|
|
3795
4253
|
return { message: "Usage: /steer <new direction>" };
|
|
3796
4254
|
}
|
|
3797
|
-
const
|
|
3798
|
-
const runningTools = Array.from(
|
|
3799
|
-
const subagents = Object.values(
|
|
4255
|
+
const s2 = stateRef.current;
|
|
4256
|
+
const runningTools = Array.from(s2.runningTools.values()).map((t) => t.name);
|
|
4257
|
+
const subagents = Object.values(s2.fleet).filter((e) => e.status === "running").map((e) => ({ label: e.name, status: e.status, tool: e.currentTool?.name }));
|
|
3800
4258
|
const subagentsTerminated = subagents.length;
|
|
3801
4259
|
const partialAssistantText = streamingTextRef.current.slice(-1500);
|
|
3802
4260
|
activeCtrlRef.current?.abort();
|
|
@@ -3804,7 +4262,7 @@ function App({
|
|
|
3804
4262
|
type: "steerStart",
|
|
3805
4263
|
snapshot: { runningTools, subagents, subagentsTerminated, partialAssistantText }
|
|
3806
4264
|
});
|
|
3807
|
-
const droppedCount =
|
|
4265
|
+
const droppedCount = s2.queue.length;
|
|
3808
4266
|
if (droppedCount > 0) dispatch({ type: "queueClear" });
|
|
3809
4267
|
if (director && subagentsTerminated > 0) {
|
|
3810
4268
|
const cap = new Promise((resolve) => {
|
|
@@ -3851,8 +4309,8 @@ function App({
|
|
|
3851
4309
|
handleRewindTo(idx);
|
|
3852
4310
|
return {};
|
|
3853
4311
|
}
|
|
3854
|
-
const
|
|
3855
|
-
if (
|
|
4312
|
+
const s2 = stateRef.current;
|
|
4313
|
+
if (s2.checkpoints.length === 0) {
|
|
3856
4314
|
return { message: "No checkpoints in this session yet." };
|
|
3857
4315
|
}
|
|
3858
4316
|
dispatch({ type: "rewindOverlayOpen" });
|
|
@@ -4201,6 +4659,108 @@ function App({
|
|
|
4201
4659
|
offRewound();
|
|
4202
4660
|
};
|
|
4203
4661
|
}, [events, onClearHistory]);
|
|
4662
|
+
useEffect(() => {
|
|
4663
|
+
if (!subscribeAutoPhase) return;
|
|
4664
|
+
const handler = (event, payload) => {
|
|
4665
|
+
switch (event) {
|
|
4666
|
+
case "phase.started": {
|
|
4667
|
+
const p = payload;
|
|
4668
|
+
dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status: "running", completedTasks: 0, totalTasks: 0, startedAt: Date.now() });
|
|
4669
|
+
break;
|
|
4670
|
+
}
|
|
4671
|
+
case "phase.completed": {
|
|
4672
|
+
const p = payload;
|
|
4673
|
+
dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status: "completed", completedTasks: 0, totalTasks: 0 });
|
|
4674
|
+
break;
|
|
4675
|
+
}
|
|
4676
|
+
case "phase.failed": {
|
|
4677
|
+
const p = payload;
|
|
4678
|
+
dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status: "failed", completedTasks: 0, totalTasks: 0 });
|
|
4679
|
+
break;
|
|
4680
|
+
}
|
|
4681
|
+
case "phase.statusChange": {
|
|
4682
|
+
const p = payload;
|
|
4683
|
+
const status = p.to === "running" ? "running" : p.to;
|
|
4684
|
+
dispatch({ type: "autoPhasePhaseUpdate", phaseId: p.phaseId, name: p.name, status, completedTasks: 0, totalTasks: 0 });
|
|
4685
|
+
break;
|
|
4686
|
+
}
|
|
4687
|
+
case "phase.taskCompleted": {
|
|
4688
|
+
const p = payload;
|
|
4689
|
+
const existing = stateRef.current.autoPhase?.phases[p.phaseId];
|
|
4690
|
+
if (existing) {
|
|
4691
|
+
dispatch({
|
|
4692
|
+
type: "autoPhasePhaseUpdate",
|
|
4693
|
+
phaseId: p.phaseId,
|
|
4694
|
+
name: existing.name,
|
|
4695
|
+
status: existing.status,
|
|
4696
|
+
completedTasks: existing.completedTasks + 1,
|
|
4697
|
+
totalTasks: existing.totalTasks
|
|
4698
|
+
});
|
|
4699
|
+
}
|
|
4700
|
+
break;
|
|
4701
|
+
}
|
|
4702
|
+
case "autonomous.tick": {
|
|
4703
|
+
const p = payload;
|
|
4704
|
+
dispatch({ type: "autoPhaseRunningPhases", phaseIds: p.activePhases.map((ph) => ph.id) });
|
|
4705
|
+
const ap = stateRef.current.autoPhase;
|
|
4706
|
+
if (ap) {
|
|
4707
|
+
const firstPhase = ap.phases[Object.keys(ap.phases)[0] ?? ""];
|
|
4708
|
+
const elapsed = ap.elapsedMs > 0 ? ap.elapsedMs + 1e3 : Date.now() - (firstPhase?.startedAt ?? Date.now());
|
|
4709
|
+
dispatch({ type: "autoPhaseElapsed", ms: elapsed });
|
|
4710
|
+
}
|
|
4711
|
+
break;
|
|
4712
|
+
}
|
|
4713
|
+
case "graph.completed": {
|
|
4714
|
+
dispatch({ type: "autoPhaseReset" });
|
|
4715
|
+
break;
|
|
4716
|
+
}
|
|
4717
|
+
case "graph.failed": {
|
|
4718
|
+
dispatch({ type: "autoPhaseReset" });
|
|
4719
|
+
break;
|
|
4720
|
+
}
|
|
4721
|
+
case "worktree.allocated": {
|
|
4722
|
+
const p = payload;
|
|
4723
|
+
dispatch({
|
|
4724
|
+
type: "worktreeUpsert",
|
|
4725
|
+
handleId: p.handleId,
|
|
4726
|
+
baseBranch: p.baseBranch,
|
|
4727
|
+
row: { branch: p.branch, ownerLabel: p.ownerLabel, baseBranch: p.baseBranch, status: "active", allocatedAt: Date.now() }
|
|
4728
|
+
});
|
|
4729
|
+
break;
|
|
4730
|
+
}
|
|
4731
|
+
case "worktree.committed": {
|
|
4732
|
+
const p = payload;
|
|
4733
|
+
dispatch({
|
|
4734
|
+
type: "worktreeUpsert",
|
|
4735
|
+
handleId: p.handleId,
|
|
4736
|
+
row: { insertions: p.insertions, deletions: p.deletions, files: p.files, status: "committing" }
|
|
4737
|
+
});
|
|
4738
|
+
break;
|
|
4739
|
+
}
|
|
4740
|
+
case "worktree.merged": {
|
|
4741
|
+
const p = payload;
|
|
4742
|
+
dispatch({ type: "worktreeUpsert", handleId: p.handleId, row: { status: "merged" } });
|
|
4743
|
+
break;
|
|
4744
|
+
}
|
|
4745
|
+
case "worktree.conflict": {
|
|
4746
|
+
const p = payload;
|
|
4747
|
+
dispatch({ type: "worktreeUpsert", handleId: p.handleId, row: { status: "needs-review", conflictFiles: p.conflictFiles } });
|
|
4748
|
+
break;
|
|
4749
|
+
}
|
|
4750
|
+
case "worktree.failed": {
|
|
4751
|
+
const p = payload;
|
|
4752
|
+
dispatch({ type: "worktreeUpsert", handleId: p.handleId, row: { status: "failed" } });
|
|
4753
|
+
break;
|
|
4754
|
+
}
|
|
4755
|
+
case "worktree.released": {
|
|
4756
|
+
const p = payload;
|
|
4757
|
+
if (!p.kept) dispatch({ type: "worktreeRemove", handleId: p.handleId });
|
|
4758
|
+
break;
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
};
|
|
4762
|
+
return subscribeAutoPhase(handler);
|
|
4763
|
+
}, [subscribeAutoPhase]);
|
|
4204
4764
|
useEffect(() => {
|
|
4205
4765
|
const offFired = events.on("compaction.fired", (e) => {
|
|
4206
4766
|
const { level, tokens, load, maxContext: maxContext2, report } = e;
|
|
@@ -4302,16 +4862,16 @@ function App({
|
|
|
4302
4862
|
streamFlushTimer = null;
|
|
4303
4863
|
};
|
|
4304
4864
|
const status = d.status();
|
|
4305
|
-
for (const
|
|
4306
|
-
const meta = d.getSubagentMeta(
|
|
4865
|
+
for (const s2 of status.subagents) {
|
|
4866
|
+
const meta = d.getSubagentMeta(s2.id);
|
|
4307
4867
|
dispatch({
|
|
4308
4868
|
type: "fleetSpawn",
|
|
4309
|
-
id:
|
|
4310
|
-
name: meta?.name ??
|
|
4869
|
+
id: s2.id,
|
|
4870
|
+
name: meta?.name ?? s2.name,
|
|
4311
4871
|
provider: meta?.provider,
|
|
4312
4872
|
model: meta?.model
|
|
4313
4873
|
});
|
|
4314
|
-
labelFor(
|
|
4874
|
+
labelFor(s2.id, meta?.name ?? s2.name);
|
|
4315
4875
|
}
|
|
4316
4876
|
dispatch({ type: "fleetCost", cost: d.snapshot().total.cost, input: d.snapshot().total.input, output: d.snapshot().total.output });
|
|
4317
4877
|
const seen = new Set(Object.keys(status.subagents));
|
|
@@ -4726,6 +5286,14 @@ function App({
|
|
|
4726
5286
|
}
|
|
4727
5287
|
return;
|
|
4728
5288
|
}
|
|
5289
|
+
if (key.ctrl && input === "p") {
|
|
5290
|
+
dispatch({ type: "autoPhaseMonitorToggle" });
|
|
5291
|
+
return;
|
|
5292
|
+
}
|
|
5293
|
+
if (key.ctrl && input === "t") {
|
|
5294
|
+
dispatch({ type: "worktreeMonitorToggle" });
|
|
5295
|
+
return;
|
|
5296
|
+
}
|
|
4729
5297
|
if (state.autonomyPicker.open) {
|
|
4730
5298
|
if (key.escape) {
|
|
4731
5299
|
dispatch({ type: "autonomyPickerClose" });
|
|
@@ -4947,6 +5515,15 @@ function App({
|
|
|
4947
5515
|
if (state.historyIndex > 0) dispatch({ type: "historyDown" });
|
|
4948
5516
|
return;
|
|
4949
5517
|
}
|
|
5518
|
+
if (key.ctrl && input === "p") {
|
|
5519
|
+
if (state.autoPhase) dispatch({ type: "autoPhaseMonitorToggle" });
|
|
5520
|
+
else {
|
|
5521
|
+
slashRegistry.dispatch("/autophase", agent.ctx).then((res) => {
|
|
5522
|
+
if (res?.message) dispatch({ type: "addEntry", entry: { kind: "info", text: res.message } });
|
|
5523
|
+
});
|
|
5524
|
+
}
|
|
5525
|
+
return;
|
|
5526
|
+
}
|
|
4950
5527
|
if (key.ctrl && input === "a") {
|
|
4951
5528
|
setDraft(buffer, 0);
|
|
4952
5529
|
return;
|
|
@@ -5194,6 +5771,10 @@ function App({
|
|
|
5194
5771
|
if (res?.message) {
|
|
5195
5772
|
dispatch({ type: "addEntry", entry: { kind: "info", text: res.message } });
|
|
5196
5773
|
}
|
|
5774
|
+
if (res?.metadata?.autoPhaseInit) {
|
|
5775
|
+
const m = res.metadata.autoPhaseInit;
|
|
5776
|
+
dispatch({ type: "autoPhaseInit", title: m.title });
|
|
5777
|
+
}
|
|
5197
5778
|
const ctxModel = agent.ctx.model;
|
|
5198
5779
|
if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
|
|
5199
5780
|
const ctxProviderId = agent.ctx.provider?.id;
|
|
@@ -5433,6 +6014,23 @@ User message:
|
|
|
5433
6014
|
totalTokens: state.fleetTokens,
|
|
5434
6015
|
nowTick
|
|
5435
6016
|
}
|
|
6017
|
+
) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
|
|
6018
|
+
PhaseMonitor,
|
|
6019
|
+
{
|
|
6020
|
+
phases: state.autoPhase.phases,
|
|
6021
|
+
runningPhaseIds: state.autoPhase.runningPhaseIds,
|
|
6022
|
+
elapsedMs: state.autoPhase.elapsedMs,
|
|
6023
|
+
nowTick,
|
|
6024
|
+
onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
|
|
6025
|
+
}
|
|
6026
|
+
) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
|
|
6027
|
+
WorktreeMonitor,
|
|
6028
|
+
{
|
|
6029
|
+
worktrees: state.worktrees,
|
|
6030
|
+
baseBranch: state.worktreeBase,
|
|
6031
|
+
nowTick,
|
|
6032
|
+
onClose: () => dispatch({ type: "worktreeMonitorToggle" })
|
|
6033
|
+
}
|
|
5436
6034
|
) : state.monitorOpen ? /* @__PURE__ */ jsx(
|
|
5437
6035
|
FleetMonitor,
|
|
5438
6036
|
{
|
|
@@ -5441,7 +6039,16 @@ User message:
|
|
|
5441
6039
|
totalTokens: state.fleetTokens,
|
|
5442
6040
|
nowTick
|
|
5443
6041
|
}
|
|
5444
|
-
) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null
|
|
6042
|
+
) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null,
|
|
6043
|
+
state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
|
|
6044
|
+
PhasePanel,
|
|
6045
|
+
{
|
|
6046
|
+
phases: state.autoPhase.phases,
|
|
6047
|
+
runningPhaseIds: state.autoPhase.runningPhaseIds,
|
|
6048
|
+
nowTick
|
|
6049
|
+
}
|
|
6050
|
+
) : null,
|
|
6051
|
+
Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null
|
|
5445
6052
|
] });
|
|
5446
6053
|
}
|
|
5447
6054
|
function renderRunningTools(running) {
|
|
@@ -5476,6 +6083,75 @@ function fmtTok3(n) {
|
|
|
5476
6083
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
5477
6084
|
}
|
|
5478
6085
|
|
|
6086
|
+
// src/terminal-title.ts
|
|
6087
|
+
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
6088
|
+
var setTitle = (s2) => `\x1B]0;${s2}\x07`;
|
|
6089
|
+
function shortModel(model) {
|
|
6090
|
+
if (!model) return "";
|
|
6091
|
+
const base = model.split("/").pop() ?? model;
|
|
6092
|
+
return base.replace(/-\d{8}$/, "").replace(/\[.*?\]$/, "");
|
|
6093
|
+
}
|
|
6094
|
+
function marquee(text, offset, width) {
|
|
6095
|
+
const padded = `${text} `;
|
|
6096
|
+
const start = offset % padded.length;
|
|
6097
|
+
return (padded + padded).slice(start, start + width);
|
|
6098
|
+
}
|
|
6099
|
+
function startTerminalTitle(opts) {
|
|
6100
|
+
const { stdout, events } = opts;
|
|
6101
|
+
if (process.env["WRONGSTACK_NO_TITLE"] === "1" || !stdout.isTTY) {
|
|
6102
|
+
return () => {
|
|
6103
|
+
};
|
|
6104
|
+
}
|
|
6105
|
+
const app = opts.appName ?? "WrongStack";
|
|
6106
|
+
const model = shortModel(opts.model);
|
|
6107
|
+
const idleAfter = opts.idleAfterMs ?? 3500;
|
|
6108
|
+
const suffix = model ? ` \xB7 ${model}` : "";
|
|
6109
|
+
let frame = 0;
|
|
6110
|
+
let scroll = 0;
|
|
6111
|
+
let phase = "idle";
|
|
6112
|
+
let toolName = "";
|
|
6113
|
+
let lastActivity = 0;
|
|
6114
|
+
const touch = (next, tool) => {
|
|
6115
|
+
phase = next;
|
|
6116
|
+
if (tool) toolName = tool;
|
|
6117
|
+
lastActivity = Date.now();
|
|
6118
|
+
};
|
|
6119
|
+
const offs = [
|
|
6120
|
+
events.on("iteration.started", () => touch("thinking")),
|
|
6121
|
+
events.on("provider.text_delta", () => touch("thinking")),
|
|
6122
|
+
events.on("provider.thinking_delta", () => touch("thinking")),
|
|
6123
|
+
events.on("tool.started", (e) => touch("tool", e.name ?? "tool")),
|
|
6124
|
+
events.on("tool.executed", () => touch("thinking"))
|
|
6125
|
+
];
|
|
6126
|
+
const write = (s2) => {
|
|
6127
|
+
try {
|
|
6128
|
+
stdout.write(s2);
|
|
6129
|
+
} catch {
|
|
6130
|
+
}
|
|
6131
|
+
};
|
|
6132
|
+
const timer = setInterval(() => {
|
|
6133
|
+
frame = (frame + 1) % SPINNER.length;
|
|
6134
|
+
scroll += 1;
|
|
6135
|
+
if (lastActivity && Date.now() - lastActivity > idleAfter) phase = "idle";
|
|
6136
|
+
const sp = SPINNER[frame];
|
|
6137
|
+
let title;
|
|
6138
|
+
if (phase === "tool") {
|
|
6139
|
+
title = `${sp} \u25B8 ${toolName}${suffix}`;
|
|
6140
|
+
} else if (phase === "thinking") {
|
|
6141
|
+
title = `${sp} thinking\u2026${suffix}`;
|
|
6142
|
+
} else {
|
|
6143
|
+
title = marquee(`\u2726 ${app}${suffix}`, scroll >> 1, 22);
|
|
6144
|
+
}
|
|
6145
|
+
write(setTitle(title));
|
|
6146
|
+
}, opts.intervalMs ?? 130);
|
|
6147
|
+
timer.unref?.();
|
|
6148
|
+
return () => {
|
|
6149
|
+
clearInterval(timer);
|
|
6150
|
+
for (const off of offs) off();
|
|
6151
|
+
write(setTitle(model ? `${app} \xB7 ${model}` : app));
|
|
6152
|
+
};
|
|
6153
|
+
}
|
|
6154
|
+
|
|
5479
6155
|
// src/run-tui.ts
|
|
5480
6156
|
var BRACKETED_PASTE_ON = "\x1B[?2004h";
|
|
5481
6157
|
var BRACKETED_PASTE_OFF = "\x1B[?2004l";
|
|
@@ -5497,12 +6173,13 @@ async function runTui(opts) {
|
|
|
5497
6173
|
stdout.write(CURSOR_HOME);
|
|
5498
6174
|
}
|
|
5499
6175
|
stdout.write(BRACKETED_PASTE_ON);
|
|
6176
|
+
const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
|
|
5500
6177
|
const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
|
|
5501
6178
|
const swallow = () => {
|
|
5502
6179
|
};
|
|
5503
|
-
for (const
|
|
6180
|
+
for (const s2 of swallowSignals) {
|
|
5504
6181
|
try {
|
|
5505
|
-
process.on(
|
|
6182
|
+
process.on(s2, swallow);
|
|
5506
6183
|
} catch {
|
|
5507
6184
|
}
|
|
5508
6185
|
}
|
|
@@ -5510,6 +6187,10 @@ async function runTui(opts) {
|
|
|
5510
6187
|
const cleanup = () => {
|
|
5511
6188
|
if (cleaned) return;
|
|
5512
6189
|
cleaned = true;
|
|
6190
|
+
try {
|
|
6191
|
+
stopTitle();
|
|
6192
|
+
} catch {
|
|
6193
|
+
}
|
|
5513
6194
|
try {
|
|
5514
6195
|
stdout.write(BRACKETED_PASTE_OFF);
|
|
5515
6196
|
if (useAltScreen) {
|
|
@@ -5521,13 +6202,13 @@ async function runTui(opts) {
|
|
|
5521
6202
|
const signals = ["SIGTERM", "SIGHUP", "SIGINT"];
|
|
5522
6203
|
const signalHandler = () => cleanup();
|
|
5523
6204
|
const exitHandler = () => cleanup();
|
|
5524
|
-
for (const
|
|
6205
|
+
for (const s2 of signals) process.on(s2, signalHandler);
|
|
5525
6206
|
process.on("exit", exitHandler);
|
|
5526
6207
|
const detachListeners = () => {
|
|
5527
|
-
for (const
|
|
5528
|
-
for (const
|
|
6208
|
+
for (const s2 of signals) process.off(s2, signalHandler);
|
|
6209
|
+
for (const s2 of swallowSignals) {
|
|
5529
6210
|
try {
|
|
5530
|
-
process.off(
|
|
6211
|
+
process.off(s2, swallow);
|
|
5531
6212
|
} catch {
|
|
5532
6213
|
}
|
|
5533
6214
|
}
|
|
@@ -5570,6 +6251,7 @@ async function runTui(opts) {
|
|
|
5570
6251
|
getParallelEngine: opts.getParallelEngine,
|
|
5571
6252
|
subscribeEternalIteration: opts.subscribeEternalIteration,
|
|
5572
6253
|
subscribeEternalStage: opts.subscribeEternalStage,
|
|
6254
|
+
subscribeAutoPhase: opts.subscribeAutoPhase,
|
|
5573
6255
|
appVersion: opts.appVersion,
|
|
5574
6256
|
provider: opts.provider,
|
|
5575
6257
|
family: opts.family,
|