@wrongstack/tui 0.8.5 → 0.9.0
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 +443 -186
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -162,156 +162,37 @@ function FilePicker({ query, matches, selected }) {
|
|
|
162
162
|
function highlight(path3, query) {
|
|
163
163
|
return path3;
|
|
164
164
|
}
|
|
165
|
-
var STATUS_ICON = {
|
|
166
|
-
idle: { icon: "\u25CB", color: "gray" },
|
|
167
|
-
running: { icon: "\u25CF", color: "green" },
|
|
168
|
-
success: { icon: "\u2713", color: "green" },
|
|
169
|
-
failed: { icon: "\u2717", color: "red" },
|
|
170
|
-
timeout: { icon: "\u23F1", color: "yellow" },
|
|
171
|
-
stopped: { icon: "\u2298", color: "yellow" }
|
|
172
|
-
};
|
|
173
|
-
function fmtCost(n) {
|
|
174
|
-
if (n === 0) return "\u2014";
|
|
175
|
-
return `$${n.toFixed(3)}`;
|
|
176
|
-
}
|
|
177
|
-
function fmtCount(n) {
|
|
178
|
-
if (n === 0) return "\u2014";
|
|
179
|
-
return String(n);
|
|
180
|
-
}
|
|
181
|
-
function fmtDuration(ms) {
|
|
182
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
183
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
184
|
-
}
|
|
185
|
-
function fmtBytes(n) {
|
|
186
|
-
if (n < 1024) return `${n}B`;
|
|
187
|
-
return `${(n / 1024).toFixed(1)}KB`;
|
|
188
|
-
}
|
|
189
|
-
function fmtRecentTool(tool) {
|
|
190
|
-
const status = tool.ok === false ? "fail" : "ok";
|
|
191
|
-
const name = tool.name.length > 24 ? `${tool.name.slice(0, 23)}...` : tool.name;
|
|
192
|
-
const parts = [status, name];
|
|
193
|
-
if (typeof tool.durationMs === "number") parts.push(fmtDuration(tool.durationMs));
|
|
194
|
-
if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes(tool.outputBytes));
|
|
195
|
-
if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
|
|
196
|
-
return parts.join(" ");
|
|
197
|
-
}
|
|
198
|
-
function fmtRecentMessage(message) {
|
|
199
|
-
const text = message.text.replace(/\s+/g, " ");
|
|
200
|
-
return text.length > 80 ? `${text.slice(0, 79)}...` : text;
|
|
201
|
-
}
|
|
202
|
-
function fmtModel(provider, model) {
|
|
203
|
-
if (!provider && !model) return "";
|
|
204
|
-
const p = provider ?? "";
|
|
205
|
-
const m = model ?? "";
|
|
206
|
-
return p && m ? `${p}/${m}` : p || m;
|
|
207
|
-
}
|
|
208
|
-
function resolveName(entry, roster) {
|
|
209
|
-
const rosterEntry = roster?.[entry.id];
|
|
210
|
-
if (rosterEntry) return rosterEntry.name;
|
|
211
|
-
return entry.name;
|
|
212
|
-
}
|
|
213
165
|
function FleetPanel({ entries, totalCost, roster }) {
|
|
214
166
|
const list = Object.values(entries);
|
|
215
167
|
if (list.length === 0) return null;
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
245
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: totalLabel })
|
|
246
|
-
] }),
|
|
247
|
-
sorted.map((entry) => {
|
|
248
|
-
const si = STATUS_ICON[entry.status];
|
|
249
|
-
const modelTag = fmtModel(entry.provider, entry.model);
|
|
250
|
-
const name = resolveName(entry, roster);
|
|
251
|
-
const recentTools = (entry.recentTools ?? []).slice(-2).map(fmtRecentTool).join(" | ");
|
|
252
|
-
const recentMessages = (entry.recentMessages ?? []).slice(-2).map(fmtRecentMessage);
|
|
253
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
254
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
255
|
-
/* @__PURE__ */ jsx(Text, { color: si.color, children: si.icon }),
|
|
256
|
-
/* @__PURE__ */ jsx(Text, { children: name.slice(0, 16).padEnd(16) }),
|
|
257
|
-
modelTag ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
258
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
259
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: modelTag })
|
|
260
|
-
] }) : null,
|
|
261
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
262
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
263
|
-
fmtCount(entry.iterations).padStart(3),
|
|
264
|
-
"it"
|
|
265
|
-
] }),
|
|
266
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
267
|
-
fmtCount(entry.toolCalls).padStart(3),
|
|
268
|
-
"tc"
|
|
269
|
-
] }),
|
|
270
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
271
|
-
/* @__PURE__ */ jsx(Text, { color: "yellow", children: fmtCost(entry.cost) })
|
|
272
|
-
] }),
|
|
273
|
-
entry.status === "running" && entry.currentTool ? /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, children: [
|
|
274
|
-
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
275
|
-
"\u2192 ",
|
|
276
|
-
entry.currentTool.name
|
|
277
|
-
] }),
|
|
278
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
279
|
-
" (",
|
|
280
|
-
Math.max(0, Date.now() - entry.currentTool.startedAt),
|
|
281
|
-
"ms)"
|
|
282
|
-
] })
|
|
283
|
-
] }) : null,
|
|
284
|
-
recentTools ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
285
|
-
"tools: ",
|
|
286
|
-
recentTools
|
|
287
|
-
] }) }) : null,
|
|
288
|
-
recentMessages.map((message, index) => /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
289
|
-
"msg: ",
|
|
290
|
-
message
|
|
291
|
-
] }) }, `${entry.id}-msg-${index}-${message}`)),
|
|
292
|
-
entry.budgetWarning ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
293
|
-
"\u26A1 hitting ",
|
|
294
|
-
entry.budgetWarning.kind,
|
|
295
|
-
" limit (",
|
|
296
|
-
entry.budgetWarning.used,
|
|
297
|
-
"/",
|
|
298
|
-
entry.budgetWarning.limit,
|
|
299
|
-
") \u2014 extending"
|
|
300
|
-
] }) }) : null,
|
|
301
|
-
entry.status === "running" && entry.streamingText ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
302
|
-
">",
|
|
303
|
-
" ",
|
|
304
|
-
entry.streamingText.slice(-80)
|
|
305
|
-
] }) }) : null,
|
|
306
|
-
entry.transcriptPath ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
307
|
-
"log: ",
|
|
308
|
-
entry.transcriptPath
|
|
309
|
-
] }) }) : null
|
|
310
|
-
] }, entry.id);
|
|
311
|
-
})
|
|
312
|
-
]
|
|
313
|
-
}
|
|
314
|
-
);
|
|
168
|
+
const running = list.filter((e) => e.status === "running");
|
|
169
|
+
const runningCount = running.length;
|
|
170
|
+
const costLabel = totalCost > 0 ? ` \xB7 $${totalCost.toFixed(3)}` : "";
|
|
171
|
+
const summaryLine = runningCount > 0 ? `${runningCount} running${costLabel}` : `idle${costLabel}`;
|
|
172
|
+
const shown = running.slice(0, 3);
|
|
173
|
+
const overflow = running.length > 3 ? running.length - 3 : 0;
|
|
174
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
175
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
176
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u26A1 Fleet" }),
|
|
177
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
178
|
+
/* @__PURE__ */ jsx(Text, { children: summaryLine })
|
|
179
|
+
] }),
|
|
180
|
+
shown.map((entry) => {
|
|
181
|
+
const name = roster?.[entry.id]?.name ?? entry.name;
|
|
182
|
+
const tool = entry.currentTool?.name ?? "\u2014";
|
|
183
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
184
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: "\u25CF" }),
|
|
185
|
+
/* @__PURE__ */ jsx(Text, { children: name.slice(0, 12).padEnd(12) }),
|
|
186
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2192" }),
|
|
187
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: tool })
|
|
188
|
+
] }, entry.id);
|
|
189
|
+
}),
|
|
190
|
+
overflow > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
191
|
+
" +",
|
|
192
|
+
overflow,
|
|
193
|
+
" more running"
|
|
194
|
+
] }) : null
|
|
195
|
+
] });
|
|
315
196
|
}
|
|
316
197
|
function StatusBar({
|
|
317
198
|
model,
|
|
@@ -721,7 +602,7 @@ function fmtTokens(n) {
|
|
|
721
602
|
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
722
603
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
723
604
|
}
|
|
724
|
-
function
|
|
605
|
+
function fmtCost(n) {
|
|
725
606
|
if (n === 0) return "\u2014";
|
|
726
607
|
return `$${n.toFixed(3)}`;
|
|
727
608
|
}
|
|
@@ -832,7 +713,7 @@ function FleetMonitor({
|
|
|
832
713
|
/* @__PURE__ */ jsx(Text, { color: s2.color, children: e.status.padEnd(10) }),
|
|
833
714
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `L${e.iterations} ${e.toolCalls}t`.padEnd(8) }),
|
|
834
715
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(8).slice(0, 8) }),
|
|
835
|
-
/* @__PURE__ */ jsx(Text, { color: "yellow", children:
|
|
716
|
+
/* @__PURE__ */ jsx(Text, { color: "yellow", children: fmtCost(e.cost) }),
|
|
836
717
|
e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
837
718
|
" \u26A1\xD7",
|
|
838
719
|
e.extensions
|
|
@@ -865,6 +746,20 @@ var STATUS2 = {
|
|
|
865
746
|
function isTerminal(status) {
|
|
866
747
|
return status === "success" || status === "failed" || status === "timeout" || status === "stopped";
|
|
867
748
|
}
|
|
749
|
+
var IDLE_HIDE_MS = 6e4;
|
|
750
|
+
function selectLiveAgents(all, now, idleHideMs = IDLE_HIDE_MS) {
|
|
751
|
+
const visible = all.filter((e) => {
|
|
752
|
+
if (isTerminal(e.status)) return false;
|
|
753
|
+
if (e.status === "running") return true;
|
|
754
|
+
return now - e.lastEventAt < idleHideMs;
|
|
755
|
+
});
|
|
756
|
+
return visible.sort((a, b) => {
|
|
757
|
+
if (a.status === "running" && b.status !== "running") return -1;
|
|
758
|
+
if (a.status !== "running" && b.status === "running") return 1;
|
|
759
|
+
if (a.status === "running") return a.startedAt - b.startedAt;
|
|
760
|
+
return b.lastEventAt - a.lastEventAt;
|
|
761
|
+
});
|
|
762
|
+
}
|
|
868
763
|
function fmtTokens2(n) {
|
|
869
764
|
if (n < 1e3) return String(n);
|
|
870
765
|
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
@@ -882,15 +777,14 @@ function AgentsMonitor({
|
|
|
882
777
|
nowTick
|
|
883
778
|
}) {
|
|
884
779
|
const all = Object.values(entries);
|
|
885
|
-
const live = all
|
|
780
|
+
const live = selectLiveAgents(all, nowTick);
|
|
886
781
|
const running = live.filter((e) => e.status === "running").length;
|
|
887
782
|
const totalDone = all.filter((e) => e.status === "success").length;
|
|
888
783
|
const totalFailed = all.filter((e) => e.status === "failed" || e.status === "timeout").length;
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
});
|
|
784
|
+
const hiddenIdle = all.filter(
|
|
785
|
+
(e) => e.status === "idle" && nowTick - e.lastEventAt >= IDLE_HIDE_MS
|
|
786
|
+
).length;
|
|
787
|
+
const ordered = live;
|
|
894
788
|
const shown = ordered.slice(0, 8);
|
|
895
789
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
|
|
896
790
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
@@ -927,7 +821,12 @@ function AgentsMonitor({
|
|
|
927
821
|
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
928
822
|
"$",
|
|
929
823
|
totalCost.toFixed(3)
|
|
930
|
-
] })
|
|
824
|
+
] }),
|
|
825
|
+
hiddenIdle > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
826
|
+
"\xB7 ",
|
|
827
|
+
hiddenIdle,
|
|
828
|
+
" idle hidden"
|
|
829
|
+
] }) : null
|
|
931
830
|
] }),
|
|
932
831
|
shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No live agents \u2014 spawn with /spawn or /fleet dispatch." }) : null,
|
|
933
832
|
shown.map((e) => {
|
|
@@ -1151,18 +1050,18 @@ function PhasePanel({ phases, runningPhaseIds, nowTick }) {
|
|
|
1151
1050
|
] }),
|
|
1152
1051
|
list.map((phase, i) => {
|
|
1153
1052
|
const phaseKey = Object.keys(phases).find((k) => phases[k] === phase) ?? String(i);
|
|
1154
|
-
const
|
|
1053
|
+
const st2 = s(phase.status);
|
|
1155
1054
|
const progress = phase.totalTasks > 0 ? `${phase.completedTasks}/${phase.totalTasks}` : "";
|
|
1156
1055
|
const elapsed = phase.startedAt ? fmtElapsed3(nowTick - phase.startedAt) : "";
|
|
1157
1056
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1158
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
1057
|
+
/* @__PURE__ */ jsx(Text, { color: st2.color, children: st2.icon }),
|
|
1159
1058
|
/* @__PURE__ */ jsx(Text, { children: phase.name.slice(0, 14).padEnd(14) }),
|
|
1160
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
1059
|
+
/* @__PURE__ */ jsx(Text, { color: st2.color, dimColor: true, children: st2.icon === "\u25CF" ? phase.status : "" }),
|
|
1161
1060
|
progress ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1162
1061
|
" ",
|
|
1163
1062
|
progress
|
|
1164
1063
|
] }) : null,
|
|
1165
|
-
elapsed &&
|
|
1064
|
+
elapsed && st2.icon === "\u25CF" ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1166
1065
|
" ",
|
|
1167
1066
|
elapsed
|
|
1168
1067
|
] }) : null
|
|
@@ -1172,6 +1071,204 @@ function PhasePanel({ phases, runningPhaseIds, nowTick }) {
|
|
|
1172
1071
|
}
|
|
1173
1072
|
);
|
|
1174
1073
|
}
|
|
1074
|
+
var fmtElapsed4 = (ms) => {
|
|
1075
|
+
const s2 = Math.floor(ms / 1e3);
|
|
1076
|
+
const m = Math.floor(s2 / 60);
|
|
1077
|
+
const h = Math.floor(m / 60);
|
|
1078
|
+
if (h > 0) return `${h}:${String(m % 60).padStart(2, "0")}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1079
|
+
if (m > 0) return `${m}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1080
|
+
return `${s2}s`;
|
|
1081
|
+
};
|
|
1082
|
+
var STATUS4 = {
|
|
1083
|
+
allocating: { icon: "\u25CB", color: "gray" },
|
|
1084
|
+
active: { icon: "\u25CF", color: "yellow" },
|
|
1085
|
+
committing: { icon: "\u25D0", color: "cyan" },
|
|
1086
|
+
merging: { icon: "\u21E1", color: "blue" },
|
|
1087
|
+
merged: { icon: "\u2713", color: "green" },
|
|
1088
|
+
"needs-review": { icon: "\u26A0", color: "magenta" },
|
|
1089
|
+
failed: { icon: "\u2717", color: "red" }
|
|
1090
|
+
};
|
|
1091
|
+
function st(status) {
|
|
1092
|
+
return STATUS4[status] ?? { icon: "?", color: "white" };
|
|
1093
|
+
}
|
|
1094
|
+
function WorktreePanel({
|
|
1095
|
+
worktrees,
|
|
1096
|
+
nowTick
|
|
1097
|
+
}) {
|
|
1098
|
+
const list = Object.values(worktrees);
|
|
1099
|
+
if (list.length === 0) return null;
|
|
1100
|
+
const active = list.filter((w) => w.status === "active" || w.status === "committing" || w.status === "merging").length;
|
|
1101
|
+
const merged = list.filter((w) => w.status === "merged").length;
|
|
1102
|
+
const failed = list.filter((w) => w.status === "failed" || w.status === "needs-review").length;
|
|
1103
|
+
return /* @__PURE__ */ jsxs(
|
|
1104
|
+
Box,
|
|
1105
|
+
{
|
|
1106
|
+
flexDirection: "column",
|
|
1107
|
+
paddingX: 1,
|
|
1108
|
+
borderStyle: "single",
|
|
1109
|
+
borderTop: false,
|
|
1110
|
+
borderBottom: false,
|
|
1111
|
+
borderLeft: false,
|
|
1112
|
+
borderRight: false,
|
|
1113
|
+
children: [
|
|
1114
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
|
|
1115
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Worktrees" }),
|
|
1116
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
1117
|
+
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
1118
|
+
"\u25B6",
|
|
1119
|
+
active
|
|
1120
|
+
] }),
|
|
1121
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
1122
|
+
"\u2713",
|
|
1123
|
+
merged
|
|
1124
|
+
] }),
|
|
1125
|
+
failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1126
|
+
"\u2717",
|
|
1127
|
+
failed
|
|
1128
|
+
] }) : null,
|
|
1129
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1130
|
+
"\xB7 ",
|
|
1131
|
+
list.length,
|
|
1132
|
+
" total \xB7 Ctrl+W for details"
|
|
1133
|
+
] })
|
|
1134
|
+
] }),
|
|
1135
|
+
list.map((w) => {
|
|
1136
|
+
const s2 = st(w.status);
|
|
1137
|
+
const conflict = w.status === "needs-review";
|
|
1138
|
+
const elapsed = w.allocatedAt ? fmtElapsed4(nowTick - w.allocatedAt) : "";
|
|
1139
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1140
|
+
/* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.icon }),
|
|
1141
|
+
/* @__PURE__ */ jsx(Text, { children: w.branch.replace(/^wstack\/ap\//, "").slice(0, 18).padEnd(18) }),
|
|
1142
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: w.ownerLabel.slice(0, 12) }),
|
|
1143
|
+
conflict ? /* @__PURE__ */ jsx(Text, { color: "magenta", children: " CONFLICT" }) : /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1144
|
+
" +",
|
|
1145
|
+
w.insertions,
|
|
1146
|
+
"/-",
|
|
1147
|
+
w.deletions,
|
|
1148
|
+
" ",
|
|
1149
|
+
w.files,
|
|
1150
|
+
"f"
|
|
1151
|
+
] }),
|
|
1152
|
+
elapsed && (w.status === "active" || w.status === "committing") ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1153
|
+
" ",
|
|
1154
|
+
elapsed
|
|
1155
|
+
] }) : null
|
|
1156
|
+
] }, w.branch);
|
|
1157
|
+
})
|
|
1158
|
+
]
|
|
1159
|
+
}
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
var fmtElapsed5 = (ms) => {
|
|
1163
|
+
const s2 = Math.floor(ms / 1e3);
|
|
1164
|
+
const m = Math.floor(s2 / 60);
|
|
1165
|
+
const h = Math.floor(m / 60);
|
|
1166
|
+
if (h > 0) return `${h}:${String(m % 60).padStart(2, "0")}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1167
|
+
if (m > 0) return `${m}:${String(s2 % 60).padStart(2, "0")}`;
|
|
1168
|
+
return `${s2}s`;
|
|
1169
|
+
};
|
|
1170
|
+
var WT_STATUS = {
|
|
1171
|
+
allocating: { icon: "\u25CB", color: "gray", label: "allocating" },
|
|
1172
|
+
active: { icon: "\u25CF", color: "yellow", label: "active" },
|
|
1173
|
+
committing: { icon: "\u25D0", color: "cyan", label: "committing" },
|
|
1174
|
+
merging: { icon: "\u21E1", color: "blue", label: "merging" },
|
|
1175
|
+
merged: { icon: "\u2713", color: "green", label: "merged" },
|
|
1176
|
+
"needs-review": { icon: "\u26A0", color: "magenta", label: "needs-review" },
|
|
1177
|
+
failed: { icon: "\u2717", color: "red", label: "failed" }
|
|
1178
|
+
};
|
|
1179
|
+
function fmt(s2) {
|
|
1180
|
+
return WT_STATUS[s2] ?? { icon: "?", color: "white", label: s2 };
|
|
1181
|
+
}
|
|
1182
|
+
function WorktreeMonitor({
|
|
1183
|
+
worktrees,
|
|
1184
|
+
baseBranch,
|
|
1185
|
+
nowTick,
|
|
1186
|
+
onClose
|
|
1187
|
+
}) {
|
|
1188
|
+
useInput((input, key) => {
|
|
1189
|
+
if (key.escape || key.ctrl && input === "w") onClose();
|
|
1190
|
+
});
|
|
1191
|
+
const list = Object.values(worktrees);
|
|
1192
|
+
const active = list.filter((w) => ["active", "committing", "merging"].includes(w.status)).length;
|
|
1193
|
+
const merged = list.filter((w) => w.status === "merged").length;
|
|
1194
|
+
const failed = list.filter((w) => w.status === "failed" || w.status === "needs-review").length;
|
|
1195
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: [
|
|
1196
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginBottom: 1, children: [
|
|
1197
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "WORKTREE MONITOR" }),
|
|
1198
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
1199
|
+
baseBranch ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1200
|
+
"base ",
|
|
1201
|
+
baseBranch
|
|
1202
|
+
] }) : null,
|
|
1203
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
1204
|
+
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
1205
|
+
"\u25B6",
|
|
1206
|
+
active
|
|
1207
|
+
] }),
|
|
1208
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
1209
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
1210
|
+
"\u2713",
|
|
1211
|
+
merged
|
|
1212
|
+
] }),
|
|
1213
|
+
failed > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1214
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
1215
|
+
/* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1216
|
+
"\u2717",
|
|
1217
|
+
failed
|
|
1218
|
+
] })
|
|
1219
|
+
] }) : null,
|
|
1220
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+W / Esc to close" })
|
|
1221
|
+
] }),
|
|
1222
|
+
list.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No worktrees. They appear when AutoPhase runs with isolation on." }) : list.map((w) => {
|
|
1223
|
+
const s2 = fmt(w.status);
|
|
1224
|
+
const short = w.branch.replace(/^wstack\/ap\//, "");
|
|
1225
|
+
const elapsed = w.allocatedAt ? fmtElapsed5(nowTick - w.allocatedAt) : "\u2014";
|
|
1226
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
1227
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1228
|
+
/* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
|
|
1229
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: short }),
|
|
1230
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
1231
|
+
/* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.label }),
|
|
1232
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1233
|
+
"\xB7 elapsed ",
|
|
1234
|
+
elapsed
|
|
1235
|
+
] })
|
|
1236
|
+
] }),
|
|
1237
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginLeft: 2, children: [
|
|
1238
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1239
|
+
w.baseBranch ?? baseBranch ?? "base",
|
|
1240
|
+
" \u2192 ",
|
|
1241
|
+
short
|
|
1242
|
+
] }),
|
|
1243
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1244
|
+
"\xB7 owner: ",
|
|
1245
|
+
w.ownerLabel
|
|
1246
|
+
] })
|
|
1247
|
+
] }),
|
|
1248
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginLeft: 2, children: [
|
|
1249
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
1250
|
+
"+",
|
|
1251
|
+
w.insertions
|
|
1252
|
+
] }),
|
|
1253
|
+
/* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1254
|
+
"-",
|
|
1255
|
+
w.deletions
|
|
1256
|
+
] }),
|
|
1257
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1258
|
+
"\xB7 ",
|
|
1259
|
+
w.files,
|
|
1260
|
+
" files"
|
|
1261
|
+
] })
|
|
1262
|
+
] }),
|
|
1263
|
+
w.conflictFiles && w.conflictFiles.length > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
|
|
1264
|
+
"conflicts: ",
|
|
1265
|
+
w.conflictFiles.join(", ")
|
|
1266
|
+
] }) }) : null
|
|
1267
|
+
] }, w.branch);
|
|
1268
|
+
}),
|
|
1269
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Esc close \xB7 merge conflicts with /worktree merge <branch>" }) })
|
|
1270
|
+
] });
|
|
1271
|
+
}
|
|
1175
1272
|
|
|
1176
1273
|
// src/markdown-table.ts
|
|
1177
1274
|
function renderMarkdownTables(text, maxWidth) {
|
|
@@ -1409,7 +1506,7 @@ function ToolStreamBox({
|
|
|
1409
1506
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
1410
1507
|
/* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u25C6 " }),
|
|
1411
1508
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: name }),
|
|
1412
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${
|
|
1509
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration(elapsedMs)}` }),
|
|
1413
1510
|
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
|
|
1414
1511
|
] }),
|
|
1415
1512
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
|
|
@@ -1504,7 +1601,7 @@ function Entry({
|
|
|
1504
1601
|
parts.push(`${entry.outputLines} L`);
|
|
1505
1602
|
}
|
|
1506
1603
|
if (entry.outputBytes && entry.outputBytes > 0) {
|
|
1507
|
-
parts.push(
|
|
1604
|
+
parts.push(fmtBytes(entry.outputBytes));
|
|
1508
1605
|
}
|
|
1509
1606
|
if (entry.outputTokens && entry.outputTokens > 0) {
|
|
1510
1607
|
parts.push(`\u2248${fmtTok2(entry.outputTokens)} tok`);
|
|
@@ -1520,7 +1617,7 @@ function Entry({
|
|
|
1520
1617
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1521
1618
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: argSummary })
|
|
1522
1619
|
] }) : null,
|
|
1523
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${
|
|
1620
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration(entry.durationMs)}` }),
|
|
1524
1621
|
sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
|
|
1525
1622
|
] }),
|
|
1526
1623
|
outLines.map((line, i) => (
|
|
@@ -1632,7 +1729,7 @@ function fmtTok2(n) {
|
|
|
1632
1729
|
if (n >= 1e3) return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
|
|
1633
1730
|
return String(n);
|
|
1634
1731
|
}
|
|
1635
|
-
function
|
|
1732
|
+
function fmtDuration(ms) {
|
|
1636
1733
|
if (ms < 1e3) return `${ms}ms`;
|
|
1637
1734
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
1638
1735
|
const totalSec = Math.floor(ms / 1e3);
|
|
@@ -1760,7 +1857,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
1760
1857
|
const bytes = numOf(o["bytes_written"]) ?? numOf(o["bytes"]);
|
|
1761
1858
|
const created = o["created"] === true;
|
|
1762
1859
|
const tag = created ? "created" : "updated";
|
|
1763
|
-
if (bytes !== void 0) return [`${tag} \xB7 ${
|
|
1860
|
+
if (bytes !== void 0) return [`${tag} \xB7 ${fmtBytes(bytes)}`];
|
|
1764
1861
|
return [tag];
|
|
1765
1862
|
}
|
|
1766
1863
|
}
|
|
@@ -1825,17 +1922,17 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
1825
1922
|
if (json && typeof json === "object") {
|
|
1826
1923
|
const o = json;
|
|
1827
1924
|
const bytes = numOf(o["bytes"]);
|
|
1828
|
-
if (bytes !== void 0) return [`${
|
|
1925
|
+
if (bytes !== void 0) return [`${fmtBytes(bytes)} read`];
|
|
1829
1926
|
}
|
|
1830
1927
|
const range = scanNumberedRange(text);
|
|
1831
1928
|
if (range.count > 0 && range.first !== void 0 && range.last !== void 0) {
|
|
1832
1929
|
if (range.first === range.last) {
|
|
1833
|
-
return [`L${range.first} \xB7 ${
|
|
1930
|
+
return [`L${range.first} \xB7 ${fmtBytes(text.length)}`];
|
|
1834
1931
|
}
|
|
1835
1932
|
const contiguous = range.count === range.last - range.first + 1;
|
|
1836
1933
|
const head = `L${range.first}\u2013${range.last}`;
|
|
1837
1934
|
const tail = contiguous ? `${range.count} line${range.count === 1 ? "" : "s"}` : `${range.count} lines (gaps)`;
|
|
1838
|
-
return [`${head} \xB7 ${tail} \xB7 ${
|
|
1935
|
+
return [`${head} \xB7 ${tail} \xB7 ${fmtBytes(text.length)}`];
|
|
1839
1936
|
}
|
|
1840
1937
|
}
|
|
1841
1938
|
if (toolName === "grep" || toolName === "glob") {
|
|
@@ -1893,7 +1990,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
1893
1990
|
const head = [];
|
|
1894
1991
|
if (status !== void 0) head.push(`HTTP ${status}`);
|
|
1895
1992
|
if (ct) head.push(ct.split(";")[0] ?? ct);
|
|
1896
|
-
if (content) head.push(
|
|
1993
|
+
if (content) head.push(fmtBytes(Buffer.byteLength(content, "utf8")));
|
|
1897
1994
|
const lines = [];
|
|
1898
1995
|
if (head.length > 0) lines.push(head.join(" \xB7 "));
|
|
1899
1996
|
if (url && status !== void 0 && (status < 200 || status >= 400)) {
|
|
@@ -1984,7 +2081,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
1984
2081
|
if (runner && runner !== "none") head.push(runner);
|
|
1985
2082
|
head.push(`${passed}/${total} passed`);
|
|
1986
2083
|
if (failed > 0) head.push(`${failed} failed`);
|
|
1987
|
-
if (duration !== void 0) head.push(
|
|
2084
|
+
if (duration !== void 0) head.push(fmtDuration(duration));
|
|
1988
2085
|
return [head.join(" \xB7 ")];
|
|
1989
2086
|
}
|
|
1990
2087
|
}
|
|
@@ -2269,7 +2366,7 @@ function countLines(text) {
|
|
|
2269
2366
|
if (!text) return 0;
|
|
2270
2367
|
return text.replace(/\n$/, "").split("\n").length;
|
|
2271
2368
|
}
|
|
2272
|
-
function
|
|
2369
|
+
function fmtBytes(n) {
|
|
2273
2370
|
if (n < 1024) return `${n}B`;
|
|
2274
2371
|
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
2275
2372
|
return `${(n / (1024 * 1024)).toFixed(1)}MB`;
|
|
@@ -2350,27 +2447,27 @@ function Input({
|
|
|
2350
2447
|
hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
|
|
2351
2448
|
] });
|
|
2352
2449
|
}
|
|
2353
|
-
function
|
|
2450
|
+
function fmtElapsed6(ms) {
|
|
2354
2451
|
if (ms < 1e3) return `${ms}ms`;
|
|
2355
2452
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2356
2453
|
const m = Math.floor(ms / 6e4);
|
|
2357
2454
|
const s2 = Math.floor(ms % 6e4 / 1e3);
|
|
2358
2455
|
return `${m}m${s2.toString().padStart(2, "0")}s`;
|
|
2359
2456
|
}
|
|
2360
|
-
function
|
|
2457
|
+
function fmtBytes2(n) {
|
|
2361
2458
|
if (n < 1024) return `${n}B`;
|
|
2362
2459
|
return `${(n / 1024).toFixed(1)}KB`;
|
|
2363
2460
|
}
|
|
2364
|
-
function
|
|
2461
|
+
function fmtRecentTool(tool) {
|
|
2365
2462
|
const status = tool.ok === false ? "fail" : "ok";
|
|
2366
2463
|
const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
|
|
2367
2464
|
const parts = [status, name];
|
|
2368
|
-
if (typeof tool.durationMs === "number") parts.push(
|
|
2369
|
-
if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(
|
|
2465
|
+
if (typeof tool.durationMs === "number") parts.push(fmtElapsed6(tool.durationMs));
|
|
2466
|
+
if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes2(tool.outputBytes));
|
|
2370
2467
|
if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
|
|
2371
2468
|
return parts.join(" ");
|
|
2372
2469
|
}
|
|
2373
|
-
function
|
|
2470
|
+
function fmtRecentMessage(message) {
|
|
2374
2471
|
const text = message.text.replace(/\s+/g, " ");
|
|
2375
2472
|
return text.length > 48 ? `${text.slice(0, 47)}...` : text;
|
|
2376
2473
|
}
|
|
@@ -2386,9 +2483,9 @@ function LiveActivityStrip({
|
|
|
2386
2483
|
running.map((e) => {
|
|
2387
2484
|
const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
|
|
2388
2485
|
const taskElapsed = now - e.startedAt;
|
|
2389
|
-
const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${
|
|
2390
|
-
const recentTools = (e.recentTools ?? []).slice(-2).map(
|
|
2391
|
-
const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(
|
|
2486
|
+
const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed6(toolElapsed)})` : "idle between tools";
|
|
2487
|
+
const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool).join(" | ");
|
|
2488
|
+
const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage).join("");
|
|
2392
2489
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
2393
2490
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u25CF" }),
|
|
2394
2491
|
/* @__PURE__ */ jsx(Text, { children: e.name.slice(0, 14).padEnd(14) }),
|
|
@@ -2400,7 +2497,7 @@ function LiveActivityStrip({
|
|
|
2400
2497
|
"it ",
|
|
2401
2498
|
e.toolCalls,
|
|
2402
2499
|
"tc \xB7 ",
|
|
2403
|
-
|
|
2500
|
+
fmtElapsed6(taskElapsed)
|
|
2404
2501
|
] }),
|
|
2405
2502
|
recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2406
2503
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
|
|
@@ -2413,7 +2510,7 @@ function LiveActivityStrip({
|
|
|
2413
2510
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
|
|
2414
2511
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
2415
2512
|
"msg: ",
|
|
2416
|
-
|
|
2513
|
+
fmtRecentMessage({ text: messageText})
|
|
2417
2514
|
] })
|
|
2418
2515
|
] }) : null
|
|
2419
2516
|
] }, e.id);
|
|
@@ -3452,6 +3549,34 @@ function reducer(state, action) {
|
|
|
3452
3549
|
case "autoPhaseReset": {
|
|
3453
3550
|
return { ...state, autoPhase: null };
|
|
3454
3551
|
}
|
|
3552
|
+
case "worktreeUpsert": {
|
|
3553
|
+
const prev = state.worktrees[action.handleId];
|
|
3554
|
+
const merged = {
|
|
3555
|
+
branch: "",
|
|
3556
|
+
ownerLabel: "",
|
|
3557
|
+
status: "active",
|
|
3558
|
+
insertions: 0,
|
|
3559
|
+
deletions: 0,
|
|
3560
|
+
files: 0,
|
|
3561
|
+
allocatedAt: Date.now(),
|
|
3562
|
+
...prev,
|
|
3563
|
+
...action.row
|
|
3564
|
+
};
|
|
3565
|
+
return {
|
|
3566
|
+
...state,
|
|
3567
|
+
worktrees: { ...state.worktrees, [action.handleId]: merged },
|
|
3568
|
+
worktreeBase: action.baseBranch ?? state.worktreeBase
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
case "worktreeRemove": {
|
|
3572
|
+
if (!state.worktrees[action.handleId]) return state;
|
|
3573
|
+
const next = { ...state.worktrees };
|
|
3574
|
+
delete next[action.handleId];
|
|
3575
|
+
return { ...state, worktrees: next };
|
|
3576
|
+
}
|
|
3577
|
+
case "worktreeMonitorToggle": {
|
|
3578
|
+
return { ...state, worktreeMonitorOpen: !state.worktreeMonitorOpen };
|
|
3579
|
+
}
|
|
3455
3580
|
}
|
|
3456
3581
|
}
|
|
3457
3582
|
var PASTE_THRESHOLD_CHARS = 200;
|
|
@@ -3625,7 +3750,9 @@ function App({
|
|
|
3625
3750
|
rewindOverlay: null,
|
|
3626
3751
|
eternalStage: null,
|
|
3627
3752
|
goalSummary: null,
|
|
3628
|
-
autoPhase: null
|
|
3753
|
+
autoPhase: null,
|
|
3754
|
+
worktrees: {},
|
|
3755
|
+
worktreeMonitorOpen: false
|
|
3629
3756
|
});
|
|
3630
3757
|
const builderRef = useRef(null);
|
|
3631
3758
|
if (builderRef.current === null) {
|
|
@@ -3651,10 +3778,10 @@ function App({
|
|
|
3651
3778
|
const handleRewindTo = React2.useCallback(async (checkpointIndex) => {
|
|
3652
3779
|
const sessionId = agent.ctx.session.id;
|
|
3653
3780
|
if (!sessionId) return;
|
|
3654
|
-
const rewinder = new DefaultSessionRewinder(sessionsDir ?? "");
|
|
3781
|
+
const rewinder = new DefaultSessionRewinder(sessionsDir ?? "", projectRoot ?? agent.ctx.cwd);
|
|
3655
3782
|
await rewinder.rewindToCheckpoint(sessionId, checkpointIndex);
|
|
3656
3783
|
await agent.ctx.session.truncateToCheckpoint(checkpointIndex);
|
|
3657
|
-
}, [agent.ctx.session, sessionsDir]);
|
|
3784
|
+
}, [agent.ctx.session, sessionsDir, projectRoot, agent.ctx.cwd]);
|
|
3658
3785
|
const setDraft = (buffer, cursor) => {
|
|
3659
3786
|
draftRef.current = { buffer, cursor };
|
|
3660
3787
|
dispatch({ type: "setBuffer", buffer, cursor });
|
|
@@ -4490,6 +4617,45 @@ function App({
|
|
|
4490
4617
|
dispatch({ type: "autoPhaseReset" });
|
|
4491
4618
|
break;
|
|
4492
4619
|
}
|
|
4620
|
+
case "worktree.allocated": {
|
|
4621
|
+
const p = payload;
|
|
4622
|
+
dispatch({
|
|
4623
|
+
type: "worktreeUpsert",
|
|
4624
|
+
handleId: p.handleId,
|
|
4625
|
+
baseBranch: p.baseBranch,
|
|
4626
|
+
row: { branch: p.branch, ownerLabel: p.ownerLabel, baseBranch: p.baseBranch, status: "active", allocatedAt: Date.now() }
|
|
4627
|
+
});
|
|
4628
|
+
break;
|
|
4629
|
+
}
|
|
4630
|
+
case "worktree.committed": {
|
|
4631
|
+
const p = payload;
|
|
4632
|
+
dispatch({
|
|
4633
|
+
type: "worktreeUpsert",
|
|
4634
|
+
handleId: p.handleId,
|
|
4635
|
+
row: { insertions: p.insertions, deletions: p.deletions, files: p.files, status: "committing" }
|
|
4636
|
+
});
|
|
4637
|
+
break;
|
|
4638
|
+
}
|
|
4639
|
+
case "worktree.merged": {
|
|
4640
|
+
const p = payload;
|
|
4641
|
+
dispatch({ type: "worktreeUpsert", handleId: p.handleId, row: { status: "merged" } });
|
|
4642
|
+
break;
|
|
4643
|
+
}
|
|
4644
|
+
case "worktree.conflict": {
|
|
4645
|
+
const p = payload;
|
|
4646
|
+
dispatch({ type: "worktreeUpsert", handleId: p.handleId, row: { status: "needs-review", conflictFiles: p.conflictFiles } });
|
|
4647
|
+
break;
|
|
4648
|
+
}
|
|
4649
|
+
case "worktree.failed": {
|
|
4650
|
+
const p = payload;
|
|
4651
|
+
dispatch({ type: "worktreeUpsert", handleId: p.handleId, row: { status: "failed" } });
|
|
4652
|
+
break;
|
|
4653
|
+
}
|
|
4654
|
+
case "worktree.released": {
|
|
4655
|
+
const p = payload;
|
|
4656
|
+
if (!p.kept) dispatch({ type: "worktreeRemove", handleId: p.handleId });
|
|
4657
|
+
break;
|
|
4658
|
+
}
|
|
4493
4659
|
}
|
|
4494
4660
|
};
|
|
4495
4661
|
return subscribeAutoPhase(handler);
|
|
@@ -5023,6 +5189,10 @@ function App({
|
|
|
5023
5189
|
dispatch({ type: "autoPhaseMonitorToggle" });
|
|
5024
5190
|
return;
|
|
5025
5191
|
}
|
|
5192
|
+
if (key.ctrl && input === "t") {
|
|
5193
|
+
dispatch({ type: "worktreeMonitorToggle" });
|
|
5194
|
+
return;
|
|
5195
|
+
}
|
|
5026
5196
|
if (state.autonomyPicker.open) {
|
|
5027
5197
|
if (key.escape) {
|
|
5028
5198
|
dispatch({ type: "autonomyPickerClose" });
|
|
@@ -5266,6 +5436,10 @@ function App({
|
|
|
5266
5436
|
return;
|
|
5267
5437
|
}
|
|
5268
5438
|
if (key.ctrl && input === "w") {
|
|
5439
|
+
if (state.worktreeMonitorOpen) {
|
|
5440
|
+
dispatch({ type: "worktreeMonitorToggle" });
|
|
5441
|
+
return;
|
|
5442
|
+
}
|
|
5269
5443
|
if (cursor === 0) return;
|
|
5270
5444
|
const beforeCursor = buffer.slice(0, cursor);
|
|
5271
5445
|
const lastWordStart = beforeCursor.lastIndexOf(" ") + 1;
|
|
@@ -5752,6 +5926,14 @@ User message:
|
|
|
5752
5926
|
nowTick,
|
|
5753
5927
|
onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
|
|
5754
5928
|
}
|
|
5929
|
+
) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
|
|
5930
|
+
WorktreeMonitor,
|
|
5931
|
+
{
|
|
5932
|
+
worktrees: state.worktrees,
|
|
5933
|
+
baseBranch: state.worktreeBase,
|
|
5934
|
+
nowTick,
|
|
5935
|
+
onClose: () => dispatch({ type: "worktreeMonitorToggle" })
|
|
5936
|
+
}
|
|
5755
5937
|
) : state.monitorOpen ? /* @__PURE__ */ jsx(
|
|
5756
5938
|
FleetMonitor,
|
|
5757
5939
|
{
|
|
@@ -5768,7 +5950,8 @@ User message:
|
|
|
5768
5950
|
runningPhaseIds: state.autoPhase.runningPhaseIds,
|
|
5769
5951
|
nowTick
|
|
5770
5952
|
}
|
|
5771
|
-
) : null
|
|
5953
|
+
) : null,
|
|
5954
|
+
Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null
|
|
5772
5955
|
] });
|
|
5773
5956
|
}
|
|
5774
5957
|
function renderRunningTools(running) {
|
|
@@ -5803,6 +5986,75 @@ function fmtTok3(n) {
|
|
|
5803
5986
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
5804
5987
|
}
|
|
5805
5988
|
|
|
5989
|
+
// src/terminal-title.ts
|
|
5990
|
+
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5991
|
+
var setTitle = (s2) => `\x1B]0;${s2}\x07`;
|
|
5992
|
+
function shortModel(model) {
|
|
5993
|
+
if (!model) return "";
|
|
5994
|
+
const base = model.split("/").pop() ?? model;
|
|
5995
|
+
return base.replace(/-\d{8}$/, "").replace(/\[.*?\]$/, "");
|
|
5996
|
+
}
|
|
5997
|
+
function marquee(text, offset, width) {
|
|
5998
|
+
const padded = `${text} `;
|
|
5999
|
+
const start = offset % padded.length;
|
|
6000
|
+
return (padded + padded).slice(start, start + width);
|
|
6001
|
+
}
|
|
6002
|
+
function startTerminalTitle(opts) {
|
|
6003
|
+
const { stdout, events } = opts;
|
|
6004
|
+
if (process.env["WRONGSTACK_NO_TITLE"] === "1" || !stdout.isTTY) {
|
|
6005
|
+
return () => {
|
|
6006
|
+
};
|
|
6007
|
+
}
|
|
6008
|
+
const app = opts.appName ?? "WrongStack";
|
|
6009
|
+
const model = shortModel(opts.model);
|
|
6010
|
+
const idleAfter = opts.idleAfterMs ?? 3500;
|
|
6011
|
+
const suffix = model ? ` \xB7 ${model}` : "";
|
|
6012
|
+
let frame = 0;
|
|
6013
|
+
let scroll = 0;
|
|
6014
|
+
let phase = "idle";
|
|
6015
|
+
let toolName = "";
|
|
6016
|
+
let lastActivity = 0;
|
|
6017
|
+
const touch = (next, tool) => {
|
|
6018
|
+
phase = next;
|
|
6019
|
+
if (tool) toolName = tool;
|
|
6020
|
+
lastActivity = Date.now();
|
|
6021
|
+
};
|
|
6022
|
+
const offs = [
|
|
6023
|
+
events.on("iteration.started", () => touch("thinking")),
|
|
6024
|
+
events.on("provider.text_delta", () => touch("thinking")),
|
|
6025
|
+
events.on("provider.thinking_delta", () => touch("thinking")),
|
|
6026
|
+
events.on("tool.started", (e) => touch("tool", e.name ?? "tool")),
|
|
6027
|
+
events.on("tool.executed", () => touch("thinking"))
|
|
6028
|
+
];
|
|
6029
|
+
const write = (s2) => {
|
|
6030
|
+
try {
|
|
6031
|
+
stdout.write(s2);
|
|
6032
|
+
} catch {
|
|
6033
|
+
}
|
|
6034
|
+
};
|
|
6035
|
+
const timer = setInterval(() => {
|
|
6036
|
+
frame = (frame + 1) % SPINNER.length;
|
|
6037
|
+
scroll += 1;
|
|
6038
|
+
if (lastActivity && Date.now() - lastActivity > idleAfter) phase = "idle";
|
|
6039
|
+
const sp = SPINNER[frame];
|
|
6040
|
+
let title;
|
|
6041
|
+
if (phase === "tool") {
|
|
6042
|
+
title = `${sp} \u25B8 ${toolName}${suffix}`;
|
|
6043
|
+
} else if (phase === "thinking") {
|
|
6044
|
+
title = `${sp} thinking\u2026${suffix}`;
|
|
6045
|
+
} else {
|
|
6046
|
+
title = marquee(`\u2726 ${app}${suffix}`, scroll >> 1, 22);
|
|
6047
|
+
}
|
|
6048
|
+
write(setTitle(title));
|
|
6049
|
+
}, opts.intervalMs ?? 130);
|
|
6050
|
+
timer.unref?.();
|
|
6051
|
+
return () => {
|
|
6052
|
+
clearInterval(timer);
|
|
6053
|
+
for (const off of offs) off();
|
|
6054
|
+
write(setTitle(model ? `${app} \xB7 ${model}` : app));
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
|
|
5806
6058
|
// src/run-tui.ts
|
|
5807
6059
|
var BRACKETED_PASTE_ON = "\x1B[?2004h";
|
|
5808
6060
|
var BRACKETED_PASTE_OFF = "\x1B[?2004l";
|
|
@@ -5824,6 +6076,7 @@ async function runTui(opts) {
|
|
|
5824
6076
|
stdout.write(CURSOR_HOME);
|
|
5825
6077
|
}
|
|
5826
6078
|
stdout.write(BRACKETED_PASTE_ON);
|
|
6079
|
+
const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
|
|
5827
6080
|
const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
|
|
5828
6081
|
const swallow = () => {
|
|
5829
6082
|
};
|
|
@@ -5837,6 +6090,10 @@ async function runTui(opts) {
|
|
|
5837
6090
|
const cleanup = () => {
|
|
5838
6091
|
if (cleaned) return;
|
|
5839
6092
|
cleaned = true;
|
|
6093
|
+
try {
|
|
6094
|
+
stopTitle();
|
|
6095
|
+
} catch {
|
|
6096
|
+
}
|
|
5840
6097
|
try {
|
|
5841
6098
|
stdout.write(BRACKETED_PASTE_OFF);
|
|
5842
6099
|
if (useAltScreen) {
|