@wrongstack/tui 0.8.5 → 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.js +364 -10
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1151,18 +1151,18 @@ function PhasePanel({ phases, runningPhaseIds, nowTick }) {
|
|
|
1151
1151
|
] }),
|
|
1152
1152
|
list.map((phase, i) => {
|
|
1153
1153
|
const phaseKey = Object.keys(phases).find((k) => phases[k] === phase) ?? String(i);
|
|
1154
|
-
const
|
|
1154
|
+
const st2 = s(phase.status);
|
|
1155
1155
|
const progress = phase.totalTasks > 0 ? `${phase.completedTasks}/${phase.totalTasks}` : "";
|
|
1156
1156
|
const elapsed = phase.startedAt ? fmtElapsed3(nowTick - phase.startedAt) : "";
|
|
1157
1157
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1158
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
1158
|
+
/* @__PURE__ */ jsx(Text, { color: st2.color, children: st2.icon }),
|
|
1159
1159
|
/* @__PURE__ */ jsx(Text, { children: phase.name.slice(0, 14).padEnd(14) }),
|
|
1160
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
1160
|
+
/* @__PURE__ */ jsx(Text, { color: st2.color, dimColor: true, children: st2.icon === "\u25CF" ? phase.status : "" }),
|
|
1161
1161
|
progress ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1162
1162
|
" ",
|
|
1163
1163
|
progress
|
|
1164
1164
|
] }) : null,
|
|
1165
|
-
elapsed &&
|
|
1165
|
+
elapsed && st2.icon === "\u25CF" ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1166
1166
|
" ",
|
|
1167
1167
|
elapsed
|
|
1168
1168
|
] }) : null
|
|
@@ -1172,6 +1172,204 @@ function PhasePanel({ phases, runningPhaseIds, nowTick }) {
|
|
|
1172
1172
|
}
|
|
1173
1173
|
);
|
|
1174
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
|
+
}
|
|
1175
1373
|
|
|
1176
1374
|
// src/markdown-table.ts
|
|
1177
1375
|
function renderMarkdownTables(text, maxWidth) {
|
|
@@ -2350,7 +2548,7 @@ function Input({
|
|
|
2350
2548
|
hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
|
|
2351
2549
|
] });
|
|
2352
2550
|
}
|
|
2353
|
-
function
|
|
2551
|
+
function fmtElapsed6(ms) {
|
|
2354
2552
|
if (ms < 1e3) return `${ms}ms`;
|
|
2355
2553
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2356
2554
|
const m = Math.floor(ms / 6e4);
|
|
@@ -2365,7 +2563,7 @@ function fmtRecentTool2(tool) {
|
|
|
2365
2563
|
const status = tool.ok === false ? "fail" : "ok";
|
|
2366
2564
|
const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
|
|
2367
2565
|
const parts = [status, name];
|
|
2368
|
-
if (typeof tool.durationMs === "number") parts.push(
|
|
2566
|
+
if (typeof tool.durationMs === "number") parts.push(fmtElapsed6(tool.durationMs));
|
|
2369
2567
|
if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes3(tool.outputBytes));
|
|
2370
2568
|
if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
|
|
2371
2569
|
return parts.join(" ");
|
|
@@ -2386,7 +2584,7 @@ function LiveActivityStrip({
|
|
|
2386
2584
|
running.map((e) => {
|
|
2387
2585
|
const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
|
|
2388
2586
|
const taskElapsed = now - e.startedAt;
|
|
2389
|
-
const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${
|
|
2587
|
+
const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed6(toolElapsed)})` : "idle between tools";
|
|
2390
2588
|
const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
|
|
2391
2589
|
const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
|
|
2392
2590
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
@@ -2400,7 +2598,7 @@ function LiveActivityStrip({
|
|
|
2400
2598
|
"it ",
|
|
2401
2599
|
e.toolCalls,
|
|
2402
2600
|
"tc \xB7 ",
|
|
2403
|
-
|
|
2601
|
+
fmtElapsed6(taskElapsed)
|
|
2404
2602
|
] }),
|
|
2405
2603
|
recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2406
2604
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
|
|
@@ -3452,6 +3650,34 @@ function reducer(state, action) {
|
|
|
3452
3650
|
case "autoPhaseReset": {
|
|
3453
3651
|
return { ...state, autoPhase: null };
|
|
3454
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
|
+
}
|
|
3455
3681
|
}
|
|
3456
3682
|
}
|
|
3457
3683
|
var PASTE_THRESHOLD_CHARS = 200;
|
|
@@ -3625,7 +3851,9 @@ function App({
|
|
|
3625
3851
|
rewindOverlay: null,
|
|
3626
3852
|
eternalStage: null,
|
|
3627
3853
|
goalSummary: null,
|
|
3628
|
-
autoPhase: null
|
|
3854
|
+
autoPhase: null,
|
|
3855
|
+
worktrees: {},
|
|
3856
|
+
worktreeMonitorOpen: false
|
|
3629
3857
|
});
|
|
3630
3858
|
const builderRef = useRef(null);
|
|
3631
3859
|
if (builderRef.current === null) {
|
|
@@ -4490,6 +4718,45 @@ function App({
|
|
|
4490
4718
|
dispatch({ type: "autoPhaseReset" });
|
|
4491
4719
|
break;
|
|
4492
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
|
+
}
|
|
4493
4760
|
}
|
|
4494
4761
|
};
|
|
4495
4762
|
return subscribeAutoPhase(handler);
|
|
@@ -5023,6 +5290,10 @@ function App({
|
|
|
5023
5290
|
dispatch({ type: "autoPhaseMonitorToggle" });
|
|
5024
5291
|
return;
|
|
5025
5292
|
}
|
|
5293
|
+
if (key.ctrl && input === "t") {
|
|
5294
|
+
dispatch({ type: "worktreeMonitorToggle" });
|
|
5295
|
+
return;
|
|
5296
|
+
}
|
|
5026
5297
|
if (state.autonomyPicker.open) {
|
|
5027
5298
|
if (key.escape) {
|
|
5028
5299
|
dispatch({ type: "autonomyPickerClose" });
|
|
@@ -5752,6 +6023,14 @@ User message:
|
|
|
5752
6023
|
nowTick,
|
|
5753
6024
|
onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
|
|
5754
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
|
+
}
|
|
5755
6034
|
) : state.monitorOpen ? /* @__PURE__ */ jsx(
|
|
5756
6035
|
FleetMonitor,
|
|
5757
6036
|
{
|
|
@@ -5768,7 +6047,8 @@ User message:
|
|
|
5768
6047
|
runningPhaseIds: state.autoPhase.runningPhaseIds,
|
|
5769
6048
|
nowTick
|
|
5770
6049
|
}
|
|
5771
|
-
) : null
|
|
6050
|
+
) : null,
|
|
6051
|
+
Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null
|
|
5772
6052
|
] });
|
|
5773
6053
|
}
|
|
5774
6054
|
function renderRunningTools(running) {
|
|
@@ -5803,6 +6083,75 @@ function fmtTok3(n) {
|
|
|
5803
6083
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
5804
6084
|
}
|
|
5805
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
|
+
|
|
5806
6155
|
// src/run-tui.ts
|
|
5807
6156
|
var BRACKETED_PASTE_ON = "\x1B[?2004h";
|
|
5808
6157
|
var BRACKETED_PASTE_OFF = "\x1B[?2004l";
|
|
@@ -5824,6 +6173,7 @@ async function runTui(opts) {
|
|
|
5824
6173
|
stdout.write(CURSOR_HOME);
|
|
5825
6174
|
}
|
|
5826
6175
|
stdout.write(BRACKETED_PASTE_ON);
|
|
6176
|
+
const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
|
|
5827
6177
|
const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
|
|
5828
6178
|
const swallow = () => {
|
|
5829
6179
|
};
|
|
@@ -5837,6 +6187,10 @@ async function runTui(opts) {
|
|
|
5837
6187
|
const cleanup = () => {
|
|
5838
6188
|
if (cleaned) return;
|
|
5839
6189
|
cleaned = true;
|
|
6190
|
+
try {
|
|
6191
|
+
stopTitle();
|
|
6192
|
+
} catch {
|
|
6193
|
+
}
|
|
5840
6194
|
try {
|
|
5841
6195
|
stdout.write(BRACKETED_PASTE_OFF);
|
|
5842
6196
|
if (useAltScreen) {
|