@wrongstack/tui 0.32.0 → 0.41.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 CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Readable } from 'stream';
2
+ import { writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, buildChildEnv } from '@wrongstack/core';
3
+ export { buildGoalPreamble } from '@wrongstack/core';
2
4
  import { Box, Text, render, useApp, useStdout, measureElement, Static, useInput, useStdin } from 'ink';
3
5
  import React4, { useState, useEffect, useReducer, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
4
6
  import * as fs2 from 'fs/promises';
5
7
  import * as path2 from 'path';
6
- import { InputBuilder, DefaultSessionRewinder, formatTodosList, buildGoalPreamble, buildChildEnv } from '@wrongstack/core';
7
- export { buildGoalPreamble } from '@wrongstack/core';
8
8
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
9
9
  import { getProcessRegistry } from '@wrongstack/tools';
10
10
  import { readClipboardImage } from '@wrongstack/runtime/clipboard';
@@ -526,6 +526,11 @@ function fmtCost(n) {
526
526
  if (n === 0) return "\u2014";
527
527
  return `$${n.toFixed(3)}`;
528
528
  }
529
+ function fmtModelLabel(provider, model) {
530
+ if (!model && !provider) return "";
531
+ const short = model ? model.includes("/") ? model.slice(model.lastIndexOf("/") + 1) : model : "?";
532
+ return provider ? `${provider}:${short}` : short;
533
+ }
529
534
  function FleetMonitor({
530
535
  entries,
531
536
  totalCost,
@@ -598,7 +603,7 @@ function FleetMonitor({
598
603
  "\u2717",
599
604
  failed
600
605
  ] }) : null,
601
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+F to close" })
606
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+F / F2 to close" })
602
607
  ] }),
603
608
  collabSession ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
604
609
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
@@ -658,20 +663,24 @@ function FleetMonitor({
658
663
  shown.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
659
664
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
660
665
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
661
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "name".padEnd(16) }),
662
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "status".padEnd(10) }),
663
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "L/t".padEnd(8) }),
666
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "name".padEnd(14) }),
667
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "model".padEnd(18) }),
668
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "status".padEnd(9) }),
669
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "L/t\xB7ctx".padEnd(12) }),
664
670
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "elapsed".padEnd(8) }),
665
671
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "cost" })
666
672
  ] }),
667
673
  shown.map((e) => {
668
674
  const s2 = STATUS[e.status];
669
675
  const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : fmtElapsed(Math.max(0, nowTick - e.lastEventAt)) + " ago";
676
+ const model = fmtModelLabel(e.provider, e.model) || "\u2014";
677
+ const ltCtx = e.ctxPct !== void 0 ? `L${e.iterations} ${e.toolCalls}t ${Math.round(e.ctxPct * 100)}%` : `L${e.iterations} ${e.toolCalls}t`;
670
678
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
671
679
  /* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.icon }),
672
- /* @__PURE__ */ jsx(Text, { children: e.name.padEnd(16).slice(0, 16) }),
673
- /* @__PURE__ */ jsx(Text, { color: s2.color, children: e.status.padEnd(10) }),
674
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: `L${e.iterations} ${e.toolCalls}t`.padEnd(8) }),
680
+ /* @__PURE__ */ jsx(Text, { children: e.name.padEnd(14).slice(0, 14) }),
681
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: model.padEnd(18).slice(0, 18) }),
682
+ /* @__PURE__ */ jsx(Text, { color: s2.color, children: e.status.padEnd(9) }),
683
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ltCtx.padEnd(12).slice(0, 12) }),
675
684
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(8).slice(0, 8) }),
676
685
  /* @__PURE__ */ jsx(Text, { color: "yellow", children: fmtCost(e.cost) }),
677
686
  e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
@@ -786,7 +795,7 @@ function AgentsMonitor({
786
795
  "\u2717",
787
796
  totalFailed
788
797
  ] }) : null,
789
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+G to close" })
798
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+G / F3 to close" })
790
799
  ] }),
791
800
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
792
801
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "shown" }),
@@ -821,6 +830,7 @@ function AgentsMonitor({
821
830
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
822
831
  /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
823
832
  /* @__PURE__ */ jsx(Text, { bold: true, children: e.name }),
833
+ fmtModelLabel(e.provider, e.model) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtModelLabel(e.provider, e.model) }) : null,
824
834
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
825
835
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed }),
826
836
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
@@ -831,6 +841,10 @@ function AgentsMonitor({
831
841
  e.toolCalls,
832
842
  "t"
833
843
  ] }),
844
+ e.ctxPct !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
845
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
846
+ /* @__PURE__ */ jsx(ContextBar, { pct: e.ctxPct, tokens: e.ctxTokens, maxTokens: e.ctxMaxTokens })
847
+ ] }) : null,
834
848
  e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
835
849
  "\u26A1\xD7",
836
850
  e.extensions
@@ -877,11 +891,7 @@ function AgentsMonitor({
877
891
  e.failureReason && e.status !== "success" ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "red", children: [
878
892
  "\u2717 ",
879
893
  e.failureReason
880
- ] }) }) : null,
881
- e.ctxPct !== void 0 ? /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, children: [
882
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ctx " }),
883
- /* @__PURE__ */ jsx(ContextBar, { pct: e.ctxPct, tokens: e.ctxTokens, maxTokens: e.ctxMaxTokens })
884
- ] }) : null
894
+ ] }) }) : null
885
895
  ] }, e.id);
886
896
  })
887
897
  ] });
@@ -1041,7 +1051,7 @@ function ConfirmPrompt({
1041
1051
  onDecision
1042
1052
  }) {
1043
1053
  React4.useEffect(() => {
1044
- process.stdout.write("\x07");
1054
+ writeOut("\x07");
1045
1055
  }, []);
1046
1056
  useInput((input2, _key) => {
1047
1057
  if (!input2 || input2 === "\r" || input2 === "\n") return;
@@ -1182,9 +1192,9 @@ function helpSections(opts) {
1182
1192
  {
1183
1193
  title: "Monitors",
1184
1194
  entries: [
1185
- { keys: "Ctrl+F", desc: "fleet orchestration monitor" },
1186
- { keys: "Ctrl+G", desc: "agents live monitor" },
1187
- { keys: "Ctrl+T", desc: "worktree monitor" },
1195
+ { keys: "Ctrl+F / F2", desc: "fleet orchestration monitor" },
1196
+ { keys: "Ctrl+G / F3", desc: "agents live monitor" },
1197
+ { keys: "Ctrl+T / F4", desc: "worktree monitor" },
1188
1198
  { keys: "Esc", desc: "close the open monitor / overlay" }
1189
1199
  ]
1190
1200
  },
@@ -3181,6 +3191,42 @@ function truncMid(s2, max) {
3181
3191
  return `${s2.slice(0, max - 1)}\u2026`;
3182
3192
  }
3183
3193
 
3194
+ // src/fn-keys.ts
3195
+ function fnKey(data) {
3196
+ switch (data) {
3197
+ case "\x1BOP":
3198
+ case "\x1B[11~":
3199
+ return 1;
3200
+ case "\x1BOQ":
3201
+ case "\x1B[12~":
3202
+ return 2;
3203
+ case "\x1BOR":
3204
+ case "\x1B[13~":
3205
+ return 3;
3206
+ case "\x1BOS":
3207
+ case "\x1B[14~":
3208
+ return 4;
3209
+ case "\x1B[15~":
3210
+ return 5;
3211
+ case "\x1B[17~":
3212
+ return 6;
3213
+ case "\x1B[18~":
3214
+ return 7;
3215
+ case "\x1B[19~":
3216
+ return 8;
3217
+ case "\x1B[20~":
3218
+ return 9;
3219
+ case "\x1B[21~":
3220
+ return 10;
3221
+ case "\x1B[23~":
3222
+ return 11;
3223
+ case "\x1B[24~":
3224
+ return 12;
3225
+ default:
3226
+ return null;
3227
+ }
3228
+ }
3229
+
3184
3230
  // src/input-tokens.ts
3185
3231
  var INLINE_TOKEN_SRC = "\\[(?:pasted|image|file) #\\d+[^\\]]*\\]|\\[file:[^\\]]+\\]";
3186
3232
  var AT_END = new RegExp(`(?:${INLINE_TOKEN_SRC})$`);
@@ -3375,9 +3421,18 @@ function Input({
3375
3421
  useEffect(() => {
3376
3422
  if (!stdin || disabled) return;
3377
3423
  const handleData = (data) => {
3378
- const kind = isHomeEnd(data.toString());
3379
- if (kind === "home") onKey("", { ...EMPTY_KEY, home: true });
3380
- else if (kind === "end") onKey("", { ...EMPTY_KEY, end: true });
3424
+ const s2 = data.toString();
3425
+ const kind = isHomeEnd(s2);
3426
+ if (kind === "home") {
3427
+ onKey("", { ...EMPTY_KEY, home: true });
3428
+ return;
3429
+ }
3430
+ if (kind === "end") {
3431
+ onKey("", { ...EMPTY_KEY, end: true });
3432
+ return;
3433
+ }
3434
+ const fn = fnKey(s2);
3435
+ if (fn !== null) onKey("", { ...EMPTY_KEY, fn });
3381
3436
  };
3382
3437
  stdin.on("data", handleData);
3383
3438
  return () => {
@@ -3974,7 +4029,7 @@ function WorktreeMonitor({
3974
4029
  failed
3975
4030
  ] })
3976
4031
  ] }) : null,
3977
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+T / Esc to close" })
4032
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+T / F4 / Esc to close" })
3978
4033
  ] }),
3979
4034
  list.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No worktrees. They appear when AutoPhase runs with isolation on." }) : list.map((w) => {
3980
4035
  const s2 = fmt(w.status);
@@ -5990,7 +6045,7 @@ function App({
5990
6045
  const prevEntriesCount = useRef(0);
5991
6046
  const eraseLiveRegion = useCallback(() => {
5992
6047
  try {
5993
- process.stdout.write("\x1B[J");
6048
+ writeOut("\x1B[J");
5994
6049
  } catch {
5995
6050
  }
5996
6051
  }, []);
@@ -6202,7 +6257,7 @@ function App({
6202
6257
  const arg = args.trim().toLowerCase();
6203
6258
  if (arg === "off") {
6204
6259
  try {
6205
- process.stdout.write(ALT_OFF);
6260
+ writeOut(ALT_OFF);
6206
6261
  } catch {
6207
6262
  return { message: "Failed to exit alt-screen." };
6208
6263
  }
@@ -6213,7 +6268,7 @@ function App({
6213
6268
  }
6214
6269
  if (arg === "on") {
6215
6270
  try {
6216
- process.stdout.write(ALT_ON);
6271
+ writeOut(ALT_ON);
6217
6272
  } catch {
6218
6273
  return { message: "Failed to re-enter alt-screen." };
6219
6274
  }
@@ -6254,9 +6309,9 @@ function App({
6254
6309
  };
6255
6310
  }
6256
6311
  try {
6257
- process.stdout.write(ALT_ON);
6258
- process.stdout.write("\x1B[H");
6259
- process.stdout.write(MOUSE_ON_SEQ);
6312
+ writeOut(ALT_ON);
6313
+ writeOut("\x1B[H");
6314
+ writeOut(MOUSE_ON_SEQ);
6260
6315
  } catch {
6261
6316
  return { message: "Failed to enable mouse mode." };
6262
6317
  }
@@ -6267,8 +6322,8 @@ function App({
6267
6322
  };
6268
6323
  }
6269
6324
  try {
6270
- process.stdout.write(MOUSE_OFF_SEQ);
6271
- process.stdout.write(ALT_OFF);
6325
+ writeOut(MOUSE_OFF_SEQ);
6326
+ writeOut(ALT_OFF);
6272
6327
  } catch {
6273
6328
  return { message: "Failed to disable mouse mode." };
6274
6329
  }
@@ -7728,25 +7783,23 @@ function App({
7728
7783
  });
7729
7784
  return;
7730
7785
  }
7731
- if (key.ctrl && input === "f") {
7786
+ const toggleFleetOverlay = () => {
7732
7787
  if (state.agentsMonitorOpen) {
7733
7788
  dispatch({ type: "toggleAgentsMonitor" });
7734
7789
  dispatch({ type: "toggleMonitor" });
7735
7790
  } else {
7736
7791
  dispatch({ type: "toggleMonitor" });
7737
7792
  }
7738
- return;
7739
- }
7740
- if (key.ctrl && input === "g") {
7793
+ };
7794
+ const toggleAgentsOverlay = () => {
7741
7795
  if (state.monitorOpen) {
7742
7796
  dispatch({ type: "toggleMonitor" });
7743
7797
  dispatch({ type: "toggleAgentsMonitor" });
7744
7798
  } else {
7745
7799
  dispatch({ type: "toggleAgentsMonitor" });
7746
7800
  }
7747
- return;
7748
- }
7749
- if (key.ctrl && input === "t") {
7801
+ };
7802
+ const toggleWorktreeOverlay = () => {
7750
7803
  if (state.worktreeMonitorOpen) {
7751
7804
  dispatch({ type: "worktreeMonitorToggle" });
7752
7805
  return;
@@ -7755,6 +7808,17 @@ function App({
7755
7808
  if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
7756
7809
  if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
7757
7810
  dispatch({ type: "worktreeMonitorToggle" });
7811
+ };
7812
+ if (key.ctrl && input === "f" || key.fn === 2) {
7813
+ toggleFleetOverlay();
7814
+ return;
7815
+ }
7816
+ if (key.ctrl && input === "g" || key.fn === 3) {
7817
+ toggleAgentsOverlay();
7818
+ return;
7819
+ }
7820
+ if (key.ctrl && input === "t" || key.fn === 4) {
7821
+ toggleWorktreeOverlay();
7758
7822
  return;
7759
7823
  }
7760
7824
  if (key.ctrl && input === "s") {
@@ -7910,6 +7974,7 @@ function App({
7910
7974
  return;
7911
7975
  }
7912
7976
  if (!input || key.ctrl || key.meta) return;
7977
+ if (input.charCodeAt(0) === 27) return;
7913
7978
  if (input.length > PASTE_THRESHOLD_CHARS) {
7914
7979
  await commitPaste(input);
7915
7980
  return;
@@ -8634,7 +8699,7 @@ async function runTui(opts) {
8634
8699
  const stdout = process.stdout;
8635
8700
  const stdin = process.stdin;
8636
8701
  if (!stdout.isTTY || !stdin.isTTY) {
8637
- process.stderr.write(
8702
+ writeErr(
8638
8703
  "wstack: --tui requires an interactive terminal on both stdin and stdout.\n Drop the flag (use the plain REPL) or run wstack directly without piping.\n"
8639
8704
  );
8640
8705
  return 2;
@@ -8861,7 +8926,7 @@ async function runTui(opts) {
8861
8926
  { exitOnCtrlC: false, stdin: inkStdin }
8862
8927
  );
8863
8928
  } catch (err) {
8864
- process.stderr.write(
8929
+ writeErr(
8865
8930
  `wstack: TUI failed to start: ${err instanceof Error ? err.message : String(err)}
8866
8931
  `
8867
8932
  );