@wrongstack/tui 0.84.1 → 0.87.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
@@ -1338,6 +1338,47 @@ function EnhancePanel({
1338
1338
  ] }) })
1339
1339
  ] });
1340
1340
  }
1341
+ function EscConfirmPrompt({
1342
+ runningTools,
1343
+ subagentCount,
1344
+ onConfirm,
1345
+ onCancel
1346
+ }) {
1347
+ useInput((input, key) => {
1348
+ if (key.escape) {
1349
+ onCancel();
1350
+ return;
1351
+ }
1352
+ const ch = input.toLowerCase();
1353
+ if (ch === "y" || key.return) {
1354
+ onConfirm();
1355
+ } else if (ch === "n") {
1356
+ onCancel();
1357
+ }
1358
+ });
1359
+ const running = runningTools.length;
1360
+ const toolLabel = running === 1 ? runningTools[0] : null;
1361
+ const toolHint = toolLabel ? ` (${toolLabel})` : running > 1 ? ` (${running} tools)` : "";
1362
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1, children: [
1363
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "\u23F8 Interrupt the current run?" }) }),
1364
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1365
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1366
+ "The agent is working",
1367
+ toolHint,
1368
+ ".",
1369
+ subagentCount > 0 ? ` ${subagentCount} subagent${subagentCount === 1 ? "" : "s"} active.` : ""
1370
+ ] }),
1371
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Stop to give a new direction now, or let it finish." })
1372
+ ] }),
1373
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
1374
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsxs(Text, { children: [
1375
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "[y]" }),
1376
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "es \u2014 stop and steer " }),
1377
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "red", children: "[n]" }),
1378
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "o / Esc \u2014 keep running" })
1379
+ ] }) })
1380
+ ] });
1381
+ }
1341
1382
  function FilePicker({ query, matches, selected }) {
1342
1383
  if (matches.length === 0) {
1343
1384
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -4284,7 +4325,7 @@ function SettingsPicker({
4284
4325
  {
4285
4326
  label: "Confirm before exit",
4286
4327
  value: boolVal(confirmExit),
4287
- detail: "Ask for confirmation on Ctrl+C exit"
4328
+ detail: "Confirmation on Esc interrupt & Ctrl+C exit"
4288
4329
  },
4289
4330
  {
4290
4331
  label: "Next-step prediction",
@@ -4419,10 +4460,9 @@ function SettingsPicker({
4419
4460
  hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
4420
4461
  ] });
4421
4462
  }
4463
+ var MAX_VISIBLE_ITEMS = 8;
4422
4464
  function SlashMenu({ query, matches, selected }) {
4423
4465
  const placeholder = query ? `/${query}` : "/";
4424
- const { stdout } = useStdout();
4425
- const termRows = stdout?.rows ?? 24;
4426
4466
  const rows = [];
4427
4467
  let lastCategory = "";
4428
4468
  for (let i = 0; i < matches.length; i++) {
@@ -4433,21 +4473,18 @@ function SlashMenu({ query, matches, selected }) {
4433
4473
  }
4434
4474
  rows.push({ type: "item", match: m, index: i });
4435
4475
  }
4436
- const overhead = 1 + 2 + 2 + 2 + 6;
4437
- const maxBodyRows = Math.max(4, termRows - overhead);
4438
4476
  const selectedRowIdx = rows.findIndex((r) => r.type === "item" && r.index === selected);
4439
- const visible = windowRows(rows, selectedRowIdx < 0 ? 0 : selectedRowIdx, maxBodyRows);
4477
+ const visible = windowRows(rows, selectedRowIdx < 0 ? 0 : selectedRowIdx, MAX_VISIBLE_ITEMS);
4440
4478
  const hiddenAbove = visible.start;
4441
4479
  const hiddenBelow = rows.length - visible.end;
4442
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4480
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
4443
4481
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4444
- placeholder || "/",
4445
- " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close",
4446
- matches.length > 0 ? ` (${selected + 1}/${matches.length})` : ""
4482
+ placeholder,
4483
+ " ",
4484
+ matches.length > 0 ? `(${selected + 1}/${matches.length})` : ""
4447
4485
  ] }),
4448
4486
  hiddenAbove > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4449
- " ",
4450
- "\u2191 ",
4487
+ " \u2191 ",
4451
4488
  hiddenAbove,
4452
4489
  " more"
4453
4490
  ] }),
@@ -4477,12 +4514,12 @@ function SlashMenu({ query, matches, selected }) {
4477
4514
  ] }, m.name);
4478
4515
  }),
4479
4516
  hiddenBelow > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4480
- " ",
4481
- "\u2193 ",
4517
+ " \u2193 ",
4482
4518
  hiddenBelow,
4483
4519
  " more"
4484
4520
  ] }),
4485
- matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
4521
+ matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" }),
4522
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500 \u2191\u2193 nav \xB7 Enter run \xB7 Tab fill \xB7 Esc close" })
4486
4523
  ] });
4487
4524
  }
4488
4525
  function windowRows(rows, focus, max) {
@@ -5771,6 +5808,10 @@ function reducer(state, action) {
5771
5808
  return { ...state, enhanceEnabled: action.enabled };
5772
5809
  case "enhanceBusy":
5773
5810
  return { ...state, enhanceBusy: action.on };
5811
+ case "escConfirmOpen":
5812
+ return { ...state, escConfirm: { snapshot: action.snapshot } };
5813
+ case "escConfirmClose":
5814
+ return { ...state, escConfirm: null };
5774
5815
  case "resetContextChip":
5775
5816
  return { ...state, contextChipVersion: state.contextChipVersion + 1 };
5776
5817
  // --- Fleet ---
@@ -6447,7 +6488,7 @@ function App({
6447
6488
  confirmExit = true,
6448
6489
  enhanceEnabled = true,
6449
6490
  enhanceController,
6450
- enhanceDelayMs = 4e3,
6491
+ enhanceDelayMs = 15e3,
6451
6492
  getYolo,
6452
6493
  getAutonomy,
6453
6494
  getEternalEngine,
@@ -6585,6 +6626,7 @@ function App({
6585
6626
  enhance: null,
6586
6627
  enhanceEnabled,
6587
6628
  enhanceBusy: false,
6629
+ escConfirm: null,
6588
6630
  contextChipVersion: 0,
6589
6631
  fleet: {},
6590
6632
  leader: {
@@ -6807,7 +6849,7 @@ function App({
6807
6849
  }
6808
6850
  }, []);
6809
6851
  React6.useLayoutEffect(() => {
6810
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.confirmQueue.length > 0;
6852
+ const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.escConfirm != null || state.confirmQueue.length > 0;
6811
6853
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
6812
6854
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
6813
6855
  const curToolStreamLen = state.toolStream?.text.length ?? 0;
@@ -6826,6 +6868,7 @@ function App({
6826
6868
  state.settingsPicker.open,
6827
6869
  state.enhanceBusy,
6828
6870
  state.enhance,
6871
+ state.escConfirm,
6829
6872
  state.confirmQueue.length,
6830
6873
  state.entries.length,
6831
6874
  state.toolStream?.text,
@@ -8116,6 +8159,7 @@ function App({
8116
8159
  return;
8117
8160
  }
8118
8161
  if (state.enhance) return;
8162
+ if (state.escConfirm) return;
8119
8163
  if (state.helpOpen) {
8120
8164
  if (key.escape || input === "?" || input === "q") dispatch({ type: "toggleHelp" });
8121
8165
  return;
@@ -8355,17 +8399,26 @@ function App({
8355
8399
  }));
8356
8400
  const subagentsTerminated = subagents.length;
8357
8401
  const partialAssistantText = streamingTextRef.current.slice(-1500);
8402
+ const snapshot = {
8403
+ runningTools,
8404
+ subagents,
8405
+ subagentsTerminated,
8406
+ partialAssistantText
8407
+ };
8408
+ if (confirmExitRef.current) {
8409
+ dispatch({ type: "escConfirmOpen", snapshot });
8410
+ dispatch({
8411
+ type: "addEntry",
8412
+ entry: {
8413
+ kind: "warn",
8414
+ text: `\u23F8 Interrupt? [y]es \u2014 stop and steer \xB7 [n]o / Esc \u2014 keep running` + (subagentsTerminated > 0 ? ` (${subagentsTerminated} subagent${subagentsTerminated === 1 ? "" : "s"})` : "")
8415
+ }
8416
+ });
8417
+ return;
8418
+ }
8358
8419
  activeCtrlRef.current?.abort();
8359
8420
  dispatch({ type: "status", status: "aborting" });
8360
- dispatch({
8361
- type: "steerStart",
8362
- snapshot: {
8363
- runningTools,
8364
- subagents,
8365
- subagentsTerminated,
8366
- partialAssistantText
8367
- }
8368
- });
8421
+ dispatch({ type: "steerStart", snapshot });
8369
8422
  if (director && subagentsTerminated > 0) {
8370
8423
  const cap = new Promise((resolve) => {
8371
8424
  const t = setTimeout(resolve, 1500);
@@ -9145,14 +9198,14 @@ User message:
9145
9198
  ),
9146
9199
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
9147
9200
  /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
9148
- /* @__PURE__ */ jsx(
9201
+ enhanceActive ? /* @__PURE__ */ jsx(Box, { height: 1 }) : /* @__PURE__ */ jsx(
9149
9202
  Input,
9150
9203
  {
9151
9204
  prompt: INPUT_PROMPT,
9152
- value: enhanceActive ? "" : state.buffer,
9153
- cursor: enhanceActive ? 0 : state.cursor,
9205
+ value: state.buffer,
9206
+ cursor: state.cursor,
9154
9207
  disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
9155
- hint: enhanceActive ? "" : inputHint,
9208
+ hint: inputHint,
9156
9209
  onKey: handleKey
9157
9210
  }
9158
9211
  ),
@@ -9264,6 +9317,41 @@ User message:
9264
9317
  }
9265
9318
  );
9266
9319
  })(),
9320
+ state.escConfirm ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx(
9321
+ EscConfirmPrompt,
9322
+ {
9323
+ runningTools: state.escConfirm.snapshot.runningTools,
9324
+ subagentCount: state.escConfirm.snapshot.subagentsTerminated,
9325
+ onConfirm: () => {
9326
+ const { snapshot } = state.escConfirm;
9327
+ activeCtrlRef.current?.abort();
9328
+ dispatch({ type: "status", status: "aborting" });
9329
+ dispatch({ type: "steerStart", snapshot });
9330
+ if (director && snapshot.subagentsTerminated > 0) {
9331
+ const cap = new Promise((resolve) => {
9332
+ const t = setTimeout(resolve, 1500);
9333
+ t.unref?.();
9334
+ });
9335
+ void Promise.race([director.terminateAll().catch(() => void 0), cap]);
9336
+ }
9337
+ const droppedCount = state.queue.length;
9338
+ if (droppedCount > 0) dispatch({ type: "queueClear" });
9339
+ const droppedTag = droppedCount > 0 ? ` \xB7 dropped ${droppedCount} queued` : "";
9340
+ const fleetTag = snapshot.subagentsTerminated > 0 ? ` \xB7 stopped ${snapshot.subagentsTerminated} subagent${snapshot.subagentsTerminated === 1 ? "" : "s"}` : "";
9341
+ dispatch({
9342
+ type: "addEntry",
9343
+ entry: {
9344
+ kind: "warn",
9345
+ text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.`
9346
+ }
9347
+ });
9348
+ dispatch({ type: "escConfirmClose" });
9349
+ },
9350
+ onCancel: () => {
9351
+ dispatch({ type: "escConfirmClose" });
9352
+ }
9353
+ }
9354
+ ) }) : null,
9267
9355
  state.enhanceBusy && !state.enhance ? /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u2728 refining your request\u2026" }) }) : null,
9268
9356
  state.enhance ? (() => {
9269
9357
  const info = state.enhance;