@botconnector/bccli 0.4.5 → 0.4.7

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.
Files changed (48) hide show
  1. package/dist/{acp-M22V2CHD.js → acp-XGG33CZJ.js} +2 -2
  2. package/dist/{args-TFKRCKTY.js → args-SQRIBI2Q.js} +4 -4
  3. package/dist/{chunk-27REMLDV.js → chunk-34Z6BXTO.js} +1 -1
  4. package/dist/{chunk-6EGQI6HJ.js → chunk-3MCGYPYI.js} +1 -1
  5. package/dist/{chunk-3JQZ2IKV.js → chunk-3WYR6KVQ.js} +3 -3
  6. package/dist/{chunk-D2C6EOOS.js → chunk-42VCJJTW.js} +7 -7
  7. package/dist/{chunk-WTCWBY2D.js → chunk-54PFT7F5.js} +2 -2
  8. package/dist/{chunk-JVQZWTPV.js → chunk-6GC5I2RK.js} +4 -4
  9. package/dist/{chunk-NSARPM5C.js → chunk-CSIQDFEF.js} +1 -1
  10. package/dist/{chunk-XSUFHD67.js → chunk-EAR2C6TS.js} +1 -1
  11. package/dist/{chunk-LHLNK7HP.js → chunk-EG7GJBSV.js} +4 -4
  12. package/dist/{chunk-XQVEAF3F.js → chunk-HR7T3BY2.js} +2 -2
  13. package/dist/{chunk-BR63GX6T.js → chunk-L5FKXSFK.js} +4 -4
  14. package/dist/{chunk-D7GAZKXT.js → chunk-SE6SM2EI.js} +3 -3
  15. package/dist/{chunk-AK6TWKET.js → chunk-T5Z3FCT2.js} +2 -2
  16. package/dist/{chunk-FW6W5MN7.js → chunk-TCRAN6ZO.js} +11 -11
  17. package/dist/{chunk-DZPBWYTD.js → chunk-V4LPDP3Z.js} +1 -1
  18. package/dist/{chunk-O5ATYRUP.js → chunk-WSSJH765.js} +1 -1
  19. package/dist/{chunk-EOOWV6C6.js → chunk-XCVYR5BK.js} +1 -0
  20. package/dist/{chunk-YNI4GHBI.js → chunk-Y57FCAGM.js} +1 -1
  21. package/dist/cli.js +38 -38
  22. package/dist/{completion-JCBMQKNQ.js → completion-FETH4OGC.js} +1 -1
  23. package/dist/{config-2MR6V7U7.js → config-BUSQGUWY.js} +2 -2
  24. package/dist/{config-JCI7NAVY.js → config-PVEZPRMH.js} +3 -3
  25. package/dist/{ghBotCli-T7AWXAZ2.js → ghBotCli-X6HEOQMZ.js} +14 -14
  26. package/dist/{integrationCli-KOCF73GG.js → integrationCli-552UYCXJ.js} +2 -2
  27. package/dist/{login-OIYX4D3X.js → login-SWK74XO5.js} +4 -4
  28. package/dist/{mcpCli-IBC3WCHG.js → mcpCli-DH4R5B7F.js} +6 -6
  29. package/dist/{modelsCli-XZLHNWKL.js → modelsCli-4YEUPYBF.js} +6 -6
  30. package/dist/{oauthLogin-TUXZLIQN.js → oauthLogin-NR22NWEV.js} +1 -1
  31. package/dist/{pluginsCli-X7NZRU7X.js → pluginsCli-CIW35MNB.js} +2 -2
  32. package/dist/{prCli-GPDLWYWC.js → prCli-6FCI36AJ.js} +1 -1
  33. package/dist/providerCli-N64TSYMT.js +15 -0
  34. package/dist/sdk.js +4 -3
  35. package/dist/{serve-TJCT3TLE.js → serve-DAC2JGEH.js} +15 -15
  36. package/dist/{serve-A764QS5U.js → serve-N2TUTZGD.js} +3 -3
  37. package/dist/session-QZAXEQRE.js +12 -0
  38. package/dist/setup-2KMWTHDZ.js +24 -0
  39. package/dist/{statsCli-FKGON32O.js → statsCli-FR7GOKWO.js} +1 -1
  40. package/dist/{transferCli-HA5IKJKF.js → transferCli-ZY7WIIPY.js} +4 -4
  41. package/dist/ui/complete.d.ts +2 -0
  42. package/dist/ui/thinkingMouse.d.ts +4 -0
  43. package/dist/{ui-SMY7QSFF.js → ui-A7DY4X4E.js} +538 -153
  44. package/dist/{update-BUFIPEKY.js → update-36PG64GC.js} +2 -2
  45. package/package.json +1 -1
  46. package/dist/providerCli-WCFA36S4.js +0 -15
  47. package/dist/session-ML2DPI7P.js +0 -12
  48. package/dist/setup-QYCA7ATT.js +0 -24
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  CATALOG,
4
4
  fillTemplate
5
- } from "./chunk-XQVEAF3F.js";
5
+ } from "./chunk-HR7T3BY2.js";
6
6
  import {
7
7
  MAX_VERIFY_ROUNDS,
8
8
  runVerify,
9
9
  toolLabel,
10
10
  verifyFollowup,
11
11
  worktreeListText
12
- } from "./chunk-D7GAZKXT.js";
12
+ } from "./chunk-SE6SM2EI.js";
13
13
  import {
14
14
  BUILTIN_AGENTS,
15
15
  appendMemory,
@@ -24,7 +24,7 @@ import {
24
24
  runCommand,
25
25
  runHooks,
26
26
  validateGlobPattern
27
- } from "./chunk-NSARPM5C.js";
27
+ } from "./chunk-CSIQDFEF.js";
28
28
  import "./chunk-PSO7R5AD.js";
29
29
  import {
30
30
  nextMode,
@@ -34,39 +34,39 @@ import {
34
34
  hasKey,
35
35
  providerName,
36
36
  writeGlobalConfig
37
- } from "./chunk-27REMLDV.js";
37
+ } from "./chunk-34Z6BXTO.js";
38
38
  import {
39
39
  Session
40
- } from "./chunk-YNI4GHBI.js";
40
+ } from "./chunk-Y57FCAGM.js";
41
41
  import {
42
42
  contentText
43
- } from "./chunk-BR63GX6T.js";
43
+ } from "./chunk-L5FKXSFK.js";
44
44
  import {
45
45
  REASONING_LEVELS,
46
46
  assertReasoningSupported,
47
47
  parseReasoningLevel,
48
48
  supportedReasoningLevels
49
- } from "./chunk-XSUFHD67.js";
49
+ } from "./chunk-EAR2C6TS.js";
50
50
  import {
51
51
  addGlobalServer,
52
52
  globalMcpPath,
53
53
  readMcpFile,
54
54
  removeGlobalServer
55
- } from "./chunk-WTCWBY2D.js";
55
+ } from "./chunk-54PFT7F5.js";
56
56
  import {
57
57
  ACCENT,
58
58
  PRESETS,
59
59
  color,
60
60
  matchKeybind,
61
61
  removeCredential
62
- } from "./chunk-AK6TWKET.js";
62
+ } from "./chunk-T5Z3FCT2.js";
63
63
  import {
64
64
  getLanguage,
65
65
  locale,
66
66
  parseLang,
67
67
  setLanguage,
68
68
  t
69
- } from "./chunk-EOOWV6C6.js";
69
+ } from "./chunk-XCVYR5BK.js";
70
70
 
71
71
  // src/ui/index.tsx
72
72
  import { render } from "ink";
@@ -77,11 +77,12 @@ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, wr
77
77
  import { homedir, tmpdir } from "os";
78
78
  import { dirname, join as join3, relative as relative2, resolve as resolve2 } from "path";
79
79
  import { Box as Box13, Static, Text as Text16, measureElement, useApp, useInput as useInput9, useStdin, useStdout as useStdout2 } from "ink";
80
- import { useCallback, useEffect as useEffect3, useRef, useState as useState8 } from "react";
80
+ import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState10 } from "react";
81
81
 
82
82
  // src/commands.ts
83
83
  var SLASH_COMMANDS = [
84
84
  { name: "help", description: "list of commands and shortcuts" },
85
+ { name: "thinking", description: "show/hide the model\u2019s thinking" },
85
86
  { name: "model", description: "change model" },
86
87
  { name: "reasoning", description: "set reasoning: auto/off/low/medium/high/max" },
87
88
  { name: "provider", description: "add/choose an AI provider" },
@@ -775,14 +776,38 @@ function masked(s) {
775
776
 
776
777
  // src/ui/EditableText.tsx
777
778
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
779
+ var MAX_VISIBLE_LINES = 10;
780
+ function windowedState(s) {
781
+ const lines = s.value.split("\n");
782
+ if (lines.length <= MAX_VISIBLE_LINES) return { view: s, hiddenAbove: 0, hiddenBelow: 0 };
783
+ const cursorLine = s.value.slice(0, s.cursor).split("\n").length - 1;
784
+ const startLine = Math.max(0, Math.min(cursorLine - Math.floor(MAX_VISIBLE_LINES / 2), lines.length - MAX_VISIBLE_LINES));
785
+ const endLine = Math.min(lines.length, startLine + MAX_VISIBLE_LINES);
786
+ let charStart = 0;
787
+ for (let i = 0; i < startLine; i++) charStart += lines[i].length + 1;
788
+ const sliced = lines.slice(startLine, endLine).join("\n");
789
+ const relCursor = Math.max(0, Math.min(sliced.length, s.cursor - charStart));
790
+ return {
791
+ view: { value: sliced, cursor: relCursor },
792
+ hiddenAbove: startLine,
793
+ hiddenBelow: lines.length - endLine
794
+ };
795
+ }
778
796
  function EditableText({ state, mask = false, showCursor = true }) {
779
- const view = mask ? masked(state) : state;
780
- if (!showCursor) return /* @__PURE__ */ jsx2(Fragment, { children: view.value });
797
+ const raw = mask ? masked(state) : state;
798
+ const { view, hiddenAbove, hiddenBelow } = windowedState(raw);
799
+ const prefix = hiddenAbove > 0 ? `\u2026 (+${hiddenAbove} lines above)
800
+ ` : "";
801
+ const suffix = hiddenBelow > 0 ? `
802
+ \u2026 (+${hiddenBelow} lines below)` : "";
803
+ if (!showCursor) return /* @__PURE__ */ jsx2(Fragment, { children: `${prefix}${view.value}${suffix}` });
781
804
  const { before, at, after } = splitAtCursor(view);
782
805
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
806
+ prefix ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: prefix }) : null,
783
807
  before,
784
808
  /* @__PURE__ */ jsx2(Text2, { inverse: true, children: at }),
785
- after
809
+ after,
810
+ suffix ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: suffix }) : null
786
811
  ] });
787
812
  }
788
813
 
@@ -817,10 +842,23 @@ function clickedThinking(click, cursorRow, rootHeight, target) {
817
842
  const origin = cursorRow - rootHeight + 1;
818
843
  const row = origin + target.y;
819
844
  const col = target.x + 1;
820
- return (click.y === row || click.y === row - 1) && click.x >= col && click.x < col + Math.min(target.width, 46);
845
+ return (click.y === row || click.y === row - 1) && click.x >= col && click.x < col + Math.min(target.width, 72);
846
+ }
847
+ function parseMouseWheel(input) {
848
+ const match = /^\[<?(\d+);\d+;\d+[mM]$/.exec(input);
849
+ if (!match) return void 0;
850
+ const button = Number(match[1]);
851
+ if ((button & 64) === 0) return void 0;
852
+ const dir = button & 3;
853
+ if (dir === 0) return "up";
854
+ if (dir === 1) return "down";
855
+ return void 0;
856
+ }
857
+ function hasMouseWheelSequence(chunk) {
858
+ return chunk.includes("\x1B[") && /\[<?6[4567];\d+;\d+[mM]/.test(chunk);
821
859
  }
822
860
  function isMouseControlInput(input) {
823
- return /^\[<(?:\d+;){2}\d+[mM]$/.test(input) || /^\[(?:\d+;)\d+R$/.test(input);
861
+ return /^\[<[\d;]*[mM]?$/.test(input) || /^\[(?:\d+;){2}\d+[mM]$/.test(input) || /^\[(?:\d+;)+\d*R?$/.test(input) || /^\[M[\s\S]{0,3}$/.test(input) || input.includes("\x1B[<");
824
862
  }
825
863
  function parseThinkingReports(value) {
826
864
  const events = [];
@@ -875,9 +913,20 @@ function ListPicker({ title, items, onPick }) {
875
913
  const shown = items.filter((i) => i.label.toLowerCase().includes(filter.toLowerCase()));
876
914
  const cursor = Math.min(index, Math.max(0, shown.length - 1));
877
915
  useInput2((input, key) => {
916
+ const wheel = parseMouseWheel(input);
917
+ if (wheel === "up") {
918
+ setIndex(Math.max(0, cursor - 1));
919
+ return;
920
+ }
921
+ if (wheel === "down") {
922
+ setIndex(Math.min(Math.max(0, shown.length - 1), cursor + 1));
923
+ return;
924
+ }
878
925
  if (isMouseControlInput(input)) return;
879
926
  if (key.upArrow) setIndex(Math.max(0, cursor - 1));
880
- else if (key.downArrow) setIndex(Math.min(shown.length - 1, cursor + 1));
927
+ else if (key.downArrow) setIndex(Math.min(Math.max(0, shown.length - 1), cursor + 1));
928
+ else if (key.pageUp) setIndex(Math.max(0, cursor - VISIBLE));
929
+ else if (key.pageDown) setIndex(Math.min(Math.max(0, shown.length - 1), cursor + VISIBLE));
881
930
  else if (key.return && !shown[cursor]?.disabled) onPick(shown[cursor]?.id);
882
931
  else if (key.escape) onPick(void 0);
883
932
  else if (key.backspace || key.delete) {
@@ -917,11 +966,23 @@ function ModelPicker({ groups, current, onPick }) {
917
966
  for (const m of models) rows.push({ kind: "model", ref: `${g.providerId}/${m}`, label: m });
918
967
  }
919
968
  const selectable = rows.flatMap((r, i) => r.kind === "model" ? [i] : []);
920
- const cursor = selectable[Math.min(index, selectable.length - 1)] ?? -1;
969
+ const clampedIndex = Math.min(index, Math.max(0, selectable.length - 1));
970
+ const cursor = selectable[clampedIndex] ?? -1;
921
971
  useInput3((input, key) => {
972
+ const wheel = parseMouseWheel(input);
973
+ if (wheel === "up") {
974
+ setIndex(Math.max(0, clampedIndex - 1));
975
+ return;
976
+ }
977
+ if (wheel === "down") {
978
+ setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + 1));
979
+ return;
980
+ }
922
981
  if (isMouseControlInput(input)) return;
923
- if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
924
- else if (key.downArrow) setIndex((i) => Math.min(selectable.length - 1, i + 1));
982
+ if (key.upArrow) setIndex(Math.max(0, clampedIndex - 1));
983
+ else if (key.downArrow) setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + 1));
984
+ else if (key.pageUp) setIndex(Math.max(0, clampedIndex - VISIBLE2));
985
+ else if (key.pageDown) setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + VISIBLE2));
925
986
  else if (key.return) {
926
987
  const row = rows[cursor];
927
988
  if (row?.kind === "model") onPick(row.ref);
@@ -952,9 +1013,21 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
952
1013
  function ProviderMenu({ entries, onPick }) {
953
1014
  const rows = [...entries.map((e) => e.id), "custom"];
954
1015
  const [index, setIndex] = useState4(0);
955
- useInput4((_input, key) => {
1016
+ useInput4((input, key) => {
1017
+ const wheel = parseMouseWheel(input);
1018
+ if (wheel === "up") {
1019
+ setIndex((i) => Math.max(0, i - 1));
1020
+ return;
1021
+ }
1022
+ if (wheel === "down") {
1023
+ setIndex((i) => Math.min(rows.length - 1, i + 1));
1024
+ return;
1025
+ }
1026
+ if (isMouseControlInput(input) || isFocusReport(input)) return;
956
1027
  if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
957
1028
  else if (key.downArrow) setIndex((i) => Math.min(rows.length - 1, i + 1));
1029
+ else if (key.pageUp) setIndex((i) => Math.max(0, i - 10));
1030
+ else if (key.pageDown) setIndex((i) => Math.min(rows.length - 1, i + 10));
958
1031
  else if (key.return) onPick(rows[index]);
959
1032
  else if (key.escape) onPick(void 0);
960
1033
  });
@@ -970,6 +1043,7 @@ function ProviderMenu({ entries, onPick }) {
970
1043
 
971
1044
  // src/ui/PermissionPrompt.tsx
972
1045
  import { Box as Box7, Text as Text8, useInput as useInput5 } from "ink";
1046
+ import { useState as useState5 } from "react";
973
1047
 
974
1048
  // src/ui/DiffView.tsx
975
1049
  import { Box as Box6, Text as Text7 } from "ink";
@@ -985,11 +1059,41 @@ function DiffView({ diff }) {
985
1059
  // src/ui/PermissionPrompt.tsx
986
1060
  import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
987
1061
  var VERB = { edit: "edit", write: "write", bash: "run", fetch: "fetch" };
1062
+ var MAX_DIFF_LINES = 16;
988
1063
  function PermissionPrompt({
989
1064
  request,
990
1065
  onAnswer
991
1066
  }) {
1067
+ const diffLines = request.preview ? request.preview.split("\n") : [];
1068
+ const maxOffset = Math.max(0, diffLines.length - MAX_DIFF_LINES);
1069
+ const [offset, setOffset] = useState5(0);
992
1070
  useInput5((input, key) => {
1071
+ const wheel = parseMouseWheel(input);
1072
+ if (wheel === "up") {
1073
+ setOffset((o) => Math.max(0, o - 2));
1074
+ return;
1075
+ }
1076
+ if (wheel === "down") {
1077
+ setOffset((o) => Math.min(maxOffset, o + 2));
1078
+ return;
1079
+ }
1080
+ if (isMouseControlInput(input) || isFocusReport(input)) return;
1081
+ if (key.upArrow) {
1082
+ setOffset((o) => Math.max(0, o - 1));
1083
+ return;
1084
+ }
1085
+ if (key.downArrow) {
1086
+ setOffset((o) => Math.min(maxOffset, o + 1));
1087
+ return;
1088
+ }
1089
+ if (key.pageUp) {
1090
+ setOffset((o) => Math.max(0, o - MAX_DIFF_LINES));
1091
+ return;
1092
+ }
1093
+ if (key.pageDown) {
1094
+ setOffset((o) => Math.min(maxOffset, o + MAX_DIFF_LINES));
1095
+ return;
1096
+ }
993
1097
  const k2 = input.toLowerCase();
994
1098
  if (k2 === "y") onAnswer("yes");
995
1099
  else if (k2 === "a" && request.sessionRules?.length) onAnswer("session");
@@ -998,9 +1102,11 @@ function PermissionPrompt({
998
1102
  });
999
1103
  const rules = request.sessionRules ?? [];
1000
1104
  const sessionLabel = !rules.length ? null : request.kind === "edit" ? t("yes for all edits in this project, this session") : t("yes this session for {rules}", { rules: rules.join(", ") });
1105
+ const visibleDiff = diffLines.length > MAX_DIFF_LINES ? diffLines.slice(offset, offset + MAX_DIFF_LINES).join("\n") : request.preview;
1001
1106
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: color("yellow"), paddingX: 1, children: [
1002
1107
  /* @__PURE__ */ jsx8(Text8, { bold: true, children: `${request.agent ? `[${request.agent}] ` : ""}${t("Allow {verb} {target}?", { verb: t(VERB[request.tool] ?? request.tool), target: request.target })}` }),
1003
- request.preview ? /* @__PURE__ */ jsx8(DiffView, { diff: request.preview }) : null,
1108
+ visibleDiff ? /* @__PURE__ */ jsx8(DiffView, { diff: visibleDiff }) : null,
1109
+ diffLines.length > MAX_DIFF_LINES ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: `\u2191\u2193 scroll (${offset + 1}\u2013${Math.min(diffLines.length, offset + MAX_DIFF_LINES)} / ${diffLines.length})` }) : null,
1004
1110
  /* @__PURE__ */ jsxs7(Text8, { children: [
1005
1111
  /* @__PURE__ */ jsx8(Text8, { color: color("green"), children: t("[y] yes") }),
1006
1112
  " ",
@@ -1013,19 +1119,19 @@ function PermissionPrompt({
1013
1119
 
1014
1120
  // src/ui/PromptInput.tsx
1015
1121
  import { Box as Box8, Text as Text9, useInput as useInput6 } from "ink";
1016
- import { useEffect, useState as useState5 } from "react";
1122
+ import { useEffect, useRef, useState as useState6 } from "react";
1017
1123
 
1018
1124
  // src/ui/complete.ts
1019
1125
  import { existsSync, globSync, readFileSync, statSync } from "fs";
1020
1126
  import { isAbsolute, join, relative, resolve } from "path";
1021
1127
  var MAX_ATTACH_BYTES = 32768;
1022
- function completeFile(value, cwd) {
1128
+ function listFileSuggestions(value, cwd, limit = 8) {
1023
1129
  const match = /(^|\s)@([^\s]*)$/.exec(value);
1024
- if (!match) return value;
1130
+ if (!match) return [];
1025
1131
  const prefix = match[2];
1026
1132
  const escaped = prefix.replace(/([*?[\]{}()!])/g, "\\$1");
1027
1133
  const pattern = `${escaped}*`;
1028
- if (validateGlobPattern(pattern)) return value;
1134
+ if (validateGlobPattern(pattern)) return [];
1029
1135
  const exclude = ignorePatterns(cwd);
1030
1136
  const formatEntry = (entry2) => {
1031
1137
  const normalized = normalizeGlobPath(entry2);
@@ -1035,17 +1141,30 @@ function completeFile(value, cwd) {
1035
1141
  return normalized;
1036
1142
  }
1037
1143
  };
1038
- let found = globSync(pattern, { cwd, exclude }).map(formatEntry).sort();
1039
- if (!found.length && prefix && !prefix.includes("/")) {
1040
- const deepPattern = `**/*${escaped}*`;
1041
- if (!validateGlobPattern(deepPattern)) {
1042
- found = globSync(deepPattern, { cwd, exclude }).map(formatEntry).sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
1144
+ try {
1145
+ let found = globSync(pattern, { cwd, exclude }).map(formatEntry).sort();
1146
+ if (!found.length && prefix && !prefix.includes("/")) {
1147
+ const deepPattern = `**/*${escaped}*`;
1148
+ if (!validateGlobPattern(deepPattern)) {
1149
+ found = globSync(deepPattern, { cwd, exclude }).map(formatEntry).sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
1150
+ }
1043
1151
  }
1152
+ return found.slice(0, limit);
1153
+ } catch {
1154
+ return [];
1044
1155
  }
1045
- if (!found.length) return value;
1046
- const pick = found[0];
1156
+ }
1157
+ function applyFileSuggestion(value, pick) {
1158
+ const match = /(^|\s)@([^\s]*)$/.exec(value);
1159
+ if (!match) return value;
1160
+ const prefix = match[2];
1047
1161
  return `${value.slice(0, value.length - prefix.length)}${pick}${pick.endsWith("/") ? "" : " "}`;
1048
1162
  }
1163
+ function completeFile(value, cwd) {
1164
+ const found = listFileSuggestions(value, cwd, 1);
1165
+ if (!found.length) return value;
1166
+ return applyFileSuggestion(value, found[0]);
1167
+ }
1049
1168
  function expandAtFileMentions(prompt, cwd, readFiles) {
1050
1169
  const matches = [...prompt.matchAll(/(?:^|\s)@([^\s]+)/g)];
1051
1170
  if (!matches.length) return prompt;
@@ -1181,14 +1300,15 @@ ${s.value.slice(start)}`, cursor: start }, mode: "insert", handled: true };
1181
1300
  // src/ui/PromptInput.tsx
1182
1301
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1183
1302
  function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected, vim }) {
1184
- const [line, setLine] = useState5({ value: "", cursor: 0 });
1303
+ const [line, setLine] = useState6({ value: "", cursor: 0 });
1185
1304
  const { value } = line;
1186
- const [historyIndex, setHistoryIndex] = useState5(null);
1187
- const [selected, setSelected] = useState5(0);
1188
- const [dismissed, setDismissed] = useState5(false);
1189
- const [vimMode, setVimMode] = useState5("insert");
1190
- const [vimPending, setVimPending] = useState5(void 0);
1191
- const injectedSeen = useState5(0);
1305
+ const [historyIndex, setHistoryIndex] = useState6(null);
1306
+ const draftRef = useRef("");
1307
+ const [selected, setSelected] = useState6(0);
1308
+ const [dismissed, setDismissed] = useState6(false);
1309
+ const [vimMode, setVimMode] = useState6("insert");
1310
+ const [vimPending, setVimPending] = useState6(void 0);
1311
+ const injectedSeen = useState6(0);
1192
1312
  useEffect(() => {
1193
1313
  if (injected && injected.n !== injectedSeen[0]) {
1194
1314
  injectedSeen[1](injected.n);
@@ -1198,7 +1318,10 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
1198
1318
  }, [injected, injectedSeen]);
1199
1319
  const all = [...SLASH_COMMANDS, ...(extraCommands ?? []).filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name))];
1200
1320
  const suggestions = historyIndex === null && !dismissed && value.startsWith("/") && !/\s/.test(value) ? all.filter((c) => c.name.startsWith(value.slice(1))).slice(0, 8) : [];
1201
- const highlighted = Math.min(selected, Math.max(0, suggestions.length - 1));
1321
+ const beforeCursor = value.slice(0, line.cursor);
1322
+ const fileSuggestions = historyIndex === null && !dismissed && !suggestions.length && /(^|\s)@([^\s]*)$/.test(beforeCursor) ? listFileSuggestions(beforeCursor, cwd, 8) : [];
1323
+ const activeCount = suggestions.length || fileSuggestions.length;
1324
+ const highlighted = Math.min(selected, Math.max(0, activeCount - 1));
1202
1325
  const typed = (text) => ({ value: text, cursor: text.length });
1203
1326
  const edit = (next) => {
1204
1327
  setLine(next);
@@ -1208,6 +1331,13 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
1208
1331
  };
1209
1332
  useInput6(
1210
1333
  (input, key) => {
1334
+ const wheel = parseMouseWheel(input);
1335
+ if (wheel) {
1336
+ if (activeCount > 0) {
1337
+ setSelected((current) => wheel === "up" ? Math.max(0, Math.min(current, activeCount - 1) - 1) : Math.min(activeCount - 1, Math.min(current, activeCount - 1) + 1));
1338
+ }
1339
+ return;
1340
+ }
1211
1341
  if (isMouseControlInput(input)) return;
1212
1342
  if (suggestions.length) {
1213
1343
  if (key.escape) {
@@ -1226,13 +1356,33 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
1226
1356
  edit(typed(`/${suggestions[highlighted].name} `));
1227
1357
  return;
1228
1358
  }
1229
- if (key.return && !key.meta) {
1359
+ if (key.return && !key.meta && !key.shift) {
1230
1360
  edit(typed(""));
1231
1361
  setHistoryIndex(null);
1362
+ draftRef.current = "";
1232
1363
  onSubmit(`/${suggestions[highlighted].name}`);
1233
1364
  return;
1234
1365
  }
1235
1366
  }
1367
+ if (fileSuggestions.length) {
1368
+ if (key.escape) {
1369
+ setDismissed(true);
1370
+ return;
1371
+ }
1372
+ if (key.upArrow) {
1373
+ setSelected(Math.max(0, highlighted - 1));
1374
+ return;
1375
+ }
1376
+ if (key.downArrow) {
1377
+ setSelected(Math.min(fileSuggestions.length - 1, highlighted + 1));
1378
+ return;
1379
+ }
1380
+ if (key.tab && !key.shift) {
1381
+ const completed = applyFileSuggestion(beforeCursor, fileSuggestions[highlighted]);
1382
+ edit({ value: completed + value.slice(line.cursor), cursor: completed.length });
1383
+ return;
1384
+ }
1385
+ }
1236
1386
  if (vim) {
1237
1387
  const r = applyVim(line, vimMode, input, key, vimPending);
1238
1388
  if (r.handled) {
@@ -1243,7 +1393,7 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
1243
1393
  return;
1244
1394
  }
1245
1395
  }
1246
- if (key.return && key.meta || key.ctrl && input.toLowerCase() === "j") {
1396
+ if (key.return && (key.meta || key.shift) || key.ctrl && input.toLowerCase() === "j" || input === "\n") {
1247
1397
  edit((current) => insert(current, "\n"));
1248
1398
  return;
1249
1399
  }
@@ -1258,6 +1408,7 @@ ${value.slice(line.cursor)}`, cursor: before.length });
1258
1408
  if (!text2) return;
1259
1409
  edit(typed(""));
1260
1410
  setHistoryIndex(null);
1411
+ draftRef.current = "";
1261
1412
  setVimMode("insert");
1262
1413
  setVimPending(void 0);
1263
1414
  onSubmit(text2);
@@ -1277,6 +1428,7 @@ ${value.slice(line.cursor)}`, cursor: before.length });
1277
1428
  }
1278
1429
  if (key.upArrow) {
1279
1430
  if (!history.length) return;
1431
+ if (historyIndex === null) draftRef.current = value;
1280
1432
  const i = historyIndex === null ? history.length - 1 : Math.max(0, historyIndex - 1);
1281
1433
  setHistoryIndex(i);
1282
1434
  setLine(typed(history[i]));
@@ -1286,7 +1438,7 @@ ${value.slice(line.cursor)}`, cursor: before.length });
1286
1438
  if (historyIndex === null) return;
1287
1439
  const i = historyIndex + 1;
1288
1440
  setHistoryIndex(i >= history.length ? null : i);
1289
- setLine(typed(i >= history.length ? "" : history[i]));
1441
+ setLine(typed(i >= history.length ? draftRef.current : history[i]));
1290
1442
  return;
1291
1443
  }
1292
1444
  if (key.tab && !key.shift) {
@@ -1310,17 +1462,18 @@ ${value.slice(line.cursor)}`, cursor: before.length });
1310
1462
  t("NORMAL")
1311
1463
  ] })
1312
1464
  ] }) }),
1313
- suggestions.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === highlighted ? color("green") : void 0, dimColor: i !== highlighted, children: `${i === highlighted ? "\u203A" : " "} /${s.name} ${t(s.description)}` }, s.name))
1465
+ suggestions.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === highlighted ? color("green") : void 0, dimColor: i !== highlighted, children: `${i === highlighted ? "\u203A" : " "} /${s.name} ${t(s.description)}` }, s.name)),
1466
+ fileSuggestions.map((f, i) => /* @__PURE__ */ jsx9(Text9, { color: i === highlighted ? color("green") : void 0, dimColor: i !== highlighted, children: `${i === highlighted ? "\u203A" : " "} @${f}` }, f))
1314
1467
  ] });
1315
1468
  }
1316
1469
 
1317
1470
  // src/ui/Spinner.tsx
1318
1471
  import { Text as Text10 } from "ink";
1319
- import { useEffect as useEffect2, useState as useState6 } from "react";
1472
+ import { useEffect as useEffect2, useState as useState7 } from "react";
1320
1473
  import { jsx as jsx10 } from "react/jsx-runtime";
1321
1474
  var FRAMES = ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "\u273D"];
1322
1475
  function Spinner({ label, startedAt }) {
1323
- const [tick, setTick] = useState6(0);
1476
+ const [tick, setTick] = useState7(0);
1324
1477
  useEffect2(() => {
1325
1478
  const id = setInterval(() => setTick((t2) => t2 + 1), 120);
1326
1479
  return () => clearInterval(id);
@@ -1348,14 +1501,26 @@ function StatusBar({ mode, tokens, busy, model, reasoning = "auto" }) {
1348
1501
 
1349
1502
  // src/ui/McpMenu.tsx
1350
1503
  import { Box as Box9, Text as Text12, useInput as useInput7 } from "ink";
1351
- import { useState as useState7 } from "react";
1504
+ import { useState as useState8 } from "react";
1352
1505
  import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1353
1506
  var MARK = { ready: "\u2713", error: "\u2717", starting: "\u2026", off: "\u25CB" };
1354
1507
  function McpMenu({ items, onPick }) {
1355
- const [index, setIndex] = useState7(0);
1356
- useInput7((_input, key) => {
1508
+ const [index, setIndex] = useState8(0);
1509
+ useInput7((input, key) => {
1510
+ const wheel = parseMouseWheel(input);
1511
+ if (wheel === "up") {
1512
+ setIndex((i) => Math.max(0, i - 1));
1513
+ return;
1514
+ }
1515
+ if (wheel === "down") {
1516
+ setIndex((i) => Math.min(items.length - 1, i + 1));
1517
+ return;
1518
+ }
1519
+ if (isMouseControlInput(input) || isFocusReport(input)) return;
1357
1520
  if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
1358
1521
  else if (key.downArrow) setIndex((i) => Math.min(items.length - 1, i + 1));
1522
+ else if (key.pageUp) setIndex((i) => Math.max(0, i - 10));
1523
+ else if (key.pageDown) setIndex((i) => Math.min(items.length - 1, i + 10));
1359
1524
  else if (key.return) onPick(items[index]?.name);
1360
1525
  else if (key.escape) onPick(void 0);
1361
1526
  });
@@ -1370,17 +1535,50 @@ function McpMenu({ items, onPick }) {
1370
1535
 
1371
1536
  // src/ui/PlanApproval.tsx
1372
1537
  import { Box as Box10, Text as Text13, useInput as useInput8 } from "ink";
1538
+ import { useState as useState9 } from "react";
1373
1539
  import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1540
+ var MAX_PLAN_LINES = 16;
1374
1541
  function PlanApproval({ plan, onAnswer }) {
1542
+ const planLines = plan.split("\n");
1543
+ const maxOffset = Math.max(0, planLines.length - MAX_PLAN_LINES);
1544
+ const [offset, setOffset] = useState9(0);
1375
1545
  useInput8((input, key) => {
1546
+ const wheel = parseMouseWheel(input);
1547
+ if (wheel === "up") {
1548
+ setOffset((o) => Math.max(0, o - 2));
1549
+ return;
1550
+ }
1551
+ if (wheel === "down") {
1552
+ setOffset((o) => Math.min(maxOffset, o + 2));
1553
+ return;
1554
+ }
1555
+ if (isMouseControlInput(input) || isFocusReport(input)) return;
1556
+ if (key.upArrow) {
1557
+ setOffset((o) => Math.max(0, o - 1));
1558
+ return;
1559
+ }
1560
+ if (key.downArrow) {
1561
+ setOffset((o) => Math.min(maxOffset, o + 1));
1562
+ return;
1563
+ }
1564
+ if (key.pageUp) {
1565
+ setOffset((o) => Math.max(0, o - MAX_PLAN_LINES));
1566
+ return;
1567
+ }
1568
+ if (key.pageDown) {
1569
+ setOffset((o) => Math.min(maxOffset, o + MAX_PLAN_LINES));
1570
+ return;
1571
+ }
1376
1572
  const k2 = input.toLowerCase();
1377
1573
  if (k2 === "a") onAnswer("acceptEdits");
1378
1574
  else if (k2 === "y") onAnswer("default");
1379
1575
  else if (k2 === "n" || key.escape) onAnswer("no");
1380
1576
  });
1577
+ const visiblePlan = planLines.length > MAX_PLAN_LINES ? planLines.slice(offset, offset + MAX_PLAN_LINES).join("\n") : plan;
1381
1578
  return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
1382
1579
  /* @__PURE__ */ jsx13(Text13, { bold: true, children: t("Plan from the agent") }),
1383
- /* @__PURE__ */ jsx13(Markdown, { text: plan, indent: 5 }),
1580
+ /* @__PURE__ */ jsx13(Markdown, { text: visiblePlan, indent: 5 }),
1581
+ planLines.length > MAX_PLAN_LINES ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: `\u2191\u2193 scroll (${offset + 1}\u2013${Math.min(planLines.length, offset + MAX_PLAN_LINES)} / ${planLines.length})` }) : null,
1384
1582
  /* @__PURE__ */ jsxs10(Text13, { children: [
1385
1583
  /* @__PURE__ */ jsx13(Text13, { color: color("green"), children: t("[a] yes, auto-edit") }),
1386
1584
  " ",
@@ -1402,8 +1600,9 @@ function TodoList({ items }) {
1402
1600
 
1403
1601
  // src/ui/ToolBlock.tsx
1404
1602
  import { Box as Box12, Text as Text15 } from "ink";
1405
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
1603
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
1406
1604
  var PREVIEW_LINES = 5;
1605
+ var DIFF_PREVIEW_LINES = 20;
1407
1606
  function ToolBlock({ tool, target, output, display, isError, done, expanded, sub, moreBind = "ctrl+o" }) {
1408
1607
  const isTask = tool === "task";
1409
1608
  const isDiff = !!display && /^\s*\d+ [+\- ] /.test(display);
@@ -1421,7 +1620,14 @@ function ToolBlock({ tool, target, output, display, isError, done, expanded, sub
1421
1620
  sub.length > 6 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: t("\u2026 {n} earlier steps", { n: sub.length - 6 }) }) : null
1422
1621
  ] }) : null,
1423
1622
  isTask && done ? /* @__PURE__ */ jsx15(Box12, { marginLeft: 3, children: /* @__PURE__ */ jsx15(Text15, { color: isError ? color("red") : color("green"), children: `${isError ? t("\u2717 failed") : t("\u2713 done")}${display ? ` \xB7 ${display}` : ""}` }) }) : null,
1424
- isDiff ? /* @__PURE__ */ jsx15(Box12, { marginLeft: 3, children: /* @__PURE__ */ jsx15(DiffView, { diff: display }) }) : output && (isError || tool === "bash" || expanded) && !(isTask && !isError) ? /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginLeft: 3, children: [
1623
+ isDiff ? /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", marginLeft: 3, children: (() => {
1624
+ const diffLines = display.split("\n");
1625
+ const shownDiff = expanded ? diffLines : diffLines.slice(0, DIFF_PREVIEW_LINES);
1626
+ return /* @__PURE__ */ jsxs11(Fragment2, { children: [
1627
+ /* @__PURE__ */ jsx15(DiffView, { diff: shownDiff.join("\n") }),
1628
+ diffLines.length > shownDiff.length ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: t("\u2026 {n} more lines ({bind})", { n: diffLines.length - shownDiff.length, bind: moreBind }) }) : expanded && diffLines.length > DIFF_PREVIEW_LINES ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: t("\u2026 ({bind} to collapse)", { bind: moreBind }) }) : null
1629
+ ] });
1630
+ })() }) : output && (isError || tool === "bash" || expanded) && !(isTask && !isError) ? /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginLeft: 3, children: [
1425
1631
  shown.map((line, i) => /* @__PURE__ */ jsx15(Text15, { dimColor: !isError, color: isError ? color("red") : void 0, children: line }, i)),
1426
1632
  lines.length > shown.length ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: t("\u2026 {n} more lines ({bind})", { n: lines.length - shown.length, bind: moreBind }) }) : expanded && lines.length > PREVIEW_LINES ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: t("\u2026 ({bind} to collapse)", { bind: moreBind }) }) : null
1427
1633
  ] }) : null
@@ -1470,7 +1676,10 @@ function applyEvent(t2, event) {
1470
1676
  case "text": {
1471
1677
  const last = t2.live.at(-1);
1472
1678
  if (last?.kind === "assistant") return flushLines({ ...t2, live: [...t2.live.slice(0, -1), { ...last, text: last.text + event.delta }] });
1473
- return flushLines({ ...t2, live: [...t2.live, entry({ kind: "assistant", text: event.delta })] });
1679
+ return flushLines({
1680
+ done: [...t2.done, ...t2.live.filter((e) => !isEmptyCont(e))],
1681
+ live: [entry({ kind: "assistant", text: event.delta })]
1682
+ });
1474
1683
  }
1475
1684
  case "thinking": {
1476
1685
  const last = t2.live.at(-1);
@@ -1618,54 +1827,83 @@ var INIT_PROMPT = "Study this project (folder structure, package/build files, te
1618
1827
  var labelFor = (runtime, ref) => `${ref.slice(ref.indexOf("/") + 1)} \xB7 ${runtime.providerLabel()}`;
1619
1828
  function App({ runtime, initialPrompt, version, updateMessage }) {
1620
1829
  const { exit } = useApp();
1621
- const [transcript, setTranscript] = useState8(() => ({
1830
+ const [transcript, setTranscript] = useState10(() => ({
1622
1831
  done: [entry({ kind: "header" }), ...updateMessage ? [entry({ kind: "notice", text: updateMessage, tone: "warn" })] : [], ...runtime.agent.messages.length ? replayEntries(runtime.session.loadHistory()) : []],
1623
1832
  live: []
1624
1833
  }));
1625
- const [busy, setBusy] = useState8(false);
1626
- const [cancelling, setCancelling] = useState8(false);
1627
- const [startedAt, setStartedAt] = useState8(0);
1628
- const [mode, setMode] = useState8(runtime.agent.permissions.mode);
1629
- const [reasoning, setReasoning] = useState8(runtime.reasoning);
1630
- const [tokens, setTokens] = useState8(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
1631
- const [asks, setAsks] = useState8([]);
1834
+ const [busy, setBusy] = useState10(false);
1835
+ const [cancelling, setCancelling] = useState10(false);
1836
+ const [startedAt, setStartedAt] = useState10(0);
1837
+ const [mode, setMode] = useState10(runtime.agent.permissions.mode);
1838
+ const [reasoning, setReasoning] = useState10(runtime.reasoning);
1839
+ const [tokens, setTokens] = useState10(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
1840
+ const [asks, setAsks] = useState10([]);
1632
1841
  const pending = asks[0] ?? null;
1633
- const askId = useRef(0);
1634
- const [picker, setPicker] = useState8(null);
1635
- const [providerMenu, setProviderMenu] = useState8(null);
1636
- const [prompt, setPrompt] = useState8(null);
1842
+ const askId = useRef2(0);
1843
+ const [picker, setPicker] = useState10(null);
1844
+ const [providerMenu, setProviderMenu] = useState10(null);
1845
+ const [prompt, setPrompt] = useState10(null);
1637
1846
  const ask = useCallback((label, mask = false) => new Promise((resolve3) => setPrompt({ label, mask, resolve: resolve3 })), []);
1638
- const [modelLabel, setModelLabel] = useState8(() => labelFor(runtime, runtime.modelRef));
1639
- const [history, setHistory] = useState8([]);
1640
- const controller = useRef(null);
1641
- const [todos, setTodos] = useState8(runtime.todos.items);
1642
- const [mcpMenu, setMcpMenu] = useState8(null);
1643
- const [listPicker, setListPicker] = useState8(null);
1644
- const [planAsk, setPlanAsk] = useState8(null);
1645
- const [queued, setQueued] = useState8([]);
1646
- const [showThinking, setShowThinking] = useState8(runtime.config.thinkingVisible === true);
1647
- const showThinkingRef = useRef(showThinking);
1648
- const [showToolOutput, setShowToolOutput] = useState8(false);
1649
- const [, setConfigRev] = useState8(0);
1847
+ const [modelLabel, setModelLabel] = useState10(() => labelFor(runtime, runtime.modelRef));
1848
+ const [history, setHistory] = useState10([]);
1849
+ const controller = useRef2(null);
1850
+ const [todos, setTodos] = useState10(runtime.todos.items);
1851
+ const [mcpMenu, setMcpMenu] = useState10(null);
1852
+ const [listPicker, setListPicker] = useState10(null);
1853
+ const [planAsk, setPlanAsk] = useState10(null);
1854
+ const [queued, setQueued] = useState10([]);
1855
+ const [showThinking, setShowThinking] = useState10(runtime.config.thinkingVisible === true);
1856
+ const showThinkingRef = useRef2(showThinking);
1857
+ const [showToolOutput, setShowToolOutput] = useState10(false);
1858
+ const [staticKey, setStaticKey] = useState10(0);
1859
+ const [, setConfigRev] = useState10(0);
1650
1860
  const lastUserIdx = transcript.done.findLastIndex((e) => e.kind === "user");
1651
- const staticDone = lastUserIdx > 0 ? transcript.done.slice(0, lastUserIdx) : [];
1652
- const activeTurnDone = lastUserIdx > 0 ? transcript.done.slice(lastUserIdx) : transcript.done;
1653
- const activeTurnEntries = [...activeTurnDone, ...transcript.live];
1654
- const lastThinkingEntryId = activeTurnEntries.findLast((e) => e.kind === "thinking" && !!e.text.trim())?.id;
1655
- const hasThinking = lastThinkingEntryId !== void 0;
1656
1861
  const { stdin } = useStdin();
1657
1862
  const { stdout } = useStdout2();
1658
- const liveRootRef = useRef(null);
1659
- const thinkingTargetRef = useRef(null);
1660
- const [injected, setInjected] = useState8();
1863
+ const isRealTty = Boolean(stdout?.isTTY && "fd" in stdout);
1864
+ const maxLiveLines = Math.max(8, (stdout?.rows ?? 24) - 10);
1865
+ const maxSplitIdxRef = useRef2(0);
1866
+ const refreshStatic = useCallback(() => {
1867
+ if (stdout?.isTTY && "fd" in stdout) {
1868
+ stdout.write("\x1B[2J\x1B[3J\x1B[H");
1869
+ }
1870
+ maxSplitIdxRef.current = 0;
1871
+ setStaticKey((k2) => k2 + 1);
1872
+ }, [stdout]);
1873
+ let splitIdx = Math.max(maxSplitIdxRef.current, lastUserIdx > 0 ? lastUserIdx : 0);
1874
+ if (isRealTty) {
1875
+ const entryLines = (e) => {
1876
+ if (e.kind === "thinking") return showThinking ? Math.min(maxLiveLines, e.text.split("\n").length + 2) : 2;
1877
+ if (e.kind === "tool") return showToolOutput ? Math.min(maxLiveLines, (e.output ?? e.display ?? "").split("\n").length + 2) : 5;
1878
+ if (e.kind === "assistant" || e.kind === "notice" || e.kind === "user") return e.text.split("\n").length + 1;
1879
+ return 2;
1880
+ };
1881
+ const liveLines = transcript.live.reduce((acc, e) => acc + entryLines(e), 0);
1882
+ let doneLines = transcript.done.slice(splitIdx).reduce((acc, e) => acc + entryLines(e), 0);
1883
+ while (splitIdx < transcript.done.length - 1 && doneLines + liveLines > maxLiveLines) {
1884
+ doneLines -= entryLines(transcript.done[splitIdx]);
1885
+ splitIdx++;
1886
+ }
1887
+ maxSplitIdxRef.current = splitIdx;
1888
+ }
1889
+ const staticDone = transcript.done.slice(0, splitIdx);
1890
+ const activeTurnDone = transcript.done.slice(splitIdx);
1891
+ const activeTurnEntries = [...activeTurnDone, ...transcript.live];
1892
+ const lastThinkingEntryId = activeTurnEntries.findLast((e) => e.kind === "thinking" && !!e.text.trim())?.id;
1893
+ const hasThinking = lastThinkingEntryId !== void 0 || transcript.done.some((e) => e.kind === "thinking" && !!e.text.trim());
1894
+ const liveRootRef = useRef2(null);
1895
+ const thinkingTargetRef = useRef2(null);
1896
+ const spinnerTargetRef = useRef2(null);
1897
+ const thinkingEntryRefs = useRef2(/* @__PURE__ */ new Map());
1898
+ const [injected, setInjected] = useState10();
1661
1899
  useEffect3(() => runtime.todos.subscribe(setTodos), [runtime]);
1662
1900
  const extraCommands = [
1663
1901
  ...runtime.commands.filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name.toLowerCase())).map((c) => ({ name: c.name.toLowerCase(), description: c.description ?? t("custom command") })),
1664
1902
  ...runtime.skills.filter((s) => !runtime.commands.some((c) => c.name.toLowerCase() === s.name.toLowerCase())).filter((s) => !SLASH_COMMANDS.some((b) => b.name === s.name.toLowerCase())).map((s) => ({ name: s.name, description: t("skill \xB7 {description}", { description: s.description.slice(0, 50) }) }))
1665
1903
  ];
1666
- const lastTool = useRef(null);
1667
- const toolTargets = useRef(/* @__PURE__ */ new Map());
1668
- const editedInTurn = useRef(false);
1904
+ const lastTool = useRef2(null);
1905
+ const toolTargets = useRef2(/* @__PURE__ */ new Map());
1906
+ const editedInTurn = useRef2(false);
1669
1907
  const notice = useCallback((text, tone = "info") => {
1670
1908
  setTranscript((t2) => ({ ...t2, done: [...t2.done, entry({ kind: "notice", text, tone })] }));
1671
1909
  }, []);
@@ -1739,20 +1977,55 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1739
1977
  },
1740
1978
  [runtime, notice]
1741
1979
  );
1980
+ const toggleThinking = useCallback(() => {
1981
+ const next = !showThinkingRef.current;
1982
+ showThinkingRef.current = next;
1983
+ setShowThinking(next);
1984
+ if (maxSplitIdxRef.current > 0) refreshStatic();
1985
+ try {
1986
+ writeGlobalConfig({ thinkingVisible: next }, runtime.env);
1987
+ } catch (error) {
1988
+ notice(t("Could not save thinking visibility: {error}", { error: error.message }), "warn");
1989
+ }
1990
+ }, [runtime, notice, refreshStatic]);
1742
1991
  const runSlashCommand = useCallback(
1743
1992
  async (name, args) => {
1744
1993
  switch (name) {
1745
1994
  case "help":
1746
1995
  notice(helpText(runtime));
1747
1996
  return;
1997
+ case "thinking":
1998
+ case "t": {
1999
+ const v = args.trim().toLowerCase();
2000
+ if (v === "on" || v === "true") {
2001
+ if (!showThinkingRef.current) toggleThinking();
2002
+ } else if (v === "off" || v === "false") {
2003
+ if (showThinkingRef.current) toggleThinking();
2004
+ } else {
2005
+ toggleThinking();
2006
+ }
2007
+ return;
2008
+ }
2009
+ case "output":
2010
+ case "o":
2011
+ setShowToolOutput((prev) => !prev);
2012
+ if (maxSplitIdxRef.current > 0) refreshStatic();
2013
+ return;
1748
2014
  case "exit":
2015
+ case "quit":
1749
2016
  exit();
1750
2017
  return;
1751
2018
  case "clear":
2019
+ controller.current?.abort();
2020
+ controller.current = null;
2021
+ setQueued([]);
2022
+ setBusy(false);
2023
+ setCancelling(false);
1752
2024
  runtime.agent.clear();
1753
2025
  setTokens(0);
1754
2026
  setShowToolOutput(false);
1755
- setTranscript((t2) => ({ ...t2, done: t2.done.filter((e) => e.kind !== "thinking") }));
2027
+ setTranscript({ done: [entry({ kind: "header" })], live: [] });
2028
+ refreshStatic();
1756
2029
  notice(t("Conversation cleared."));
1757
2030
  return;
1758
2031
  case "cost": {
@@ -1845,10 +2118,16 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1845
2118
  return;
1846
2119
  }
1847
2120
  case "new":
2121
+ controller.current?.abort();
2122
+ controller.current = null;
2123
+ setQueued([]);
2124
+ setBusy(false);
2125
+ setCancelling(false);
1848
2126
  runtime.newSession();
1849
2127
  setTokens(0);
1850
2128
  setShowToolOutput(false);
1851
- setTranscript((t2) => ({ ...t2, done: t2.done.filter((e) => e.kind !== "thinking") }));
2129
+ setTranscript({ done: [entry({ kind: "header" })], live: [] });
2130
+ refreshStatic();
1852
2131
  notice(t("New session started. The previous session stays saved (/resume)."));
1853
2132
  return;
1854
2133
  case "session":
@@ -1880,9 +2159,11 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1880
2159
  const chosen = others.find((s) => s.session.file === id);
1881
2160
  if (!chosen) return;
1882
2161
  runtime.resume(chosen.session);
2162
+ setModelLabel(labelFor(runtime, runtime.modelRef));
1883
2163
  setTokens(0);
1884
2164
  setShowToolOutput(false);
1885
2165
  setTranscript({ done: [entry({ kind: "header" }), ...replayEntries(chosen.session.loadHistory())], live: [] });
2166
+ refreshStatic();
1886
2167
  }
1887
2168
  });
1888
2169
  return;
@@ -2084,6 +2365,15 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2084
2365
  notice(t("Nothing to rewind."));
2085
2366
  return;
2086
2367
  }
2368
+ setTranscript((tr) => {
2369
+ const userIndices = [];
2370
+ tr.done.forEach((e, idx) => {
2371
+ if (e.kind === "user") userIndices.push(idx);
2372
+ });
2373
+ const cutIdx = userIndices[Math.max(0, userIndices.length - result.turns)];
2374
+ return cutIdx !== void 0 ? { done: tr.done.slice(0, cutIdx), live: [] } : tr;
2375
+ });
2376
+ refreshStatic();
2087
2377
  const rel = (p) => relative2(runtime.cwd, p) || p;
2088
2378
  notice(
2089
2379
  [
@@ -2146,6 +2436,7 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2146
2436
  writeGlobalConfig({ thinkingVisible: next }, runtime.env);
2147
2437
  showThinkingRef.current = next;
2148
2438
  setShowThinking(next);
2439
+ refreshStatic();
2149
2440
  } else if (key === "thinkingClickCollapse") writeGlobalConfig({ thinkingClickCollapse: val === "true" || val === "on" }, runtime.env);
2150
2441
  else if (key === "sandbox" && (val === "on" || val === "off")) writeGlobalConfig({ sandbox: val }, runtime.env);
2151
2442
  else if (key === "networkPolicy" && (val === "allow" || val === "offline")) writeGlobalConfig({ networkPolicy: val }, runtime.env);
@@ -2182,7 +2473,7 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2182
2473
  notice(t("Unknown command: /{name}. Type /help.", { name }), "warn");
2183
2474
  }
2184
2475
  },
2185
- [runtime, notice, exit, version, runTurn, ask]
2476
+ [runtime, notice, exit, version, runTurn, ask, refreshStatic, toggleThinking]
2186
2477
  );
2187
2478
  const runSlash = useCallback(
2188
2479
  (name, args) => runSlashCommand(name, args).catch((error) => notice(`/${name}: ${error.message}`, "error")),
@@ -2198,8 +2489,12 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2198
2489
  if (text.startsWith("!") && text.slice(1).trim()) {
2199
2490
  const cmd = text.slice(1).trim();
2200
2491
  setTranscript((tr) => ({ ...tr, done: [...tr.done, entry({ kind: "user", text })] }));
2492
+ setBusy(true);
2493
+ setStartedAt(Date.now());
2494
+ const ctrl = new AbortController();
2495
+ controller.current = ctrl;
2201
2496
  const shell = resolveShell({ platform: process.platform, env: runtime.env, exists: existsSync3 });
2202
- void runCommand(cmd, { cwd: runtime.cwd, timeoutMs: 6e4, signal: new AbortController().signal, shell }).then((r) => {
2497
+ void runCommand(cmd, { cwd: runtime.cwd, timeoutMs: 6e4, signal: ctrl.signal, shell }).then((r) => {
2203
2498
  const out = r.output.trim() || "(no output)";
2204
2499
  notice(`$ ${cmd}
2205
2500
  ${out}`, r.exitCode ? "error" : "info");
@@ -2208,6 +2503,10 @@ $ ${cmd}
2208
2503
  ${out}` };
2209
2504
  runtime.agent.messages.push(ctxMsg);
2210
2505
  runtime.session.append(ctxMsg);
2506
+ }).finally(() => {
2507
+ controller.current = null;
2508
+ setBusy(false);
2509
+ setCancelling(false);
2211
2510
  });
2212
2511
  return;
2213
2512
  }
@@ -2218,7 +2517,7 @@ ${out}` };
2218
2517
  }
2219
2518
  const command = runtime.commands.find((c) => c.name.toLowerCase() === slash.name);
2220
2519
  const skill = runtime.skills.find((s) => s.name.toLowerCase() === slash.name);
2221
- if (SLASH_COMMANDS.some((b) => b.name === slash.name) || !command && !skill) void runSlash(slash.name, slash.args);
2520
+ if (SLASH_COMMANDS.some((b) => b.name === slash.name) || slash.name === "t" || slash.name === "output" || slash.name === "o" || slash.name === "quit" || !command && !skill) void runSlash(slash.name, slash.args);
2222
2521
  else if (command) void runTurn(expandCommand(command, slash.args));
2223
2522
  else if (skill) {
2224
2523
  let body;
@@ -2237,10 +2536,26 @@ ARGUMENTS: ${slash.args}`);
2237
2536
  );
2238
2537
  const onPrompt = useCallback(
2239
2538
  (text) => {
2240
- if (busy) setQueued((q) => [...q, text]);
2241
- else submit(text);
2539
+ if (!busy) {
2540
+ submit(text);
2541
+ return;
2542
+ }
2543
+ if (text.startsWith("#") && text.slice(1).trim()) {
2544
+ submit(text);
2545
+ return;
2546
+ }
2547
+ const slash = parseSlash(text);
2548
+ if (slash) {
2549
+ const turnMutating = /* @__PURE__ */ new Set(["init", "pr", "compact", "rewind", "undo", "redo", "resume"]);
2550
+ const isCustomTurn = !SLASH_COMMANDS.some((b) => b.name === slash.name) && slash.name !== "t" && slash.name !== "output" && slash.name !== "o" && slash.name !== "quit" && (runtime.commands.some((c) => c.name.toLowerCase() === slash.name) || runtime.skills.some((s) => s.name.toLowerCase() === slash.name));
2551
+ if (!turnMutating.has(slash.name) && !isCustomTurn) {
2552
+ submit(text);
2553
+ return;
2554
+ }
2555
+ }
2556
+ setQueued((q) => [...q, text]);
2242
2557
  },
2243
- [busy, submit]
2558
+ [busy, submit, runtime]
2244
2559
  );
2245
2560
  useEffect3(() => {
2246
2561
  if (busy || pending || planAsk || !queued.length) return;
@@ -2248,58 +2563,81 @@ ARGUMENTS: ${slash.args}`);
2248
2563
  setQueued(rest);
2249
2564
  submit(next);
2250
2565
  }, [busy, pending, planAsk, queued, submit]);
2251
- const initialSent = useRef(false);
2566
+ const initialSent = useRef2(false);
2252
2567
  useEffect3(() => {
2253
2568
  if (initialPrompt && !initialSent.current) {
2254
2569
  initialSent.current = true;
2255
2570
  submit(initialPrompt);
2256
2571
  }
2257
2572
  }, [initialPrompt, submit]);
2258
- const toggleThinking = useCallback(() => {
2259
- const next = !showThinkingRef.current;
2260
- showThinkingRef.current = next;
2261
- setShowThinking(next);
2262
- try {
2263
- writeGlobalConfig({ thinkingVisible: next }, runtime.env);
2264
- } catch (error) {
2265
- notice(t("Could not save thinking visibility: {error}", { error: error.message }), "warn");
2266
- }
2267
- }, [runtime, notice]);
2573
+ useEffect3(() => {
2574
+ if (!stdout?.isTTY || !("fd" in stdout)) return;
2575
+ stdout.write("\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?2004h");
2576
+ return () => {
2577
+ stdout.write("\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?2004l");
2578
+ };
2579
+ }, [stdout]);
2580
+ const scrollableModalOpen = !!(picker || listPicker || mcpMenu || providerMenu || pending || planAsk);
2581
+ useEffect3(() => {
2582
+ if (!scrollableModalOpen || !stdout?.isTTY || !("fd" in stdout)) return;
2583
+ stdout.write("\x1B[?1000h\x1B[?1006h");
2584
+ return () => {
2585
+ stdout.write("\x1B[?1000l\x1B[?1006l");
2586
+ };
2587
+ }, [scrollableModalOpen, stdout]);
2268
2588
  useEffect3(() => {
2269
2589
  const modalOpen = !!(pending || planAsk || picker || listPicker || mcpMenu || providerMenu || prompt);
2270
- if (!runtime.config.thinkingClickCollapse || !busy && !hasThinking || modalOpen || !stdin.isTTY || !stdout.isTTY || !thinkingTargetRef.current) return;
2590
+ if (!runtime.config.thinkingClickCollapse || !busy && !hasThinking || modalOpen || !stdin.isTTY || !stdout.isTTY) return;
2271
2591
  let click;
2272
2592
  let mouseBuffer = "";
2593
+ let wheelTimer;
2273
2594
  const onData = (data) => {
2274
- mouseBuffer += String(data);
2595
+ const chunk = String(data);
2596
+ if (hasMouseWheelSequence(chunk)) {
2597
+ stdout.write("\x1B[?1000l\x1B[?1006l");
2598
+ if (wheelTimer) clearTimeout(wheelTimer);
2599
+ wheelTimer = setTimeout(() => {
2600
+ stdout.write("\x1B[?1000h\x1B[?1006h");
2601
+ }, 1200);
2602
+ }
2603
+ mouseBuffer += chunk;
2275
2604
  const reports = parseThinkingReports(mouseBuffer);
2276
2605
  mouseBuffer = reports.rest.slice(-80);
2277
2606
  for (const report of reports.events) {
2278
2607
  if (report.type === "mouse") {
2279
2608
  click = report.press;
2280
2609
  stdout.write("\x1B[6n");
2281
- } else if (click && liveRootRef.current && thinkingTargetRef.current) {
2610
+ } else if (click && liveRootRef.current) {
2282
2611
  const current = click;
2283
2612
  click = void 0;
2284
2613
  const root = measureElement(liveRootRef.current);
2285
- const target = measureElement(thinkingTargetRef.current);
2286
- if (clickedThinking(current, report.cursor.row, root.height, target)) toggleThinking();
2614
+ const candidates = [];
2615
+ if (thinkingTargetRef.current) candidates.push(thinkingTargetRef.current);
2616
+ if (spinnerTargetRef.current) candidates.push(spinnerTargetRef.current);
2617
+ for (const el of thinkingEntryRefs.current.values()) {
2618
+ if (!candidates.includes(el)) candidates.push(el);
2619
+ }
2620
+ if (candidates.some((el) => clickedThinking(current, report.cursor.row, root.height, measureElement(el)))) {
2621
+ toggleThinking();
2622
+ }
2287
2623
  }
2288
2624
  }
2289
2625
  };
2290
2626
  stdin.on("data", onData);
2291
2627
  stdout.write("\x1B[?1000h\x1B[?1006h");
2292
2628
  return () => {
2629
+ if (wheelTimer) clearTimeout(wheelTimer);
2293
2630
  stdin.off("data", onData);
2294
2631
  stdout.write("\x1B[?1000l\x1B[?1006l");
2295
2632
  };
2296
- }, [busy, pending, planAsk, picker, listPicker, mcpMenu, providerMenu, prompt, stdin, stdout, toggleThinking, hasThinking, runtime.config.thinkingClickCollapse]);
2633
+ }, [busy, hasThinking, pending, planAsk, picker, listPicker, mcpMenu, providerMenu, prompt, stdin, stdout, toggleThinking, runtime.config.thinkingClickCollapse]);
2297
2634
  useInput9((input, key) => {
2635
+ const modalOpen = !!(pending || planAsk || picker || listPicker || mcpMenu || providerMenu || prompt);
2298
2636
  if (key.tab && key.shift) {
2299
2637
  const next = nextMode(runtime.agent.permissions.mode);
2300
2638
  runtime.agent.permissions.mode = next;
2301
2639
  setMode(next);
2302
- } else if (key.escape && busy && !pending && !planAsk) {
2640
+ } else if (key.escape && busy && !modalOpen) {
2303
2641
  controller.current?.abort();
2304
2642
  setCancelling(true);
2305
2643
  if (queued.length) {
@@ -2316,10 +2654,13 @@ ARGUMENTS: ${slash.args}`);
2316
2654
  notice(t("No image on the clipboard."), "warn");
2317
2655
  }
2318
2656
  })();
2319
- } else if (matchKeybind(runtime.config.keybinds.thinking, input, key)) {
2657
+ } else if (matchKeybind(runtime.config.keybinds.thinking, input, key) || key.meta && !key.ctrl && input.toLowerCase() === "t") {
2320
2658
  toggleThinking();
2321
2659
  } else if (matchKeybind(runtime.config.keybinds.toolOutput, input, key)) {
2322
2660
  setShowToolOutput((prev) => !prev);
2661
+ if (maxSplitIdxRef.current > 0) refreshStatic();
2662
+ } else if (key.ctrl && input.toLowerCase() === "l") {
2663
+ refreshStatic();
2323
2664
  }
2324
2665
  });
2325
2666
  const pickMcp = async (name) => {
@@ -2384,8 +2725,21 @@ ARGUMENTS: ${slash.args}`);
2384
2725
  pending?.resolve(a);
2385
2726
  setAsks((q) => q.slice(1));
2386
2727
  };
2728
+ const capLiveMarkdown = (raw) => {
2729
+ if (!isRealTty) return raw;
2730
+ const lines = raw.split("\n");
2731
+ if (lines.length <= maxLiveLines) return raw;
2732
+ const hidden = lines.length - maxLiveLines;
2733
+ const headFence = lines.slice(0, hidden).find((l) => l.trimStart().startsWith("```"));
2734
+ const prefix = headFence ? `${headFence}
2735
+ \u2026 (${hidden} lines above) \u2026
2736
+ ` : `\u2026 (${hidden} lines above) \u2026
2737
+ `;
2738
+ return `${prefix}${lines.slice(-maxLiveLines).join("\n")}`;
2739
+ };
2387
2740
  const cwd = runtime.cwd.startsWith(homedir()) ? `~${runtime.cwd.slice(homedir().length)}` : runtime.cwd;
2388
2741
  const renderEntry = (e) => {
2742
+ const isLive = transcript.live.some((le) => le.id === e.id);
2389
2743
  switch (e.kind) {
2390
2744
  case "header":
2391
2745
  return /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text16, { children: [
@@ -2395,34 +2749,53 @@ ARGUMENTS: ${slash.args}`);
2395
2749
  ] }) }, e.id);
2396
2750
  case "user":
2397
2751
  return /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `> ${e.text}` }) }, e.id);
2398
- case "assistant":
2752
+ case "assistant": {
2753
+ const displayText = isLive ? capLiveMarkdown(e.text) : e.text;
2399
2754
  if (e.cont) {
2400
- return e.text ? /* @__PURE__ */ jsx16(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Markdown, { text: e.text }) }, e.id) : null;
2755
+ return displayText ? /* @__PURE__ */ jsx16(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Markdown, { text: displayText }) }, e.id) : null;
2401
2756
  }
2402
- if (!e.text.trim()) return null;
2757
+ if (!displayText.trim()) return null;
2403
2758
  return /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, children: [
2404
2759
  /* @__PURE__ */ jsx16(Text16, { color: color(ACCENT), children: "\u25CF " }),
2405
- /* @__PURE__ */ jsx16(Markdown, { text: e.text.replace(/^\s+/, "") })
2760
+ /* @__PURE__ */ jsx16(Markdown, { text: displayText.replace(/^\s+/, "") })
2406
2761
  ] }, e.id);
2762
+ }
2407
2763
  case "thinking": {
2408
2764
  const text = e.text.trim();
2409
2765
  if (!text) return null;
2410
- const lines = text.split("\n").length;
2766
+ const allLines = text.split("\n");
2767
+ const lines = allLines.length;
2768
+ const shownText = isLive && isRealTty && allLines.length > maxLiveLines ? `\u2026 (${allLines.length - maxLiveLines} lines above) \u2026
2769
+ ${allLines.slice(-maxLiveLines).join("\n")}` : text;
2411
2770
  const isTarget = e.id === lastThinkingEntryId;
2412
- return /* @__PURE__ */ jsxs12(Box13, { ref: isTarget ? thinkingTargetRef : void 0, marginTop: 1, flexDirection: "column", children: [
2413
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
2414
- /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: showThinking ? t("\u273B Thinking") : t("\u273B Thinking \xB7 {n} lines", { n: lines }) }),
2415
- /* @__PURE__ */ jsxs12(Text16, { dimColor: true, children: [
2416
- " \xB7 ",
2417
- runtime.config.keybinds.thinking.spec,
2418
- runtime.config.thinkingClickCollapse ? ` \xB7 ${t("click to toggle")}` : ""
2419
- ] })
2420
- ] }),
2421
- showThinking ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
2422
- /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("Model: {model} \xB7 reasoning: {level}", { model: runtime.modelRef, level: reasoning }) }),
2423
- /* @__PURE__ */ jsx16(Text16, { dimColor: true, italic: true, children: text })
2424
- ] }) : null
2425
- ] }, e.id);
2771
+ return /* @__PURE__ */ jsxs12(
2772
+ Box13,
2773
+ {
2774
+ ref: (el) => {
2775
+ if (isTarget) thinkingTargetRef.current = el;
2776
+ if (el) thinkingEntryRefs.current.set(e.id, el);
2777
+ else thinkingEntryRefs.current.delete(e.id);
2778
+ },
2779
+ marginTop: 1,
2780
+ flexDirection: "column",
2781
+ children: [
2782
+ /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
2783
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: showThinking ? t("\u273B Thinking") : t("\u273B Thinking \xB7 {n} lines", { n: lines }) }),
2784
+ /* @__PURE__ */ jsxs12(Text16, { dimColor: true, children: [
2785
+ " \xB7 ",
2786
+ runtime.config.keybinds.thinking.spec,
2787
+ " / alt+t",
2788
+ runtime.config.thinkingClickCollapse ? ` \xB7 ${t("click to toggle")}` : ""
2789
+ ] })
2790
+ ] }),
2791
+ showThinking ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
2792
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("Model: {model} \xB7 reasoning: {level}", { model: runtime.modelRef, level: reasoning }) }),
2793
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, italic: true, children: shownText })
2794
+ ] }) : null
2795
+ ]
2796
+ },
2797
+ e.id
2798
+ );
2426
2799
  }
2427
2800
  case "tool":
2428
2801
  return /* @__PURE__ */ jsx16(ToolBlock, { tool: e.tool, target: e.target, output: e.output, display: e.display, isError: e.isError, done: e.done, expanded: showToolOutput, sub: e.sub, moreBind: runtime.config.keybinds.toolOutput.spec }, e.id);
@@ -2436,24 +2809,36 @@ ARGUMENTS: ${slash.args}`);
2436
2809
  const activeSpinnerSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 20 ? `${activeTarget.slice(0, 20)}\u2026` : activeTarget}` : ""}` : "";
2437
2810
  const activeToolSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 68 ? `${activeTarget.slice(0, 68)}\u2026` : activeTarget}` : ""}` : "";
2438
2811
  return /* @__PURE__ */ jsxs12(Box13, { ref: liveRootRef, flexDirection: "column", children: [
2439
- /* @__PURE__ */ jsx16(Static, { items: staticDone, children: renderEntry }),
2812
+ /* @__PURE__ */ jsx16(Static, { items: staticDone, children: renderEntry }, staticKey),
2440
2813
  activeTurnDone.map(renderEntry),
2441
2814
  transcript.live.map(renderEntry),
2442
- busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(Box13, { ref: lastThinkingEntryId === void 0 ? thinkingTargetRef : void 0, marginTop: 1, flexDirection: "column", children: [
2443
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
2444
- /* @__PURE__ */ jsx16(Spinner, { label: cancelling ? t("Cancelling") : activeSpinnerSummary ? `${t("Thinking")} \xB7 ${activeSpinnerSummary}` : t("Thinking"), startedAt }),
2445
- /* @__PURE__ */ jsxs12(Text16, { dimColor: true, children: [
2446
- " \xB7 ",
2447
- runtime.config.keybinds.thinking.spec,
2448
- runtime.config.thinkingClickCollapse ? ` \xB7 ${t("click to toggle")}` : ""
2449
- ] })
2450
- ] }),
2451
- showThinking && lastThinkingEntryId === void 0 ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
2452
- /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("Model: {model} \xB7 reasoning: {level}", { model: runtime.modelRef, level: reasoning }) }),
2453
- activeToolSummary ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `\u25B6 ${activeToolSummary}` }) : null,
2454
- !activeToolSummary ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("No reasoning summary received yet; tool activity remains visible above.") }) : null
2455
- ] }) : null
2456
- ] }) : null,
2815
+ busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(
2816
+ Box13,
2817
+ {
2818
+ ref: (el) => {
2819
+ spinnerTargetRef.current = el;
2820
+ if (lastThinkingEntryId === void 0) thinkingTargetRef.current = el;
2821
+ },
2822
+ marginTop: 1,
2823
+ flexDirection: "column",
2824
+ children: [
2825
+ /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
2826
+ /* @__PURE__ */ jsx16(Spinner, { label: cancelling ? t("Cancelling") : activeSpinnerSummary ? `${t("Thinking")} \xB7 ${activeSpinnerSummary}` : t("Thinking"), startedAt }),
2827
+ /* @__PURE__ */ jsxs12(Text16, { dimColor: true, children: [
2828
+ " \xB7 ",
2829
+ runtime.config.keybinds.thinking.spec,
2830
+ " / alt+t",
2831
+ runtime.config.thinkingClickCollapse ? ` \xB7 ${t("click to toggle")}` : ""
2832
+ ] })
2833
+ ] }),
2834
+ showThinking && lastThinkingEntryId === void 0 ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
2835
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("Model: {model} \xB7 reasoning: {level}", { model: runtime.modelRef, level: reasoning }) }),
2836
+ activeToolSummary ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `\u25B6 ${activeToolSummary}` }) : null,
2837
+ !activeToolSummary ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("No reasoning summary received yet; tool activity remains visible above.") }) : null
2838
+ ] }) : null
2839
+ ]
2840
+ }
2841
+ ) : null,
2457
2842
  pending ? /* @__PURE__ */ jsx16(PermissionPrompt, { request: pending.request, onAnswer: answer }, pending.id) : null,
2458
2843
  picker ? /* @__PURE__ */ jsx16(
2459
2844
  ModelPicker,
@@ -2506,7 +2891,7 @@ ARGUMENTS: ${slash.args}`);
2506
2891
  import { jsx as jsx17 } from "react/jsx-runtime";
2507
2892
  async function startInteractive(rt, opts) {
2508
2893
  if (opts.resume) {
2509
- const { Session: Session2 } = await import("./session-ML2DPI7P.js");
2894
+ const { Session: Session2 } = await import("./session-QZAXEQRE.js");
2510
2895
  const list = Session2.list(rt.home, rt.cwd).slice(0, 20);
2511
2896
  if (!list.length) console.log(t("No sessions in this folder yet; starting a new one."));
2512
2897
  else {
@@ -2522,7 +2907,7 @@ async function startInteractive(rt, opts) {
2522
2907
  if (chosen) rt.resume(chosen.session);
2523
2908
  }
2524
2909
  }
2525
- const { serversToStart, setProjectTrust } = await import("./config-JCI7NAVY.js");
2910
+ const { serversToStart, setProjectTrust } = await import("./config-PVEZPRMH.js");
2526
2911
  const plan = serversToStart(rt.home, rt.cwd);
2527
2912
  if (plan.needTrust.length) {
2528
2913
  const { createInterface } = await import("readline/promises");