@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.
- package/dist/{acp-M22V2CHD.js → acp-XGG33CZJ.js} +2 -2
- package/dist/{args-TFKRCKTY.js → args-SQRIBI2Q.js} +4 -4
- package/dist/{chunk-27REMLDV.js → chunk-34Z6BXTO.js} +1 -1
- package/dist/{chunk-6EGQI6HJ.js → chunk-3MCGYPYI.js} +1 -1
- package/dist/{chunk-3JQZ2IKV.js → chunk-3WYR6KVQ.js} +3 -3
- package/dist/{chunk-D2C6EOOS.js → chunk-42VCJJTW.js} +7 -7
- package/dist/{chunk-WTCWBY2D.js → chunk-54PFT7F5.js} +2 -2
- package/dist/{chunk-JVQZWTPV.js → chunk-6GC5I2RK.js} +4 -4
- package/dist/{chunk-NSARPM5C.js → chunk-CSIQDFEF.js} +1 -1
- package/dist/{chunk-XSUFHD67.js → chunk-EAR2C6TS.js} +1 -1
- package/dist/{chunk-LHLNK7HP.js → chunk-EG7GJBSV.js} +4 -4
- package/dist/{chunk-XQVEAF3F.js → chunk-HR7T3BY2.js} +2 -2
- package/dist/{chunk-BR63GX6T.js → chunk-L5FKXSFK.js} +4 -4
- package/dist/{chunk-D7GAZKXT.js → chunk-SE6SM2EI.js} +3 -3
- package/dist/{chunk-AK6TWKET.js → chunk-T5Z3FCT2.js} +2 -2
- package/dist/{chunk-FW6W5MN7.js → chunk-TCRAN6ZO.js} +11 -11
- package/dist/{chunk-DZPBWYTD.js → chunk-V4LPDP3Z.js} +1 -1
- package/dist/{chunk-O5ATYRUP.js → chunk-WSSJH765.js} +1 -1
- package/dist/{chunk-EOOWV6C6.js → chunk-XCVYR5BK.js} +1 -0
- package/dist/{chunk-YNI4GHBI.js → chunk-Y57FCAGM.js} +1 -1
- package/dist/cli.js +38 -38
- package/dist/{completion-JCBMQKNQ.js → completion-FETH4OGC.js} +1 -1
- package/dist/{config-2MR6V7U7.js → config-BUSQGUWY.js} +2 -2
- package/dist/{config-JCI7NAVY.js → config-PVEZPRMH.js} +3 -3
- package/dist/{ghBotCli-T7AWXAZ2.js → ghBotCli-X6HEOQMZ.js} +14 -14
- package/dist/{integrationCli-KOCF73GG.js → integrationCli-552UYCXJ.js} +2 -2
- package/dist/{login-OIYX4D3X.js → login-SWK74XO5.js} +4 -4
- package/dist/{mcpCli-IBC3WCHG.js → mcpCli-DH4R5B7F.js} +6 -6
- package/dist/{modelsCli-XZLHNWKL.js → modelsCli-4YEUPYBF.js} +6 -6
- package/dist/{oauthLogin-TUXZLIQN.js → oauthLogin-NR22NWEV.js} +1 -1
- package/dist/{pluginsCli-X7NZRU7X.js → pluginsCli-CIW35MNB.js} +2 -2
- package/dist/{prCli-GPDLWYWC.js → prCli-6FCI36AJ.js} +1 -1
- package/dist/providerCli-N64TSYMT.js +15 -0
- package/dist/sdk.js +4 -3
- package/dist/{serve-TJCT3TLE.js → serve-DAC2JGEH.js} +15 -15
- package/dist/{serve-A764QS5U.js → serve-N2TUTZGD.js} +3 -3
- package/dist/session-QZAXEQRE.js +12 -0
- package/dist/setup-2KMWTHDZ.js +24 -0
- package/dist/{statsCli-FKGON32O.js → statsCli-FR7GOKWO.js} +1 -1
- package/dist/{transferCli-HA5IKJKF.js → transferCli-ZY7WIIPY.js} +4 -4
- package/dist/ui/complete.d.ts +2 -0
- package/dist/ui/thinkingMouse.d.ts +4 -0
- package/dist/{ui-SMY7QSFF.js → ui-A7DY4X4E.js} +538 -153
- package/dist/{update-BUFIPEKY.js → update-36PG64GC.js} +2 -2
- package/package.json +1 -1
- package/dist/providerCli-WCFA36S4.js +0 -15
- package/dist/session-ML2DPI7P.js +0 -12
- package/dist/setup-QYCA7ATT.js +0 -24
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CATALOG,
|
|
4
4
|
fillTemplate
|
|
5
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
37
|
+
} from "./chunk-34Z6BXTO.js";
|
|
38
38
|
import {
|
|
39
39
|
Session
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-Y57FCAGM.js";
|
|
41
41
|
import {
|
|
42
42
|
contentText
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-L5FKXSFK.js";
|
|
44
44
|
import {
|
|
45
45
|
REASONING_LEVELS,
|
|
46
46
|
assertReasoningSupported,
|
|
47
47
|
parseReasoningLevel,
|
|
48
48
|
supportedReasoningLevels
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-EAR2C6TS.js";
|
|
50
50
|
import {
|
|
51
51
|
addGlobalServer,
|
|
52
52
|
globalMcpPath,
|
|
53
53
|
readMcpFile,
|
|
54
54
|
removeGlobalServer
|
|
55
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
780
|
-
|
|
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,
|
|
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+;)
|
|
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
|
|
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(
|
|
924
|
-
else if (key.downArrow) setIndex(
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
1128
|
+
function listFileSuggestions(value, cwd, limit = 8) {
|
|
1023
1129
|
const match = /(^|\s)@([^\s]*)$/.exec(value);
|
|
1024
|
-
if (!match) return
|
|
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
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1046
|
-
|
|
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] =
|
|
1303
|
+
const [line, setLine] = useState6({ value: "", cursor: 0 });
|
|
1185
1304
|
const { value } = line;
|
|
1186
|
-
const [historyIndex, setHistoryIndex] =
|
|
1187
|
-
const
|
|
1188
|
-
const [
|
|
1189
|
-
const [
|
|
1190
|
-
const [
|
|
1191
|
-
const
|
|
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
|
|
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 ?
|
|
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
|
|
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] =
|
|
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
|
|
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] =
|
|
1356
|
-
useInput7((
|
|
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:
|
|
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, {
|
|
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({
|
|
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] =
|
|
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] =
|
|
1626
|
-
const [cancelling, setCancelling] =
|
|
1627
|
-
const [startedAt, setStartedAt] =
|
|
1628
|
-
const [mode, setMode] =
|
|
1629
|
-
const [reasoning, setReasoning] =
|
|
1630
|
-
const [tokens, setTokens] =
|
|
1631
|
-
const [asks, setAsks] =
|
|
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 =
|
|
1634
|
-
const [picker, setPicker] =
|
|
1635
|
-
const [providerMenu, setProviderMenu] =
|
|
1636
|
-
const [prompt, setPrompt] =
|
|
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] =
|
|
1639
|
-
const [history, setHistory] =
|
|
1640
|
-
const controller =
|
|
1641
|
-
const [todos, setTodos] =
|
|
1642
|
-
const [mcpMenu, setMcpMenu] =
|
|
1643
|
-
const [listPicker, setListPicker] =
|
|
1644
|
-
const [planAsk, setPlanAsk] =
|
|
1645
|
-
const [queued, setQueued] =
|
|
1646
|
-
const [showThinking, setShowThinking] =
|
|
1647
|
-
const showThinkingRef =
|
|
1648
|
-
const [showToolOutput, setShowToolOutput] =
|
|
1649
|
-
const [,
|
|
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
|
|
1659
|
-
const
|
|
1660
|
-
const
|
|
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 =
|
|
1667
|
-
const toolTargets =
|
|
1668
|
-
const editedInTurn =
|
|
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(
|
|
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(
|
|
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:
|
|
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)
|
|
2241
|
-
|
|
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 =
|
|
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
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
2286
|
-
if (
|
|
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,
|
|
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 && !
|
|
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
|
|
2755
|
+
return displayText ? /* @__PURE__ */ jsx16(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Markdown, { text: displayText }) }, e.id) : null;
|
|
2401
2756
|
}
|
|
2402
|
-
if (!
|
|
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:
|
|
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
|
|
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(
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
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(
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
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-
|
|
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-
|
|
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");
|