@botconnector/bccli 0.4.5 → 0.4.6
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-YGQXX2L7.js} +1 -1
- package/dist/{chunk-JVQZWTPV.js → chunk-2Y6TH4MA.js} +1 -1
- package/dist/{chunk-FW6W5MN7.js → chunk-AN7SNPFA.js} +4 -4
- package/dist/{chunk-BR63GX6T.js → chunk-BBZLRWPR.js} +2 -2
- package/dist/{chunk-YNI4GHBI.js → chunk-BL2KOEXJ.js} +1 -1
- package/dist/{chunk-D2C6EOOS.js → chunk-CILTKMZW.js} +1 -1
- package/dist/{chunk-LHLNK7HP.js → chunk-QZKB6VTU.js} +1 -1
- package/dist/{chunk-DZPBWYTD.js → chunk-ZGIK5INC.js} +1 -1
- package/dist/cli.js +18 -18
- package/dist/{ghBotCli-T7AWXAZ2.js → ghBotCli-IXRHTHC5.js} +5 -5
- package/dist/{login-OIYX4D3X.js → login-IMVSN4X3.js} +1 -1
- package/dist/{modelsCli-XZLHNWKL.js → modelsCli-BOPAZIG4.js} +2 -2
- package/dist/{providerCli-WCFA36S4.js → providerCli-IRJTX5JF.js} +3 -3
- package/dist/sdk.js +2 -2
- package/dist/{serve-TJCT3TLE.js → serve-XRFCR6MG.js} +6 -6
- package/dist/{session-ML2DPI7P.js → session-NPKTEDKV.js} +2 -2
- package/dist/{setup-QYCA7ATT.js → setup-KYDIUOOG.js} +5 -5
- package/dist/{transferCli-HA5IKJKF.js → transferCli-BQXPUFPH.js} +2 -2
- package/dist/ui/complete.d.ts +2 -0
- package/dist/ui/thinkingMouse.d.ts +2 -0
- package/dist/{ui-SMY7QSFF.js → ui-S6OQOIFO.js} +368 -101
- package/dist/{update-BUFIPEKY.js → update-SYDSYLR6.js} +1 -1
- package/package.json +1 -1
|
@@ -37,10 +37,10 @@ import {
|
|
|
37
37
|
} from "./chunk-27REMLDV.js";
|
|
38
38
|
import {
|
|
39
39
|
Session
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-BL2KOEXJ.js";
|
|
41
41
|
import {
|
|
42
42
|
contentText
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-BBZLRWPR.js";
|
|
44
44
|
import {
|
|
45
45
|
REASONING_LEVELS,
|
|
46
46
|
assertReasoningSupported,
|
|
@@ -77,7 +77,7 @@ 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 = [
|
|
@@ -775,14 +775,38 @@ function masked(s) {
|
|
|
775
775
|
|
|
776
776
|
// src/ui/EditableText.tsx
|
|
777
777
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
778
|
+
var MAX_VISIBLE_LINES = 10;
|
|
779
|
+
function windowedState(s) {
|
|
780
|
+
const lines = s.value.split("\n");
|
|
781
|
+
if (lines.length <= MAX_VISIBLE_LINES) return { view: s, hiddenAbove: 0, hiddenBelow: 0 };
|
|
782
|
+
const cursorLine = s.value.slice(0, s.cursor).split("\n").length - 1;
|
|
783
|
+
const startLine = Math.max(0, Math.min(cursorLine - Math.floor(MAX_VISIBLE_LINES / 2), lines.length - MAX_VISIBLE_LINES));
|
|
784
|
+
const endLine = Math.min(lines.length, startLine + MAX_VISIBLE_LINES);
|
|
785
|
+
let charStart = 0;
|
|
786
|
+
for (let i = 0; i < startLine; i++) charStart += lines[i].length + 1;
|
|
787
|
+
const sliced = lines.slice(startLine, endLine).join("\n");
|
|
788
|
+
const relCursor = Math.max(0, Math.min(sliced.length, s.cursor - charStart));
|
|
789
|
+
return {
|
|
790
|
+
view: { value: sliced, cursor: relCursor },
|
|
791
|
+
hiddenAbove: startLine,
|
|
792
|
+
hiddenBelow: lines.length - endLine
|
|
793
|
+
};
|
|
794
|
+
}
|
|
778
795
|
function EditableText({ state, mask = false, showCursor = true }) {
|
|
779
|
-
const
|
|
780
|
-
|
|
796
|
+
const raw = mask ? masked(state) : state;
|
|
797
|
+
const { view, hiddenAbove, hiddenBelow } = windowedState(raw);
|
|
798
|
+
const prefix = hiddenAbove > 0 ? `\u2026 (+${hiddenAbove} lines above)
|
|
799
|
+
` : "";
|
|
800
|
+
const suffix = hiddenBelow > 0 ? `
|
|
801
|
+
\u2026 (+${hiddenBelow} lines below)` : "";
|
|
802
|
+
if (!showCursor) return /* @__PURE__ */ jsx2(Fragment, { children: `${prefix}${view.value}${suffix}` });
|
|
781
803
|
const { before, at, after } = splitAtCursor(view);
|
|
782
804
|
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
805
|
+
prefix ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: prefix }) : null,
|
|
783
806
|
before,
|
|
784
807
|
/* @__PURE__ */ jsx2(Text2, { inverse: true, children: at }),
|
|
785
|
-
after
|
|
808
|
+
after,
|
|
809
|
+
suffix ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: suffix }) : null
|
|
786
810
|
] });
|
|
787
811
|
}
|
|
788
812
|
|
|
@@ -819,8 +843,18 @@ function clickedThinking(click, cursorRow, rootHeight, target) {
|
|
|
819
843
|
const col = target.x + 1;
|
|
820
844
|
return (click.y === row || click.y === row - 1) && click.x >= col && click.x < col + Math.min(target.width, 46);
|
|
821
845
|
}
|
|
846
|
+
function parseMouseWheel(input) {
|
|
847
|
+
const match = /^\[<?(\d+);\d+;\d+[mM]$/.exec(input);
|
|
848
|
+
if (!match) return void 0;
|
|
849
|
+
const button = Number(match[1]);
|
|
850
|
+
if ((button & 64) === 0) return void 0;
|
|
851
|
+
const dir = button & 3;
|
|
852
|
+
if (dir === 0) return "up";
|
|
853
|
+
if (dir === 1) return "down";
|
|
854
|
+
return void 0;
|
|
855
|
+
}
|
|
822
856
|
function isMouseControlInput(input) {
|
|
823
|
-
return /^\[<(?:\d+;){2}\d+[mM]$/.test(input) || /^\[(?:\d+;)
|
|
857
|
+
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
858
|
}
|
|
825
859
|
function parseThinkingReports(value) {
|
|
826
860
|
const events = [];
|
|
@@ -875,9 +909,20 @@ function ListPicker({ title, items, onPick }) {
|
|
|
875
909
|
const shown = items.filter((i) => i.label.toLowerCase().includes(filter.toLowerCase()));
|
|
876
910
|
const cursor = Math.min(index, Math.max(0, shown.length - 1));
|
|
877
911
|
useInput2((input, key) => {
|
|
912
|
+
const wheel = parseMouseWheel(input);
|
|
913
|
+
if (wheel === "up") {
|
|
914
|
+
setIndex(Math.max(0, cursor - 1));
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
if (wheel === "down") {
|
|
918
|
+
setIndex(Math.min(Math.max(0, shown.length - 1), cursor + 1));
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
878
921
|
if (isMouseControlInput(input)) return;
|
|
879
922
|
if (key.upArrow) setIndex(Math.max(0, cursor - 1));
|
|
880
|
-
else if (key.downArrow) setIndex(Math.min(shown.length - 1, cursor + 1));
|
|
923
|
+
else if (key.downArrow) setIndex(Math.min(Math.max(0, shown.length - 1), cursor + 1));
|
|
924
|
+
else if (key.pageUp) setIndex(Math.max(0, cursor - VISIBLE));
|
|
925
|
+
else if (key.pageDown) setIndex(Math.min(Math.max(0, shown.length - 1), cursor + VISIBLE));
|
|
881
926
|
else if (key.return && !shown[cursor]?.disabled) onPick(shown[cursor]?.id);
|
|
882
927
|
else if (key.escape) onPick(void 0);
|
|
883
928
|
else if (key.backspace || key.delete) {
|
|
@@ -917,11 +962,23 @@ function ModelPicker({ groups, current, onPick }) {
|
|
|
917
962
|
for (const m of models) rows.push({ kind: "model", ref: `${g.providerId}/${m}`, label: m });
|
|
918
963
|
}
|
|
919
964
|
const selectable = rows.flatMap((r, i) => r.kind === "model" ? [i] : []);
|
|
920
|
-
const
|
|
965
|
+
const clampedIndex = Math.min(index, Math.max(0, selectable.length - 1));
|
|
966
|
+
const cursor = selectable[clampedIndex] ?? -1;
|
|
921
967
|
useInput3((input, key) => {
|
|
968
|
+
const wheel = parseMouseWheel(input);
|
|
969
|
+
if (wheel === "up") {
|
|
970
|
+
setIndex(Math.max(0, clampedIndex - 1));
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
if (wheel === "down") {
|
|
974
|
+
setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + 1));
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
922
977
|
if (isMouseControlInput(input)) return;
|
|
923
|
-
if (key.upArrow) setIndex(
|
|
924
|
-
else if (key.downArrow) setIndex(
|
|
978
|
+
if (key.upArrow) setIndex(Math.max(0, clampedIndex - 1));
|
|
979
|
+
else if (key.downArrow) setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + 1));
|
|
980
|
+
else if (key.pageUp) setIndex(Math.max(0, clampedIndex - VISIBLE2));
|
|
981
|
+
else if (key.pageDown) setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + VISIBLE2));
|
|
925
982
|
else if (key.return) {
|
|
926
983
|
const row = rows[cursor];
|
|
927
984
|
if (row?.kind === "model") onPick(row.ref);
|
|
@@ -952,9 +1009,21 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
|
952
1009
|
function ProviderMenu({ entries, onPick }) {
|
|
953
1010
|
const rows = [...entries.map((e) => e.id), "custom"];
|
|
954
1011
|
const [index, setIndex] = useState4(0);
|
|
955
|
-
useInput4((
|
|
1012
|
+
useInput4((input, key) => {
|
|
1013
|
+
const wheel = parseMouseWheel(input);
|
|
1014
|
+
if (wheel === "up") {
|
|
1015
|
+
setIndex((i) => Math.max(0, i - 1));
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (wheel === "down") {
|
|
1019
|
+
setIndex((i) => Math.min(rows.length - 1, i + 1));
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (isMouseControlInput(input) || isFocusReport(input)) return;
|
|
956
1023
|
if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
|
|
957
1024
|
else if (key.downArrow) setIndex((i) => Math.min(rows.length - 1, i + 1));
|
|
1025
|
+
else if (key.pageUp) setIndex((i) => Math.max(0, i - 10));
|
|
1026
|
+
else if (key.pageDown) setIndex((i) => Math.min(rows.length - 1, i + 10));
|
|
958
1027
|
else if (key.return) onPick(rows[index]);
|
|
959
1028
|
else if (key.escape) onPick(void 0);
|
|
960
1029
|
});
|
|
@@ -970,6 +1039,7 @@ function ProviderMenu({ entries, onPick }) {
|
|
|
970
1039
|
|
|
971
1040
|
// src/ui/PermissionPrompt.tsx
|
|
972
1041
|
import { Box as Box7, Text as Text8, useInput as useInput5 } from "ink";
|
|
1042
|
+
import { useState as useState5 } from "react";
|
|
973
1043
|
|
|
974
1044
|
// src/ui/DiffView.tsx
|
|
975
1045
|
import { Box as Box6, Text as Text7 } from "ink";
|
|
@@ -985,11 +1055,41 @@ function DiffView({ diff }) {
|
|
|
985
1055
|
// src/ui/PermissionPrompt.tsx
|
|
986
1056
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
987
1057
|
var VERB = { edit: "edit", write: "write", bash: "run", fetch: "fetch" };
|
|
1058
|
+
var MAX_DIFF_LINES = 16;
|
|
988
1059
|
function PermissionPrompt({
|
|
989
1060
|
request,
|
|
990
1061
|
onAnswer
|
|
991
1062
|
}) {
|
|
1063
|
+
const diffLines = request.preview ? request.preview.split("\n") : [];
|
|
1064
|
+
const maxOffset = Math.max(0, diffLines.length - MAX_DIFF_LINES);
|
|
1065
|
+
const [offset, setOffset] = useState5(0);
|
|
992
1066
|
useInput5((input, key) => {
|
|
1067
|
+
const wheel = parseMouseWheel(input);
|
|
1068
|
+
if (wheel === "up") {
|
|
1069
|
+
setOffset((o) => Math.max(0, o - 2));
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
if (wheel === "down") {
|
|
1073
|
+
setOffset((o) => Math.min(maxOffset, o + 2));
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (isMouseControlInput(input) || isFocusReport(input)) return;
|
|
1077
|
+
if (key.upArrow) {
|
|
1078
|
+
setOffset((o) => Math.max(0, o - 1));
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (key.downArrow) {
|
|
1082
|
+
setOffset((o) => Math.min(maxOffset, o + 1));
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if (key.pageUp) {
|
|
1086
|
+
setOffset((o) => Math.max(0, o - MAX_DIFF_LINES));
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
if (key.pageDown) {
|
|
1090
|
+
setOffset((o) => Math.min(maxOffset, o + MAX_DIFF_LINES));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
993
1093
|
const k2 = input.toLowerCase();
|
|
994
1094
|
if (k2 === "y") onAnswer("yes");
|
|
995
1095
|
else if (k2 === "a" && request.sessionRules?.length) onAnswer("session");
|
|
@@ -998,9 +1098,11 @@ function PermissionPrompt({
|
|
|
998
1098
|
});
|
|
999
1099
|
const rules = request.sessionRules ?? [];
|
|
1000
1100
|
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(", ") });
|
|
1101
|
+
const visibleDiff = diffLines.length > MAX_DIFF_LINES ? diffLines.slice(offset, offset + MAX_DIFF_LINES).join("\n") : request.preview;
|
|
1001
1102
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: color("yellow"), paddingX: 1, children: [
|
|
1002
1103
|
/* @__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
|
-
|
|
1104
|
+
visibleDiff ? /* @__PURE__ */ jsx8(DiffView, { diff: visibleDiff }) : null,
|
|
1105
|
+
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
1106
|
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1005
1107
|
/* @__PURE__ */ jsx8(Text8, { color: color("green"), children: t("[y] yes") }),
|
|
1006
1108
|
" ",
|
|
@@ -1013,19 +1115,19 @@ function PermissionPrompt({
|
|
|
1013
1115
|
|
|
1014
1116
|
// src/ui/PromptInput.tsx
|
|
1015
1117
|
import { Box as Box8, Text as Text9, useInput as useInput6 } from "ink";
|
|
1016
|
-
import { useEffect, useState as
|
|
1118
|
+
import { useEffect, useRef, useState as useState6 } from "react";
|
|
1017
1119
|
|
|
1018
1120
|
// src/ui/complete.ts
|
|
1019
1121
|
import { existsSync, globSync, readFileSync, statSync } from "fs";
|
|
1020
1122
|
import { isAbsolute, join, relative, resolve } from "path";
|
|
1021
1123
|
var MAX_ATTACH_BYTES = 32768;
|
|
1022
|
-
function
|
|
1124
|
+
function listFileSuggestions(value, cwd, limit = 8) {
|
|
1023
1125
|
const match = /(^|\s)@([^\s]*)$/.exec(value);
|
|
1024
|
-
if (!match) return
|
|
1126
|
+
if (!match) return [];
|
|
1025
1127
|
const prefix = match[2];
|
|
1026
1128
|
const escaped = prefix.replace(/([*?[\]{}()!])/g, "\\$1");
|
|
1027
1129
|
const pattern = `${escaped}*`;
|
|
1028
|
-
if (validateGlobPattern(pattern)) return
|
|
1130
|
+
if (validateGlobPattern(pattern)) return [];
|
|
1029
1131
|
const exclude = ignorePatterns(cwd);
|
|
1030
1132
|
const formatEntry = (entry2) => {
|
|
1031
1133
|
const normalized = normalizeGlobPath(entry2);
|
|
@@ -1035,17 +1137,30 @@ function completeFile(value, cwd) {
|
|
|
1035
1137
|
return normalized;
|
|
1036
1138
|
}
|
|
1037
1139
|
};
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1140
|
+
try {
|
|
1141
|
+
let found = globSync(pattern, { cwd, exclude }).map(formatEntry).sort();
|
|
1142
|
+
if (!found.length && prefix && !prefix.includes("/")) {
|
|
1143
|
+
const deepPattern = `**/*${escaped}*`;
|
|
1144
|
+
if (!validateGlobPattern(deepPattern)) {
|
|
1145
|
+
found = globSync(deepPattern, { cwd, exclude }).map(formatEntry).sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
1146
|
+
}
|
|
1043
1147
|
}
|
|
1148
|
+
return found.slice(0, limit);
|
|
1149
|
+
} catch {
|
|
1150
|
+
return [];
|
|
1044
1151
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1152
|
+
}
|
|
1153
|
+
function applyFileSuggestion(value, pick) {
|
|
1154
|
+
const match = /(^|\s)@([^\s]*)$/.exec(value);
|
|
1155
|
+
if (!match) return value;
|
|
1156
|
+
const prefix = match[2];
|
|
1047
1157
|
return `${value.slice(0, value.length - prefix.length)}${pick}${pick.endsWith("/") ? "" : " "}`;
|
|
1048
1158
|
}
|
|
1159
|
+
function completeFile(value, cwd) {
|
|
1160
|
+
const found = listFileSuggestions(value, cwd, 1);
|
|
1161
|
+
if (!found.length) return value;
|
|
1162
|
+
return applyFileSuggestion(value, found[0]);
|
|
1163
|
+
}
|
|
1049
1164
|
function expandAtFileMentions(prompt, cwd, readFiles) {
|
|
1050
1165
|
const matches = [...prompt.matchAll(/(?:^|\s)@([^\s]+)/g)];
|
|
1051
1166
|
if (!matches.length) return prompt;
|
|
@@ -1181,14 +1296,15 @@ ${s.value.slice(start)}`, cursor: start }, mode: "insert", handled: true };
|
|
|
1181
1296
|
// src/ui/PromptInput.tsx
|
|
1182
1297
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1183
1298
|
function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected, vim }) {
|
|
1184
|
-
const [line, setLine] =
|
|
1299
|
+
const [line, setLine] = useState6({ value: "", cursor: 0 });
|
|
1185
1300
|
const { value } = line;
|
|
1186
|
-
const [historyIndex, setHistoryIndex] =
|
|
1187
|
-
const
|
|
1188
|
-
const [
|
|
1189
|
-
const [
|
|
1190
|
-
const [
|
|
1191
|
-
const
|
|
1301
|
+
const [historyIndex, setHistoryIndex] = useState6(null);
|
|
1302
|
+
const draftRef = useRef("");
|
|
1303
|
+
const [selected, setSelected] = useState6(0);
|
|
1304
|
+
const [dismissed, setDismissed] = useState6(false);
|
|
1305
|
+
const [vimMode, setVimMode] = useState6("insert");
|
|
1306
|
+
const [vimPending, setVimPending] = useState6(void 0);
|
|
1307
|
+
const injectedSeen = useState6(0);
|
|
1192
1308
|
useEffect(() => {
|
|
1193
1309
|
if (injected && injected.n !== injectedSeen[0]) {
|
|
1194
1310
|
injectedSeen[1](injected.n);
|
|
@@ -1198,7 +1314,10 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
|
|
|
1198
1314
|
}, [injected, injectedSeen]);
|
|
1199
1315
|
const all = [...SLASH_COMMANDS, ...(extraCommands ?? []).filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name))];
|
|
1200
1316
|
const suggestions = historyIndex === null && !dismissed && value.startsWith("/") && !/\s/.test(value) ? all.filter((c) => c.name.startsWith(value.slice(1))).slice(0, 8) : [];
|
|
1201
|
-
const
|
|
1317
|
+
const beforeCursor = value.slice(0, line.cursor);
|
|
1318
|
+
const fileSuggestions = historyIndex === null && !dismissed && !suggestions.length && /(^|\s)@([^\s]*)$/.test(beforeCursor) ? listFileSuggestions(beforeCursor, cwd, 8) : [];
|
|
1319
|
+
const activeCount = suggestions.length || fileSuggestions.length;
|
|
1320
|
+
const highlighted = Math.min(selected, Math.max(0, activeCount - 1));
|
|
1202
1321
|
const typed = (text) => ({ value: text, cursor: text.length });
|
|
1203
1322
|
const edit = (next) => {
|
|
1204
1323
|
setLine(next);
|
|
@@ -1208,6 +1327,13 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
|
|
|
1208
1327
|
};
|
|
1209
1328
|
useInput6(
|
|
1210
1329
|
(input, key) => {
|
|
1330
|
+
const wheel = parseMouseWheel(input);
|
|
1331
|
+
if (wheel) {
|
|
1332
|
+
if (activeCount > 0) {
|
|
1333
|
+
setSelected((current) => wheel === "up" ? Math.max(0, Math.min(current, activeCount - 1) - 1) : Math.min(activeCount - 1, Math.min(current, activeCount - 1) + 1));
|
|
1334
|
+
}
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1211
1337
|
if (isMouseControlInput(input)) return;
|
|
1212
1338
|
if (suggestions.length) {
|
|
1213
1339
|
if (key.escape) {
|
|
@@ -1226,13 +1352,33 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
|
|
|
1226
1352
|
edit(typed(`/${suggestions[highlighted].name} `));
|
|
1227
1353
|
return;
|
|
1228
1354
|
}
|
|
1229
|
-
if (key.return && !key.meta) {
|
|
1355
|
+
if (key.return && !key.meta && !key.shift) {
|
|
1230
1356
|
edit(typed(""));
|
|
1231
1357
|
setHistoryIndex(null);
|
|
1358
|
+
draftRef.current = "";
|
|
1232
1359
|
onSubmit(`/${suggestions[highlighted].name}`);
|
|
1233
1360
|
return;
|
|
1234
1361
|
}
|
|
1235
1362
|
}
|
|
1363
|
+
if (fileSuggestions.length) {
|
|
1364
|
+
if (key.escape) {
|
|
1365
|
+
setDismissed(true);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
if (key.upArrow) {
|
|
1369
|
+
setSelected(Math.max(0, highlighted - 1));
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
if (key.downArrow) {
|
|
1373
|
+
setSelected(Math.min(fileSuggestions.length - 1, highlighted + 1));
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
if (key.tab && !key.shift) {
|
|
1377
|
+
const completed = applyFileSuggestion(beforeCursor, fileSuggestions[highlighted]);
|
|
1378
|
+
edit({ value: completed + value.slice(line.cursor), cursor: completed.length });
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1236
1382
|
if (vim) {
|
|
1237
1383
|
const r = applyVim(line, vimMode, input, key, vimPending);
|
|
1238
1384
|
if (r.handled) {
|
|
@@ -1243,7 +1389,7 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
|
|
|
1243
1389
|
return;
|
|
1244
1390
|
}
|
|
1245
1391
|
}
|
|
1246
|
-
if (key.return && key.meta || key.ctrl && input.toLowerCase() === "j") {
|
|
1392
|
+
if (key.return && (key.meta || key.shift) || key.ctrl && input.toLowerCase() === "j" || input === "\n") {
|
|
1247
1393
|
edit((current) => insert(current, "\n"));
|
|
1248
1394
|
return;
|
|
1249
1395
|
}
|
|
@@ -1258,6 +1404,7 @@ ${value.slice(line.cursor)}`, cursor: before.length });
|
|
|
1258
1404
|
if (!text2) return;
|
|
1259
1405
|
edit(typed(""));
|
|
1260
1406
|
setHistoryIndex(null);
|
|
1407
|
+
draftRef.current = "";
|
|
1261
1408
|
setVimMode("insert");
|
|
1262
1409
|
setVimPending(void 0);
|
|
1263
1410
|
onSubmit(text2);
|
|
@@ -1277,6 +1424,7 @@ ${value.slice(line.cursor)}`, cursor: before.length });
|
|
|
1277
1424
|
}
|
|
1278
1425
|
if (key.upArrow) {
|
|
1279
1426
|
if (!history.length) return;
|
|
1427
|
+
if (historyIndex === null) draftRef.current = value;
|
|
1280
1428
|
const i = historyIndex === null ? history.length - 1 : Math.max(0, historyIndex - 1);
|
|
1281
1429
|
setHistoryIndex(i);
|
|
1282
1430
|
setLine(typed(history[i]));
|
|
@@ -1286,7 +1434,7 @@ ${value.slice(line.cursor)}`, cursor: before.length });
|
|
|
1286
1434
|
if (historyIndex === null) return;
|
|
1287
1435
|
const i = historyIndex + 1;
|
|
1288
1436
|
setHistoryIndex(i >= history.length ? null : i);
|
|
1289
|
-
setLine(typed(i >= history.length ?
|
|
1437
|
+
setLine(typed(i >= history.length ? draftRef.current : history[i]));
|
|
1290
1438
|
return;
|
|
1291
1439
|
}
|
|
1292
1440
|
if (key.tab && !key.shift) {
|
|
@@ -1310,17 +1458,18 @@ ${value.slice(line.cursor)}`, cursor: before.length });
|
|
|
1310
1458
|
t("NORMAL")
|
|
1311
1459
|
] })
|
|
1312
1460
|
] }) }),
|
|
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))
|
|
1461
|
+
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)),
|
|
1462
|
+
fileSuggestions.map((f, i) => /* @__PURE__ */ jsx9(Text9, { color: i === highlighted ? color("green") : void 0, dimColor: i !== highlighted, children: `${i === highlighted ? "\u203A" : " "} @${f}` }, f))
|
|
1314
1463
|
] });
|
|
1315
1464
|
}
|
|
1316
1465
|
|
|
1317
1466
|
// src/ui/Spinner.tsx
|
|
1318
1467
|
import { Text as Text10 } from "ink";
|
|
1319
|
-
import { useEffect as useEffect2, useState as
|
|
1468
|
+
import { useEffect as useEffect2, useState as useState7 } from "react";
|
|
1320
1469
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1321
1470
|
var FRAMES = ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "\u273D"];
|
|
1322
1471
|
function Spinner({ label, startedAt }) {
|
|
1323
|
-
const [tick, setTick] =
|
|
1472
|
+
const [tick, setTick] = useState7(0);
|
|
1324
1473
|
useEffect2(() => {
|
|
1325
1474
|
const id = setInterval(() => setTick((t2) => t2 + 1), 120);
|
|
1326
1475
|
return () => clearInterval(id);
|
|
@@ -1348,14 +1497,26 @@ function StatusBar({ mode, tokens, busy, model, reasoning = "auto" }) {
|
|
|
1348
1497
|
|
|
1349
1498
|
// src/ui/McpMenu.tsx
|
|
1350
1499
|
import { Box as Box9, Text as Text12, useInput as useInput7 } from "ink";
|
|
1351
|
-
import { useState as
|
|
1500
|
+
import { useState as useState8 } from "react";
|
|
1352
1501
|
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1353
1502
|
var MARK = { ready: "\u2713", error: "\u2717", starting: "\u2026", off: "\u25CB" };
|
|
1354
1503
|
function McpMenu({ items, onPick }) {
|
|
1355
|
-
const [index, setIndex] =
|
|
1356
|
-
useInput7((
|
|
1504
|
+
const [index, setIndex] = useState8(0);
|
|
1505
|
+
useInput7((input, key) => {
|
|
1506
|
+
const wheel = parseMouseWheel(input);
|
|
1507
|
+
if (wheel === "up") {
|
|
1508
|
+
setIndex((i) => Math.max(0, i - 1));
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
if (wheel === "down") {
|
|
1512
|
+
setIndex((i) => Math.min(items.length - 1, i + 1));
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
if (isMouseControlInput(input) || isFocusReport(input)) return;
|
|
1357
1516
|
if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
|
|
1358
1517
|
else if (key.downArrow) setIndex((i) => Math.min(items.length - 1, i + 1));
|
|
1518
|
+
else if (key.pageUp) setIndex((i) => Math.max(0, i - 10));
|
|
1519
|
+
else if (key.pageDown) setIndex((i) => Math.min(items.length - 1, i + 10));
|
|
1359
1520
|
else if (key.return) onPick(items[index]?.name);
|
|
1360
1521
|
else if (key.escape) onPick(void 0);
|
|
1361
1522
|
});
|
|
@@ -1370,17 +1531,50 @@ function McpMenu({ items, onPick }) {
|
|
|
1370
1531
|
|
|
1371
1532
|
// src/ui/PlanApproval.tsx
|
|
1372
1533
|
import { Box as Box10, Text as Text13, useInput as useInput8 } from "ink";
|
|
1534
|
+
import { useState as useState9 } from "react";
|
|
1373
1535
|
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1536
|
+
var MAX_PLAN_LINES = 16;
|
|
1374
1537
|
function PlanApproval({ plan, onAnswer }) {
|
|
1538
|
+
const planLines = plan.split("\n");
|
|
1539
|
+
const maxOffset = Math.max(0, planLines.length - MAX_PLAN_LINES);
|
|
1540
|
+
const [offset, setOffset] = useState9(0);
|
|
1375
1541
|
useInput8((input, key) => {
|
|
1542
|
+
const wheel = parseMouseWheel(input);
|
|
1543
|
+
if (wheel === "up") {
|
|
1544
|
+
setOffset((o) => Math.max(0, o - 2));
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
if (wheel === "down") {
|
|
1548
|
+
setOffset((o) => Math.min(maxOffset, o + 2));
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
if (isMouseControlInput(input) || isFocusReport(input)) return;
|
|
1552
|
+
if (key.upArrow) {
|
|
1553
|
+
setOffset((o) => Math.max(0, o - 1));
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
if (key.downArrow) {
|
|
1557
|
+
setOffset((o) => Math.min(maxOffset, o + 1));
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (key.pageUp) {
|
|
1561
|
+
setOffset((o) => Math.max(0, o - MAX_PLAN_LINES));
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
if (key.pageDown) {
|
|
1565
|
+
setOffset((o) => Math.min(maxOffset, o + MAX_PLAN_LINES));
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1376
1568
|
const k2 = input.toLowerCase();
|
|
1377
1569
|
if (k2 === "a") onAnswer("acceptEdits");
|
|
1378
1570
|
else if (k2 === "y") onAnswer("default");
|
|
1379
1571
|
else if (k2 === "n" || key.escape) onAnswer("no");
|
|
1380
1572
|
});
|
|
1573
|
+
const visiblePlan = planLines.length > MAX_PLAN_LINES ? planLines.slice(offset, offset + MAX_PLAN_LINES).join("\n") : plan;
|
|
1381
1574
|
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
1382
1575
|
/* @__PURE__ */ jsx13(Text13, { bold: true, children: t("Plan from the agent") }),
|
|
1383
|
-
/* @__PURE__ */ jsx13(Markdown, { text:
|
|
1576
|
+
/* @__PURE__ */ jsx13(Markdown, { text: visiblePlan, indent: 5 }),
|
|
1577
|
+
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
1578
|
/* @__PURE__ */ jsxs10(Text13, { children: [
|
|
1385
1579
|
/* @__PURE__ */ jsx13(Text13, { color: color("green"), children: t("[a] yes, auto-edit") }),
|
|
1386
1580
|
" ",
|
|
@@ -1402,8 +1596,9 @@ function TodoList({ items }) {
|
|
|
1402
1596
|
|
|
1403
1597
|
// src/ui/ToolBlock.tsx
|
|
1404
1598
|
import { Box as Box12, Text as Text15 } from "ink";
|
|
1405
|
-
import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1599
|
+
import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1406
1600
|
var PREVIEW_LINES = 5;
|
|
1601
|
+
var DIFF_PREVIEW_LINES = 20;
|
|
1407
1602
|
function ToolBlock({ tool, target, output, display, isError, done, expanded, sub, moreBind = "ctrl+o" }) {
|
|
1408
1603
|
const isTask = tool === "task";
|
|
1409
1604
|
const isDiff = !!display && /^\s*\d+ [+\- ] /.test(display);
|
|
@@ -1421,7 +1616,14 @@ function ToolBlock({ tool, target, output, display, isError, done, expanded, sub
|
|
|
1421
1616
|
sub.length > 6 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: t("\u2026 {n} earlier steps", { n: sub.length - 6 }) }) : null
|
|
1422
1617
|
] }) : null,
|
|
1423
1618
|
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, {
|
|
1619
|
+
isDiff ? /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", marginLeft: 3, children: (() => {
|
|
1620
|
+
const diffLines = display.split("\n");
|
|
1621
|
+
const shownDiff = expanded ? diffLines : diffLines.slice(0, DIFF_PREVIEW_LINES);
|
|
1622
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
1623
|
+
/* @__PURE__ */ jsx15(DiffView, { diff: shownDiff.join("\n") }),
|
|
1624
|
+
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
|
|
1625
|
+
] });
|
|
1626
|
+
})() }) : output && (isError || tool === "bash" || expanded) && !(isTask && !isError) ? /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginLeft: 3, children: [
|
|
1425
1627
|
shown.map((line, i) => /* @__PURE__ */ jsx15(Text15, { dimColor: !isError, color: isError ? color("red") : void 0, children: line }, i)),
|
|
1426
1628
|
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
1629
|
] }) : null
|
|
@@ -1470,7 +1672,10 @@ function applyEvent(t2, event) {
|
|
|
1470
1672
|
case "text": {
|
|
1471
1673
|
const last = t2.live.at(-1);
|
|
1472
1674
|
if (last?.kind === "assistant") return flushLines({ ...t2, live: [...t2.live.slice(0, -1), { ...last, text: last.text + event.delta }] });
|
|
1473
|
-
return flushLines({
|
|
1675
|
+
return flushLines({
|
|
1676
|
+
done: [...t2.done, ...t2.live.filter((e) => !isEmptyCont(e))],
|
|
1677
|
+
live: [entry({ kind: "assistant", text: event.delta })]
|
|
1678
|
+
});
|
|
1474
1679
|
}
|
|
1475
1680
|
case "thinking": {
|
|
1476
1681
|
const last = t2.live.at(-1);
|
|
@@ -1618,54 +1823,56 @@ var INIT_PROMPT = "Study this project (folder structure, package/build files, te
|
|
|
1618
1823
|
var labelFor = (runtime, ref) => `${ref.slice(ref.indexOf("/") + 1)} \xB7 ${runtime.providerLabel()}`;
|
|
1619
1824
|
function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
1620
1825
|
const { exit } = useApp();
|
|
1621
|
-
const [transcript, setTranscript] =
|
|
1826
|
+
const [transcript, setTranscript] = useState10(() => ({
|
|
1622
1827
|
done: [entry({ kind: "header" }), ...updateMessage ? [entry({ kind: "notice", text: updateMessage, tone: "warn" })] : [], ...runtime.agent.messages.length ? replayEntries(runtime.session.loadHistory()) : []],
|
|
1623
1828
|
live: []
|
|
1624
1829
|
}));
|
|
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] =
|
|
1830
|
+
const [busy, setBusy] = useState10(false);
|
|
1831
|
+
const [cancelling, setCancelling] = useState10(false);
|
|
1832
|
+
const [startedAt, setStartedAt] = useState10(0);
|
|
1833
|
+
const [mode, setMode] = useState10(runtime.agent.permissions.mode);
|
|
1834
|
+
const [reasoning, setReasoning] = useState10(runtime.reasoning);
|
|
1835
|
+
const [tokens, setTokens] = useState10(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
|
|
1836
|
+
const [asks, setAsks] = useState10([]);
|
|
1632
1837
|
const pending = asks[0] ?? null;
|
|
1633
|
-
const askId =
|
|
1634
|
-
const [picker, setPicker] =
|
|
1635
|
-
const [providerMenu, setProviderMenu] =
|
|
1636
|
-
const [prompt, setPrompt] =
|
|
1838
|
+
const askId = useRef2(0);
|
|
1839
|
+
const [picker, setPicker] = useState10(null);
|
|
1840
|
+
const [providerMenu, setProviderMenu] = useState10(null);
|
|
1841
|
+
const [prompt, setPrompt] = useState10(null);
|
|
1637
1842
|
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 [,
|
|
1650
|
-
const
|
|
1651
|
-
const
|
|
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;
|
|
1843
|
+
const [modelLabel, setModelLabel] = useState10(() => labelFor(runtime, runtime.modelRef));
|
|
1844
|
+
const [history, setHistory] = useState10([]);
|
|
1845
|
+
const controller = useRef2(null);
|
|
1846
|
+
const [todos, setTodos] = useState10(runtime.todos.items);
|
|
1847
|
+
const [mcpMenu, setMcpMenu] = useState10(null);
|
|
1848
|
+
const [listPicker, setListPicker] = useState10(null);
|
|
1849
|
+
const [planAsk, setPlanAsk] = useState10(null);
|
|
1850
|
+
const [queued, setQueued] = useState10([]);
|
|
1851
|
+
const [showThinking, setShowThinking] = useState10(runtime.config.thinkingVisible === true);
|
|
1852
|
+
const showThinkingRef = useRef2(showThinking);
|
|
1853
|
+
const [showToolOutput, setShowToolOutput] = useState10(false);
|
|
1854
|
+
const [staticKey, setStaticKey] = useState10(0);
|
|
1855
|
+
const [, setConfigRev] = useState10(0);
|
|
1856
|
+
const liveThinkingEntryId = transcript.live.findLast((e) => e.kind === "thinking" && !!e.text.trim())?.id;
|
|
1656
1857
|
const { stdin } = useStdin();
|
|
1657
1858
|
const { stdout } = useStdout2();
|
|
1658
|
-
const
|
|
1659
|
-
|
|
1660
|
-
|
|
1859
|
+
const refreshStatic = useCallback(() => {
|
|
1860
|
+
if (stdout?.isTTY && "fd" in stdout) {
|
|
1861
|
+
stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
1862
|
+
}
|
|
1863
|
+
setStaticKey((k2) => k2 + 1);
|
|
1864
|
+
}, [stdout]);
|
|
1865
|
+
const liveRootRef = useRef2(null);
|
|
1866
|
+
const thinkingTargetRef = useRef2(null);
|
|
1867
|
+
const [injected, setInjected] = useState10();
|
|
1661
1868
|
useEffect3(() => runtime.todos.subscribe(setTodos), [runtime]);
|
|
1662
1869
|
const extraCommands = [
|
|
1663
1870
|
...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
1871
|
...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
1872
|
];
|
|
1666
|
-
const lastTool =
|
|
1667
|
-
const toolTargets =
|
|
1668
|
-
const editedInTurn =
|
|
1873
|
+
const lastTool = useRef2(null);
|
|
1874
|
+
const toolTargets = useRef2(/* @__PURE__ */ new Map());
|
|
1875
|
+
const editedInTurn = useRef2(false);
|
|
1669
1876
|
const notice = useCallback((text, tone = "info") => {
|
|
1670
1877
|
setTranscript((t2) => ({ ...t2, done: [...t2.done, entry({ kind: "notice", text, tone })] }));
|
|
1671
1878
|
}, []);
|
|
@@ -1752,7 +1959,8 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
1752
1959
|
runtime.agent.clear();
|
|
1753
1960
|
setTokens(0);
|
|
1754
1961
|
setShowToolOutput(false);
|
|
1755
|
-
setTranscript(
|
|
1962
|
+
setTranscript({ done: [entry({ kind: "header" })], live: [] });
|
|
1963
|
+
refreshStatic();
|
|
1756
1964
|
notice(t("Conversation cleared."));
|
|
1757
1965
|
return;
|
|
1758
1966
|
case "cost": {
|
|
@@ -1848,7 +2056,8 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
1848
2056
|
runtime.newSession();
|
|
1849
2057
|
setTokens(0);
|
|
1850
2058
|
setShowToolOutput(false);
|
|
1851
|
-
setTranscript(
|
|
2059
|
+
setTranscript({ done: [entry({ kind: "header" })], live: [] });
|
|
2060
|
+
refreshStatic();
|
|
1852
2061
|
notice(t("New session started. The previous session stays saved (/resume)."));
|
|
1853
2062
|
return;
|
|
1854
2063
|
case "session":
|
|
@@ -1880,9 +2089,11 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
1880
2089
|
const chosen = others.find((s) => s.session.file === id);
|
|
1881
2090
|
if (!chosen) return;
|
|
1882
2091
|
runtime.resume(chosen.session);
|
|
2092
|
+
setModelLabel(labelFor(runtime, runtime.modelRef));
|
|
1883
2093
|
setTokens(0);
|
|
1884
2094
|
setShowToolOutput(false);
|
|
1885
2095
|
setTranscript({ done: [entry({ kind: "header" }), ...replayEntries(chosen.session.loadHistory())], live: [] });
|
|
2096
|
+
refreshStatic();
|
|
1886
2097
|
}
|
|
1887
2098
|
});
|
|
1888
2099
|
return;
|
|
@@ -2084,6 +2295,15 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
2084
2295
|
notice(t("Nothing to rewind."));
|
|
2085
2296
|
return;
|
|
2086
2297
|
}
|
|
2298
|
+
setTranscript((tr) => {
|
|
2299
|
+
const userIndices = [];
|
|
2300
|
+
tr.done.forEach((e, idx) => {
|
|
2301
|
+
if (e.kind === "user") userIndices.push(idx);
|
|
2302
|
+
});
|
|
2303
|
+
const cutIdx = userIndices[Math.max(0, userIndices.length - result.turns)];
|
|
2304
|
+
return cutIdx !== void 0 ? { done: tr.done.slice(0, cutIdx), live: [] } : tr;
|
|
2305
|
+
});
|
|
2306
|
+
refreshStatic();
|
|
2087
2307
|
const rel = (p) => relative2(runtime.cwd, p) || p;
|
|
2088
2308
|
notice(
|
|
2089
2309
|
[
|
|
@@ -2146,6 +2366,7 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
2146
2366
|
writeGlobalConfig({ thinkingVisible: next }, runtime.env);
|
|
2147
2367
|
showThinkingRef.current = next;
|
|
2148
2368
|
setShowThinking(next);
|
|
2369
|
+
refreshStatic();
|
|
2149
2370
|
} else if (key === "thinkingClickCollapse") writeGlobalConfig({ thinkingClickCollapse: val === "true" || val === "on" }, runtime.env);
|
|
2150
2371
|
else if (key === "sandbox" && (val === "on" || val === "off")) writeGlobalConfig({ sandbox: val }, runtime.env);
|
|
2151
2372
|
else if (key === "networkPolicy" && (val === "allow" || val === "offline")) writeGlobalConfig({ networkPolicy: val }, runtime.env);
|
|
@@ -2182,7 +2403,7 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
2182
2403
|
notice(t("Unknown command: /{name}. Type /help.", { name }), "warn");
|
|
2183
2404
|
}
|
|
2184
2405
|
},
|
|
2185
|
-
[runtime, notice, exit, version, runTurn, ask]
|
|
2406
|
+
[runtime, notice, exit, version, runTurn, ask, refreshStatic]
|
|
2186
2407
|
);
|
|
2187
2408
|
const runSlash = useCallback(
|
|
2188
2409
|
(name, args) => runSlashCommand(name, args).catch((error) => notice(`/${name}: ${error.message}`, "error")),
|
|
@@ -2198,8 +2419,12 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
2198
2419
|
if (text.startsWith("!") && text.slice(1).trim()) {
|
|
2199
2420
|
const cmd = text.slice(1).trim();
|
|
2200
2421
|
setTranscript((tr) => ({ ...tr, done: [...tr.done, entry({ kind: "user", text })] }));
|
|
2422
|
+
setBusy(true);
|
|
2423
|
+
setStartedAt(Date.now());
|
|
2424
|
+
const ctrl = new AbortController();
|
|
2425
|
+
controller.current = ctrl;
|
|
2201
2426
|
const shell = resolveShell({ platform: process.platform, env: runtime.env, exists: existsSync3 });
|
|
2202
|
-
void runCommand(cmd, { cwd: runtime.cwd, timeoutMs: 6e4, signal:
|
|
2427
|
+
void runCommand(cmd, { cwd: runtime.cwd, timeoutMs: 6e4, signal: ctrl.signal, shell }).then((r) => {
|
|
2203
2428
|
const out = r.output.trim() || "(no output)";
|
|
2204
2429
|
notice(`$ ${cmd}
|
|
2205
2430
|
${out}`, r.exitCode ? "error" : "info");
|
|
@@ -2208,6 +2433,10 @@ $ ${cmd}
|
|
|
2208
2433
|
${out}` };
|
|
2209
2434
|
runtime.agent.messages.push(ctxMsg);
|
|
2210
2435
|
runtime.session.append(ctxMsg);
|
|
2436
|
+
}).finally(() => {
|
|
2437
|
+
controller.current = null;
|
|
2438
|
+
setBusy(false);
|
|
2439
|
+
setCancelling(false);
|
|
2211
2440
|
});
|
|
2212
2441
|
return;
|
|
2213
2442
|
}
|
|
@@ -2248,7 +2477,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2248
2477
|
setQueued(rest);
|
|
2249
2478
|
submit(next);
|
|
2250
2479
|
}, [busy, pending, planAsk, queued, submit]);
|
|
2251
|
-
const initialSent =
|
|
2480
|
+
const initialSent = useRef2(false);
|
|
2252
2481
|
useEffect3(() => {
|
|
2253
2482
|
if (initialPrompt && !initialSent.current) {
|
|
2254
2483
|
initialSent.current = true;
|
|
@@ -2259,15 +2488,31 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2259
2488
|
const next = !showThinkingRef.current;
|
|
2260
2489
|
showThinkingRef.current = next;
|
|
2261
2490
|
setShowThinking(next);
|
|
2491
|
+
refreshStatic();
|
|
2262
2492
|
try {
|
|
2263
2493
|
writeGlobalConfig({ thinkingVisible: next }, runtime.env);
|
|
2264
2494
|
} catch (error) {
|
|
2265
2495
|
notice(t("Could not save thinking visibility: {error}", { error: error.message }), "warn");
|
|
2266
2496
|
}
|
|
2267
|
-
}, [runtime, notice]);
|
|
2497
|
+
}, [runtime, notice, refreshStatic]);
|
|
2498
|
+
useEffect3(() => {
|
|
2499
|
+
if (!stdout?.isTTY || !("fd" in stdout)) return;
|
|
2500
|
+
stdout.write("\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?2004h");
|
|
2501
|
+
return () => {
|
|
2502
|
+
stdout.write("\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?2004l");
|
|
2503
|
+
};
|
|
2504
|
+
}, [stdout]);
|
|
2505
|
+
const scrollableModalOpen = !!(picker || listPicker || mcpMenu || providerMenu || pending || planAsk);
|
|
2506
|
+
useEffect3(() => {
|
|
2507
|
+
if (!scrollableModalOpen || !stdout?.isTTY || !("fd" in stdout)) return;
|
|
2508
|
+
stdout.write("\x1B[?1000h\x1B[?1006h");
|
|
2509
|
+
return () => {
|
|
2510
|
+
stdout.write("\x1B[?1000l\x1B[?1006l");
|
|
2511
|
+
};
|
|
2512
|
+
}, [scrollableModalOpen, stdout]);
|
|
2268
2513
|
useEffect3(() => {
|
|
2269
2514
|
const modalOpen = !!(pending || planAsk || picker || listPicker || mcpMenu || providerMenu || prompt);
|
|
2270
|
-
if (!runtime.config.thinkingClickCollapse || !busy
|
|
2515
|
+
if (!runtime.config.thinkingClickCollapse || !busy || modalOpen || !stdin.isTTY || !stdout.isTTY || !thinkingTargetRef.current) return;
|
|
2271
2516
|
let click;
|
|
2272
2517
|
let mouseBuffer = "";
|
|
2273
2518
|
const onData = (data) => {
|
|
@@ -2293,7 +2538,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2293
2538
|
stdin.off("data", onData);
|
|
2294
2539
|
stdout.write("\x1B[?1000l\x1B[?1006l");
|
|
2295
2540
|
};
|
|
2296
|
-
}, [busy, pending, planAsk, picker, listPicker, mcpMenu, providerMenu, prompt, stdin, stdout, toggleThinking,
|
|
2541
|
+
}, [busy, pending, planAsk, picker, listPicker, mcpMenu, providerMenu, prompt, stdin, stdout, toggleThinking, runtime.config.thinkingClickCollapse]);
|
|
2297
2542
|
useInput9((input, key) => {
|
|
2298
2543
|
if (key.tab && key.shift) {
|
|
2299
2544
|
const next = nextMode(runtime.agent.permissions.mode);
|
|
@@ -2320,6 +2565,9 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2320
2565
|
toggleThinking();
|
|
2321
2566
|
} else if (matchKeybind(runtime.config.keybinds.toolOutput, input, key)) {
|
|
2322
2567
|
setShowToolOutput((prev) => !prev);
|
|
2568
|
+
refreshStatic();
|
|
2569
|
+
} else if (key.ctrl && input.toLowerCase() === "l") {
|
|
2570
|
+
refreshStatic();
|
|
2323
2571
|
}
|
|
2324
2572
|
});
|
|
2325
2573
|
const pickMcp = async (name) => {
|
|
@@ -2384,8 +2632,23 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2384
2632
|
pending?.resolve(a);
|
|
2385
2633
|
setAsks((q) => q.slice(1));
|
|
2386
2634
|
};
|
|
2635
|
+
const isRealTty = Boolean(stdout?.isTTY && "fd" in stdout);
|
|
2636
|
+
const maxLiveLines = Math.max(8, (stdout?.rows ?? 24) - 10);
|
|
2637
|
+
const capLiveMarkdown = (raw) => {
|
|
2638
|
+
if (!isRealTty) return raw;
|
|
2639
|
+
const lines = raw.split("\n");
|
|
2640
|
+
if (lines.length <= maxLiveLines) return raw;
|
|
2641
|
+
const hidden = lines.length - maxLiveLines;
|
|
2642
|
+
const headFence = lines.slice(0, hidden).find((l) => l.trimStart().startsWith("```"));
|
|
2643
|
+
const prefix = headFence ? `${headFence}
|
|
2644
|
+
\u2026 (${hidden} lines above) \u2026
|
|
2645
|
+
` : `\u2026 (${hidden} lines above) \u2026
|
|
2646
|
+
`;
|
|
2647
|
+
return `${prefix}${lines.slice(-maxLiveLines).join("\n")}`;
|
|
2648
|
+
};
|
|
2387
2649
|
const cwd = runtime.cwd.startsWith(homedir()) ? `~${runtime.cwd.slice(homedir().length)}` : runtime.cwd;
|
|
2388
2650
|
const renderEntry = (e) => {
|
|
2651
|
+
const isLive = transcript.live.some((le) => le.id === e.id);
|
|
2389
2652
|
switch (e.kind) {
|
|
2390
2653
|
case "header":
|
|
2391
2654
|
return /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text16, { children: [
|
|
@@ -2395,20 +2658,25 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2395
2658
|
] }) }, e.id);
|
|
2396
2659
|
case "user":
|
|
2397
2660
|
return /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `> ${e.text}` }) }, e.id);
|
|
2398
|
-
case "assistant":
|
|
2661
|
+
case "assistant": {
|
|
2662
|
+
const displayText = isLive ? capLiveMarkdown(e.text) : e.text;
|
|
2399
2663
|
if (e.cont) {
|
|
2400
|
-
return
|
|
2664
|
+
return displayText ? /* @__PURE__ */ jsx16(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Markdown, { text: displayText }) }, e.id) : null;
|
|
2401
2665
|
}
|
|
2402
|
-
if (!
|
|
2666
|
+
if (!displayText.trim()) return null;
|
|
2403
2667
|
return /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, children: [
|
|
2404
2668
|
/* @__PURE__ */ jsx16(Text16, { color: color(ACCENT), children: "\u25CF " }),
|
|
2405
|
-
/* @__PURE__ */ jsx16(Markdown, { text:
|
|
2669
|
+
/* @__PURE__ */ jsx16(Markdown, { text: displayText.replace(/^\s+/, "") })
|
|
2406
2670
|
] }, e.id);
|
|
2671
|
+
}
|
|
2407
2672
|
case "thinking": {
|
|
2408
2673
|
const text = e.text.trim();
|
|
2409
2674
|
if (!text) return null;
|
|
2410
|
-
const
|
|
2411
|
-
const
|
|
2675
|
+
const allLines = text.split("\n");
|
|
2676
|
+
const lines = allLines.length;
|
|
2677
|
+
const shownText = isLive && isRealTty && allLines.length > maxLiveLines ? `\u2026 (${allLines.length - maxLiveLines} lines above) \u2026
|
|
2678
|
+
${allLines.slice(-maxLiveLines).join("\n")}` : text;
|
|
2679
|
+
const isTarget = e.id === liveThinkingEntryId;
|
|
2412
2680
|
return /* @__PURE__ */ jsxs12(Box13, { ref: isTarget ? thinkingTargetRef : void 0, marginTop: 1, flexDirection: "column", children: [
|
|
2413
2681
|
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
|
|
2414
2682
|
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: showThinking ? t("\u273B Thinking") : t("\u273B Thinking \xB7 {n} lines", { n: lines }) }),
|
|
@@ -2420,7 +2688,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2420
2688
|
] }),
|
|
2421
2689
|
showThinking ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
|
|
2422
2690
|
/* @__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:
|
|
2691
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, italic: true, children: shownText })
|
|
2424
2692
|
] }) : null
|
|
2425
2693
|
] }, e.id);
|
|
2426
2694
|
}
|
|
@@ -2436,10 +2704,9 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2436
2704
|
const activeSpinnerSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 20 ? `${activeTarget.slice(0, 20)}\u2026` : activeTarget}` : ""}` : "";
|
|
2437
2705
|
const activeToolSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 68 ? `${activeTarget.slice(0, 68)}\u2026` : activeTarget}` : ""}` : "";
|
|
2438
2706
|
return /* @__PURE__ */ jsxs12(Box13, { ref: liveRootRef, flexDirection: "column", children: [
|
|
2439
|
-
/* @__PURE__ */ jsx16(Static, { items:
|
|
2440
|
-
activeTurnDone.map(renderEntry),
|
|
2707
|
+
/* @__PURE__ */ jsx16(Static, { items: transcript.done, children: renderEntry }, staticKey),
|
|
2441
2708
|
transcript.live.map(renderEntry),
|
|
2442
|
-
busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(Box13, { ref:
|
|
2709
|
+
busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(Box13, { ref: liveThinkingEntryId === void 0 ? thinkingTargetRef : void 0, marginTop: 1, flexDirection: "column", children: [
|
|
2443
2710
|
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
|
|
2444
2711
|
/* @__PURE__ */ jsx16(Spinner, { label: cancelling ? t("Cancelling") : activeSpinnerSummary ? `${t("Thinking")} \xB7 ${activeSpinnerSummary}` : t("Thinking"), startedAt }),
|
|
2445
2712
|
/* @__PURE__ */ jsxs12(Text16, { dimColor: true, children: [
|
|
@@ -2448,7 +2715,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2448
2715
|
runtime.config.thinkingClickCollapse ? ` \xB7 ${t("click to toggle")}` : ""
|
|
2449
2716
|
] })
|
|
2450
2717
|
] }),
|
|
2451
|
-
showThinking &&
|
|
2718
|
+
showThinking && liveThinkingEntryId === void 0 ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
|
|
2452
2719
|
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("Model: {model} \xB7 reasoning: {level}", { model: runtime.modelRef, level: reasoning }) }),
|
|
2453
2720
|
activeToolSummary ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `\u25B6 ${activeToolSummary}` }) : null,
|
|
2454
2721
|
!activeToolSummary ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("No reasoning summary received yet; tool activity remains visible above.") }) : null
|
|
@@ -2506,7 +2773,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2506
2773
|
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2507
2774
|
async function startInteractive(rt, opts) {
|
|
2508
2775
|
if (opts.resume) {
|
|
2509
|
-
const { Session: Session2 } = await import("./session-
|
|
2776
|
+
const { Session: Session2 } = await import("./session-NPKTEDKV.js");
|
|
2510
2777
|
const list = Session2.list(rt.home, rt.cwd).slice(0, 20);
|
|
2511
2778
|
if (!list.length) console.log(t("No sessions in this folder yet; starting a new one."));
|
|
2512
2779
|
else {
|