@wrongstack/tui 0.104.0 → 0.109.1

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,7 +1,7 @@
1
- import { expectDefined, writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
1
+ import { expectDefined, writeErr, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
3
  import { Box, Text, useInput, useStdin, useStdout, render, useApp, Static } from 'ink';
4
- import React6, { useState, useEffect, memo, useReducer, useRef, useMemo, useCallback } from 'react';
4
+ import React6, { useState, useEffect, memo, useCallback, useReducer, useRef, useMemo } from 'react';
5
5
  import * as fs2 from 'fs/promises';
6
6
  import * as path2 from 'path';
7
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
@@ -913,10 +913,10 @@ function AgentsMonitor({
913
913
  const live = useMemo(() => selectLiveAgents(all, nowTick), [all, nowTick]);
914
914
  const [selectedIndex, setSelectedIndex] = useState(0);
915
915
  const safeIndex = Math.min(selectedIndex, Math.max(0, live.length - 1));
916
- useInput((input, key) => {
917
- if (key.upArrow || input === "k") {
916
+ useInput((_input, key) => {
917
+ if (key.upArrow) {
918
918
  setSelectedIndex((prev) => Math.max(0, prev - 1));
919
- } else if (key.downArrow || input === "j") {
919
+ } else if (key.downArrow) {
920
920
  setSelectedIndex((prev) => Math.min(live.length - 1, prev + 1));
921
921
  }
922
922
  });
@@ -2065,9 +2065,8 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
2065
2065
  for (let c = 0; c < cols; c++) {
2066
2066
  const cell = row[c] ?? "";
2067
2067
  const stripped = stripInlineMarkers(cell);
2068
- const w = longestWord(stripped);
2069
2068
  const total = strWidth(stripped);
2070
- natural[c] = Math.max(expectDefined$1(natural[c]), w, total);
2069
+ natural[c] = Math.max(expectDefined$1(natural[c]), total);
2071
2070
  }
2072
2071
  }
2073
2072
  if (sepWidths) {
@@ -2200,19 +2199,16 @@ function strWidth(s2) {
2200
2199
  i += cpLen;
2201
2200
  continue;
2202
2201
  }
2202
+ if (code >= 9472 && code <= 9599) {
2203
+ width += 1;
2204
+ i += cpLen;
2205
+ continue;
2206
+ }
2203
2207
  width += 1;
2204
2208
  i += cpLen;
2205
2209
  }
2206
2210
  return width;
2207
2211
  }
2208
- function longestWord(s2) {
2209
- let max = 0;
2210
- for (const w of s2.split(/\s+/)) {
2211
- const visualWidth = strWidth(w);
2212
- if (visualWidth > max) max = visualWidth;
2213
- }
2214
- return max;
2215
- }
2216
2212
  function border(left, mid, right, widths) {
2217
2213
  return left + widths.map((w) => "\u2500".repeat(w + 2)).join(mid) + right;
2218
2214
  }
@@ -2220,13 +2216,12 @@ function stripInlineMarkers(text) {
2220
2216
  return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "$1").replace(/`(.+?)`/g, "$1").replace(/~~(.+?)~~/g, "$1");
2221
2217
  }
2222
2218
  var ANSI_BOLD = "\x1B[1m";
2223
- var ANSI_RESET = "\x1B[22m";
2224
2219
  var ANSI_DIM = "\x1B[2m";
2225
2220
  var ANSI_CYAN = "\x1B[36m";
2226
2221
  var ANSI_STRIKE = "\x1B[9m";
2227
2222
  var ANSI_RESET_ALL = "\x1B[0m";
2228
2223
  function applyInlineAnsi(text) {
2229
- return text.replace(/\*\*(.+?)\*\*/g, `${ANSI_BOLD}$1${ANSI_RESET}`).replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `${ANSI_DIM}$1${ANSI_RESET_ALL}`).replace(/`(.+?)`/g, `${ANSI_CYAN}$1${ANSI_RESET_ALL}`).replace(/~~(.+?)~~/g, `${ANSI_STRIKE}$1${ANSI_RESET_ALL}`);
2224
+ return text.replace(/\*\*(.+?)\*\*/g, `${ANSI_BOLD}$1${ANSI_RESET_ALL}`).replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `${ANSI_DIM}$1${ANSI_RESET_ALL}`).replace(/`(.+?)`/g, `${ANSI_CYAN}$1${ANSI_RESET_ALL}`).replace(/~~(.+?)~~/g, `${ANSI_STRIKE}$1${ANSI_RESET_ALL}`);
2230
2225
  }
2231
2226
  function renderRow(cells, widths, aligns) {
2232
2227
  const styled = cells.map((c) => applyInlineAnsi(c));
@@ -2239,7 +2234,7 @@ function renderRow(cells, widths, aligns) {
2239
2234
  for (let c = 0; c < widths.length; c++) {
2240
2235
  const w = widths[c] ?? MIN_COL_WIDTH;
2241
2236
  const text = wrapped[c]?.[line] ?? "";
2242
- parts.push(padCell(text, w, aligns[c] ?? "left"));
2237
+ parts.push(padCell(text, w, aligns[c] ?? "left") + ANSI_RESET_ALL);
2243
2238
  }
2244
2239
  out.push("\u2502 " + parts.join(" \u2502 ") + " \u2502");
2245
2240
  }
@@ -3089,7 +3084,8 @@ function CodeBlock({
3089
3084
  const hidden = Math.max(0, lines.length - MAX_CODE_LINES);
3090
3085
  if (hidden > 0) lines = lines.slice(0, MAX_CODE_LINES);
3091
3086
  const gutterW = String(lines.length).length;
3092
- const maxW = Math.max(20, Math.min(contentWidth - 6 - gutterW - 1, 120));
3087
+ const boxWidth = Math.max(22, contentWidth - 2);
3088
+ const maxW = Math.max(20, Math.min(boxWidth - 4 - gutterW - 1, 120));
3093
3089
  let carry = {};
3094
3090
  const rows = lines.map((raw) => {
3095
3091
  const display = raw.length > maxW ? `${raw.slice(0, maxW - 1)}\u2026` : raw;
@@ -3101,6 +3097,8 @@ function CodeBlock({
3101
3097
  Box,
3102
3098
  {
3103
3099
  flexDirection: "column",
3100
+ width: boxWidth,
3101
+ flexShrink: 0,
3104
3102
  marginLeft: 2,
3105
3103
  marginY: 0,
3106
3104
  borderStyle: "round",
@@ -3224,6 +3222,10 @@ function extractDiffPreview(toolName, output) {
3224
3222
  return parseUnifiedDiff(diff, DIFF_MAX_LINES);
3225
3223
  }
3226
3224
  var MESSAGE_PANEL_CHROME_WIDTH = 2;
3225
+ var MESSAGE_PANEL_MARGIN = 2;
3226
+ function assistantContentWidth(termWidth) {
3227
+ return Math.max(20, termWidth - MESSAGE_PANEL_CHROME_WIDTH - MESSAGE_PANEL_MARGIN * 2);
3228
+ }
3227
3229
  function splitFencedBlocks(text) {
3228
3230
  const lines = text.split("\n");
3229
3231
  const segs = [];
@@ -3377,9 +3379,6 @@ function brainRiskColor(risk) {
3377
3379
  return "red";
3378
3380
  }
3379
3381
  }
3380
- function assistantContentWidth(termWidth) {
3381
- return Math.max(20, termWidth - MESSAGE_PANEL_CHROME_WIDTH);
3382
- }
3383
3382
  var Entry = React6.memo(function Entry2({
3384
3383
  entry,
3385
3384
  termWidth
@@ -3389,6 +3388,7 @@ var Entry = React6.memo(function Entry2({
3389
3388
  return /* @__PURE__ */ jsx(
3390
3389
  Box,
3391
3390
  {
3391
+ marginX: MESSAGE_PANEL_MARGIN,
3392
3392
  borderStyle: "single",
3393
3393
  borderTop: false,
3394
3394
  borderRight: false,
@@ -3415,6 +3415,7 @@ var Entry = React6.memo(function Entry2({
3415
3415
  Box,
3416
3416
  {
3417
3417
  flexDirection: "column",
3418
+ marginX: MESSAGE_PANEL_MARGIN,
3418
3419
  marginY: 1,
3419
3420
  borderStyle: "single",
3420
3421
  borderTop: false,
@@ -3488,6 +3489,7 @@ var Entry = React6.memo(function Entry2({
3488
3489
  return /* @__PURE__ */ jsx(
3489
3490
  Box,
3490
3491
  {
3492
+ marginX: MESSAGE_PANEL_MARGIN,
3491
3493
  borderStyle: "single",
3492
3494
  borderTop: false,
3493
3495
  borderRight: false,
@@ -3501,6 +3503,7 @@ var Entry = React6.memo(function Entry2({
3501
3503
  return /* @__PURE__ */ jsx(
3502
3504
  Box,
3503
3505
  {
3506
+ marginX: MESSAGE_PANEL_MARGIN,
3504
3507
  borderStyle: "single",
3505
3508
  borderTop: false,
3506
3509
  borderRight: false,
@@ -3519,6 +3522,7 @@ var Entry = React6.memo(function Entry2({
3519
3522
  Box,
3520
3523
  {
3521
3524
  flexDirection: "column",
3525
+ marginX: MESSAGE_PANEL_MARGIN,
3522
3526
  marginY: 1,
3523
3527
  borderStyle: "single",
3524
3528
  borderTop: false,
@@ -3799,6 +3803,8 @@ var Input = memo(function Input2({
3799
3803
  cursor,
3800
3804
  disabled,
3801
3805
  hint,
3806
+ hidden,
3807
+ placeholderHeight,
3802
3808
  onKey
3803
3809
  }) {
3804
3810
  useInput((input, key) => {
@@ -3854,6 +3860,9 @@ var Input = memo(function Input2({
3854
3860
  }, [stdout]);
3855
3861
  const promptColor = disabled ? "red" : "cyan";
3856
3862
  const rows = layoutInputRows(prompt, value, cursor, cols);
3863
+ if (hidden) {
3864
+ return /* @__PURE__ */ jsx(Box, { height: Math.max(1, placeholderHeight ?? rows.length) });
3865
+ }
3857
3866
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
3858
3867
  rows.map(
3859
3868
  (row, i) => row.length === 0 ? (
@@ -3922,8 +3931,8 @@ var LiveActivityStrip = React6.memo(function LiveActivityStrip2({
3922
3931
  }) {
3923
3932
  const { stdout } = useStdout();
3924
3933
  const width = Math.max(10, (stdout?.columns ?? 80) - 2);
3925
- if (Object.keys(entries).length === 0) return null;
3926
- const rows = activityStripRows(entries, Date.now(), maxRows, width);
3934
+ const hasEntries = Object.keys(entries).length > 0;
3935
+ const rows = hasEntries ? activityStripRows(entries, Date.now(), maxRows, width) : new Array(maxRows).fill("");
3927
3936
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: rows.map((text, slot) => (
3928
3937
  // biome-ignore lint/suspicious/noArrayIndexKey: fixed-height slots, index IS the identity
3929
3938
  /* @__PURE__ */ jsx(Box, { height: 1, children: text === "" ? /* @__PURE__ */ jsx(Text, { children: " " }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -7310,6 +7319,7 @@ function App({
7310
7319
  getModeLabel
7311
7320
  }) {
7312
7321
  const { exit } = useApp();
7322
+ const { stdout } = useStdout();
7313
7323
  const [liveModel, setLiveModel] = useState(model);
7314
7324
  const [liveProvider, setLiveProvider] = useState(provider ?? "agent");
7315
7325
  const [activeMaxContext, setActiveMaxContext] = useState(effectiveMaxContext);
@@ -7329,32 +7339,33 @@ function App({
7329
7339
  setStatuslineHiddenItems(hiddenItems);
7330
7340
  }, [setStatuslineHiddenItems, hiddenItems]);
7331
7341
  const projectRoot = agent.ctx.projectRoot;
7332
- useEffect(() => {
7342
+ const refreshGoalSummary = useCallback(() => {
7333
7343
  if (!projectRoot) return;
7334
- const goalPath = path2.join(projectRoot, ".wrongstack", "goal.json");
7335
- fs2.readFile(goalPath, "utf8").then((raw) => {
7336
- const goal = JSON.parse(raw);
7337
- if (goal?.goal && typeof goal.iterations === "number") {
7338
- const lastEntry = goal.journal?.[goal.journal.length - 1];
7339
- dispatch({
7340
- type: "goalSummary",
7341
- summary: {
7342
- goal: goal.goal,
7343
- refinedGoal: goal.refinedGoal,
7344
- goalState: goal.goalState ?? "active",
7345
- iterations: goal.iterations,
7346
- progress: goal.progress,
7347
- progressNote: goal.progressNote,
7348
- progressTrend: goal.progressTrend,
7349
- deliverables: goal.deliverables,
7350
- lastTask: lastEntry?.task,
7351
- lastStatus: lastEntry?.status
7352
- }
7353
- });
7354
- }
7344
+ const goalPath = resolveWstackPaths({ projectRoot }).projectGoal;
7345
+ loadGoal(goalPath).then((goal) => {
7346
+ if (!goal) return;
7347
+ const lastEntry = goal.journal?.[goal.journal.length - 1];
7348
+ dispatch({
7349
+ type: "goalSummary",
7350
+ summary: {
7351
+ goal: goal.goal,
7352
+ refinedGoal: goal.refinedGoal,
7353
+ goalState: goal.goalState ?? "active",
7354
+ iterations: goal.iterations,
7355
+ progress: goal.progress,
7356
+ progressNote: goal.progressNote,
7357
+ progressTrend: goal.progressTrend,
7358
+ deliverables: goal.deliverables,
7359
+ lastTask: lastEntry?.task,
7360
+ lastStatus: lastEntry?.status
7361
+ }
7362
+ });
7355
7363
  }).catch(() => {
7356
7364
  });
7357
7365
  }, [projectRoot]);
7366
+ useEffect(() => {
7367
+ refreshGoalSummary();
7368
+ }, [refreshGoalSummary]);
7358
7369
  const restoredEntries = (() => {
7359
7370
  const msgs = agent.ctx.messages;
7360
7371
  if (!msgs || msgs.length === 0) return [];
@@ -7504,6 +7515,9 @@ function App({
7504
7515
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
7505
7516
  return () => clearInterval(t);
7506
7517
  }, []);
7518
+ useEffect(() => {
7519
+ if (state.goalPanelOpen) refreshGoalSummary();
7520
+ }, [state.goalPanelOpen, nowTick, refreshGoalSummary]);
7507
7521
  const [enhanceDots, setEnhanceDots] = useState(0);
7508
7522
  useEffect(() => {
7509
7523
  if (!state.enhanceBusy) return;
@@ -8979,18 +8993,10 @@ function App({
8979
8993
  dispatch({ type: "toggleMonitor" });
8980
8994
  return;
8981
8995
  }
8982
- if (state.worktreeMonitorOpen) {
8983
- dispatch({ type: "worktreeMonitorToggle" });
8984
- return;
8985
- }
8986
8996
  if (state.todosMonitorOpen) {
8987
8997
  dispatch({ type: "toggleTodosMonitor" });
8988
8998
  return;
8989
8999
  }
8990
- if (state.autoPhase?.monitorOpen) {
8991
- dispatch({ type: "autoPhaseMonitorToggle" });
8992
- return;
8993
- }
8994
9000
  if (state.settingsPicker.open) {
8995
9001
  dispatch({ type: "settingsClose" });
8996
9002
  return;
@@ -9008,6 +9014,9 @@ function App({
9008
9014
  return;
9009
9015
  }
9010
9016
  }
9017
+ if (state.processListOpen) {
9018
+ return;
9019
+ }
9011
9020
  if (input === "?" && !key.ctrl && !key.meta && draftRef.current.buffer === "" && !state.slashPicker.open && !state.picker.open && !state.modelPicker.open && !state.autonomyPicker.open && !state.settingsPicker.open && !state.rewindOverlay && !state.monitorOpen && !state.agentsMonitorOpen && !state.worktreeMonitorOpen && !state.todosMonitorOpen && !state.autoPhase?.monitorOpen) {
9012
9021
  dispatch({ type: "toggleHelp" });
9013
9022
  return;
@@ -9601,6 +9610,14 @@ User message:
9601
9610
  return "";
9602
9611
  }, [state.buffer, state.status, state.picker.open]);
9603
9612
  const enhanceActive = state.enhanceBusy || state.enhance != null;
9613
+ const inputCellRows = layoutInputRows(
9614
+ INPUT_PROMPT,
9615
+ state.buffer,
9616
+ state.cursor,
9617
+ stdout?.columns ?? 80
9618
+ );
9619
+ const inputHeight = Math.max(1, inputCellRows.length);
9620
+ const hideInput = enhanceActive || state.helpOpen || state.processListOpen;
9604
9621
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9605
9622
  /* @__PURE__ */ jsx(
9606
9623
  History,
@@ -9612,12 +9629,14 @@ User message:
9612
9629
  ),
9613
9630
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
9614
9631
  /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
9615
- enhanceActive ? /* @__PURE__ */ jsx(Box, { height: 1 }) : /* @__PURE__ */ jsx(
9632
+ /* @__PURE__ */ jsx(
9616
9633
  Input,
9617
9634
  {
9618
9635
  prompt: INPUT_PROMPT,
9619
9636
  value: state.buffer,
9620
9637
  cursor: state.cursor,
9638
+ hidden: hideInput,
9639
+ placeholderHeight: inputHeight,
9621
9640
  disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
9622
9641
  hint: inputHint,
9623
9642
  onKey: stableOnKey