@nomad-e/bluma-cli 0.0.103 → 0.0.104
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/config/bluma-mcp.json +2 -1
- package/dist/config/native_tools.json +299 -0
- package/dist/main.js +2861 -789
- package/package.json +3 -18
package/dist/main.js
CHANGED
|
@@ -159,43 +159,85 @@ var init_tool_call_normalizer = __esm({
|
|
|
159
159
|
});
|
|
160
160
|
|
|
161
161
|
// src/main.ts
|
|
162
|
-
import
|
|
162
|
+
import React10 from "react";
|
|
163
163
|
import { render } from "ink";
|
|
164
164
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
165
|
-
import { v4 as
|
|
165
|
+
import { v4 as uuidv43 } from "uuid";
|
|
166
166
|
|
|
167
167
|
// src/app/ui/App.tsx
|
|
168
|
-
import { useState as
|
|
169
|
-
import { Box as
|
|
168
|
+
import { useState as useState6, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback2, memo as memo10 } from "react";
|
|
169
|
+
import { Box as Box16, Text as Text15, Static } from "ink";
|
|
170
170
|
|
|
171
171
|
// src/app/ui/layout.tsx
|
|
172
172
|
import { Box, Text } from "ink";
|
|
173
|
+
import { memo } from "react";
|
|
173
174
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
174
|
-
var
|
|
175
|
+
var VERSION = "0.0.103";
|
|
176
|
+
var HeaderComponent = ({
|
|
175
177
|
sessionId: sessionId2,
|
|
176
178
|
workdir
|
|
177
179
|
}) => {
|
|
180
|
+
const dirName = workdir.split("/").pop() || workdir;
|
|
178
181
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
179
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
180
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
185
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "workspace " }),
|
|
186
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: workdir })
|
|
187
|
-
] }),
|
|
188
|
-
/* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
|
|
189
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "session " }),
|
|
190
|
-
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
191
|
-
sessionId2.slice(0, 8),
|
|
192
|
-
"..."
|
|
193
|
-
] })
|
|
182
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
183
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "bluma" }),
|
|
184
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
185
|
+
" ",
|
|
186
|
+
VERSION
|
|
194
187
|
] })
|
|
195
188
|
] }),
|
|
196
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
190
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "cwd " }),
|
|
191
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: dirName }),
|
|
192
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " | " }),
|
|
193
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "session " }),
|
|
194
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: sessionId2.slice(0, 8) })
|
|
195
|
+
] })
|
|
196
|
+
] });
|
|
197
|
+
};
|
|
198
|
+
var Header = memo(HeaderComponent);
|
|
199
|
+
var SessionInfoComponent = ({
|
|
200
|
+
sessionId: sessionId2,
|
|
201
|
+
workdir,
|
|
202
|
+
toolsCount,
|
|
203
|
+
mcpStatus
|
|
204
|
+
}) => /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
|
|
205
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "mcp " }),
|
|
206
|
+
/* @__PURE__ */ jsx(Text, { color: mcpStatus === "connected" ? "green" : "yellow", children: mcpStatus === "connected" ? "+" : "-" }),
|
|
207
|
+
toolsCount !== null && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
208
|
+
" ",
|
|
209
|
+
toolsCount,
|
|
210
|
+
" tools"
|
|
211
|
+
] }) })
|
|
212
|
+
] });
|
|
213
|
+
var SessionInfo = memo(SessionInfoComponent);
|
|
214
|
+
var TaskStatusBarComponent = ({
|
|
215
|
+
taskName,
|
|
216
|
+
mode,
|
|
217
|
+
status
|
|
218
|
+
}) => {
|
|
219
|
+
const modeColors = {
|
|
220
|
+
PLANNING: "blue",
|
|
221
|
+
EXECUTION: "green",
|
|
222
|
+
VERIFICATION: "yellow"
|
|
223
|
+
};
|
|
224
|
+
return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
|
|
225
|
+
/* @__PURE__ */ jsxs(Text, { color: modeColors[mode], bold: true, children: [
|
|
226
|
+
"[",
|
|
227
|
+
mode.charAt(0),
|
|
228
|
+
"]"
|
|
229
|
+
] }),
|
|
230
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
231
|
+
" ",
|
|
232
|
+
taskName
|
|
233
|
+
] }),
|
|
234
|
+
status && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
235
|
+
" - ",
|
|
236
|
+
status
|
|
237
|
+
] })
|
|
197
238
|
] });
|
|
198
239
|
};
|
|
240
|
+
var TaskStatusBar = memo(TaskStatusBarComponent);
|
|
199
241
|
|
|
200
242
|
// src/app/ui/components/InputPrompt.tsx
|
|
201
243
|
import { Box as Box2, Text as Text2, useStdout, useInput as useInput2 } from "ink";
|
|
@@ -479,7 +521,7 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
|
|
|
479
521
|
};
|
|
480
522
|
|
|
481
523
|
// src/app/ui/components/InputPrompt.tsx
|
|
482
|
-
import { useEffect as useEffect3, useMemo, useState as useState2, memo } from "react";
|
|
524
|
+
import { useEffect as useEffect3, useMemo, useState as useState2, memo as memo2 } from "react";
|
|
483
525
|
import { EventEmitter } from "events";
|
|
484
526
|
|
|
485
527
|
// src/app/ui/utils/slashRegistry.ts
|
|
@@ -683,7 +725,7 @@ function useAtCompletion({
|
|
|
683
725
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
684
726
|
var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter();
|
|
685
727
|
global.__bluma_ui_eventbus__ = uiEventBus;
|
|
686
|
-
var TextLine =
|
|
728
|
+
var TextLine = memo2(({
|
|
687
729
|
line,
|
|
688
730
|
lineIndex,
|
|
689
731
|
cursorLine,
|
|
@@ -712,7 +754,7 @@ var TextLine = memo(({
|
|
|
712
754
|
return true;
|
|
713
755
|
});
|
|
714
756
|
TextLine.displayName = "TextLine";
|
|
715
|
-
var PathSuggestions =
|
|
757
|
+
var PathSuggestions = memo2(({
|
|
716
758
|
suggestions,
|
|
717
759
|
selected
|
|
718
760
|
}) => {
|
|
@@ -732,7 +774,7 @@ var PathSuggestions = memo(({
|
|
|
732
774
|
}) });
|
|
733
775
|
});
|
|
734
776
|
PathSuggestions.displayName = "PathSuggestions";
|
|
735
|
-
var SlashSuggestions =
|
|
777
|
+
var SlashSuggestions = memo2(({
|
|
736
778
|
suggestions,
|
|
737
779
|
selectedIndex
|
|
738
780
|
}) => /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: suggestions.map((s, idx) => {
|
|
@@ -750,9 +792,9 @@ var SlashSuggestions = memo(({
|
|
|
750
792
|
] }, s.name);
|
|
751
793
|
}) }));
|
|
752
794
|
SlashSuggestions.displayName = "SlashSuggestions";
|
|
753
|
-
var Footer =
|
|
795
|
+
var Footer = memo2(({ isReadOnly }) => /* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: "gray", dimColor: true, children: isReadOnly ? "ctrl+c to exit | Enter to send message | Shift+Enter for new line | esc interrupt" : "ctrl+c to exit | Enter to submit | Shift+Enter for new line | /help commands | esc interrupt" }) }));
|
|
754
796
|
Footer.displayName = "Footer";
|
|
755
|
-
var TextLinesRenderer =
|
|
797
|
+
var TextLinesRenderer = memo2(({
|
|
756
798
|
lines,
|
|
757
799
|
cursorLine,
|
|
758
800
|
cursorCol,
|
|
@@ -785,7 +827,7 @@ var TextLinesRenderer = memo(({
|
|
|
785
827
|
}) });
|
|
786
828
|
});
|
|
787
829
|
TextLinesRenderer.displayName = "TextLinesRenderer";
|
|
788
|
-
var InputPrompt =
|
|
830
|
+
var InputPrompt = memo2(({
|
|
789
831
|
onSubmit,
|
|
790
832
|
isReadOnly,
|
|
791
833
|
onInterrupt,
|
|
@@ -951,77 +993,55 @@ var InputPrompt = memo(({
|
|
|
951
993
|
InputPrompt.displayName = "InputPrompt";
|
|
952
994
|
|
|
953
995
|
// src/app/ui/ConfirmationPrompt.tsx
|
|
996
|
+
import { memo as memo4 } from "react";
|
|
954
997
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
955
998
|
|
|
956
999
|
// src/app/ui/InteractiveMenu.tsx
|
|
957
|
-
import { useState as useState3, memo as
|
|
1000
|
+
import { useState as useState3, memo as memo3 } from "react";
|
|
958
1001
|
import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
|
|
959
1002
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
960
1003
|
var InteractiveMenuComponent = ({ onDecision }) => {
|
|
961
1004
|
const options = [
|
|
962
|
-
{
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
value: "accept",
|
|
966
|
-
color: "green"
|
|
967
|
-
},
|
|
968
|
-
{
|
|
969
|
-
key: "n",
|
|
970
|
-
label: "Decline",
|
|
971
|
-
value: "decline",
|
|
972
|
-
color: "red"
|
|
973
|
-
},
|
|
974
|
-
{
|
|
975
|
-
key: "a",
|
|
976
|
-
label: "Always Accept",
|
|
977
|
-
value: "accept_always",
|
|
978
|
-
color: "yellow"
|
|
979
|
-
}
|
|
1005
|
+
{ key: "y", label: "yes", value: "accept", color: "green" },
|
|
1006
|
+
{ key: "n", label: "no", value: "decline", color: "red" },
|
|
1007
|
+
{ key: "a", label: "always", value: "accept_always", color: "yellow" }
|
|
980
1008
|
];
|
|
981
|
-
const [
|
|
1009
|
+
const [selected, setSelected] = useState3(0);
|
|
982
1010
|
useInput3((input, key) => {
|
|
983
|
-
if (key.upArrow) {
|
|
984
|
-
|
|
1011
|
+
if (key.upArrow || key.leftArrow) {
|
|
1012
|
+
setSelected((i) => i > 0 ? i - 1 : options.length - 1);
|
|
985
1013
|
}
|
|
986
|
-
if (key.downArrow) {
|
|
987
|
-
|
|
1014
|
+
if (key.downArrow || key.rightArrow) {
|
|
1015
|
+
setSelected((i) => i < options.length - 1 ? i + 1 : 0);
|
|
988
1016
|
}
|
|
989
1017
|
if (key.escape) {
|
|
990
1018
|
onDecision("decline");
|
|
991
1019
|
}
|
|
992
1020
|
if (key.return) {
|
|
993
|
-
onDecision(options[
|
|
1021
|
+
onDecision(options[selected].value);
|
|
994
1022
|
}
|
|
995
|
-
const
|
|
996
|
-
if (
|
|
997
|
-
onDecision(
|
|
1023
|
+
const opt = options.find((o) => o.key === input.toLowerCase());
|
|
1024
|
+
if (opt) {
|
|
1025
|
+
onDecision(opt.value);
|
|
998
1026
|
}
|
|
999
1027
|
});
|
|
1000
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
|
|
1001
|
-
/* @__PURE__ */
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
/* @__PURE__ */ jsx3(Text3, { color: isSelected ?
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
"] ",
|
|
1015
|
-
option.label
|
|
1016
|
-
]
|
|
1017
|
-
}
|
|
1018
|
-
)
|
|
1019
|
-
] }, option.value);
|
|
1020
|
-
}) }),
|
|
1021
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, paddingLeft: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2191\u2193 to select \u2022 Enter to confirm \u2022 Esc to cancel" }) })
|
|
1028
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
1029
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1030
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "approve? " }),
|
|
1031
|
+
options.map((opt, idx) => {
|
|
1032
|
+
const isSelected = idx === selected;
|
|
1033
|
+
return /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: /* @__PURE__ */ jsxs3(Text3, { color: isSelected ? opt.color : "gray", bold: isSelected, children: [
|
|
1034
|
+
"[",
|
|
1035
|
+
opt.key,
|
|
1036
|
+
"]",
|
|
1037
|
+
opt.label
|
|
1038
|
+
] }) }, opt.value);
|
|
1039
|
+
})
|
|
1040
|
+
] }),
|
|
1041
|
+
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "arrows to select, enter to confirm, esc to cancel" }) })
|
|
1022
1042
|
] });
|
|
1023
1043
|
};
|
|
1024
|
-
var InteractiveMenu =
|
|
1044
|
+
var InteractiveMenu = memo3(InteractiveMenuComponent);
|
|
1025
1045
|
|
|
1026
1046
|
// src/app/ui/components/promptRenderers.tsx
|
|
1027
1047
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
@@ -1171,7 +1191,7 @@ var renderEditTool = ({ toolCall, preview }) => {
|
|
|
1171
1191
|
var renderGeneric = ({ toolCall }) => {
|
|
1172
1192
|
const toolName = toolCall.function.name;
|
|
1173
1193
|
const rawArguments = toolCall.function.arguments;
|
|
1174
|
-
const
|
|
1194
|
+
const MAX_LINES2 = 5;
|
|
1175
1195
|
let formattedArgsString;
|
|
1176
1196
|
if (!rawArguments) {
|
|
1177
1197
|
formattedArgsString = "";
|
|
@@ -1186,9 +1206,9 @@ var renderGeneric = ({ toolCall }) => {
|
|
|
1186
1206
|
formattedArgsString = JSON.stringify(rawArguments, null, 2);
|
|
1187
1207
|
}
|
|
1188
1208
|
const lines = formattedArgsString.split("\n");
|
|
1189
|
-
const isTruncated = lines.length >
|
|
1190
|
-
const visibleLines = isTruncated ? lines.slice(0,
|
|
1191
|
-
const remainingCount = lines.length -
|
|
1209
|
+
const isTruncated = lines.length > MAX_LINES2;
|
|
1210
|
+
const visibleLines = isTruncated ? lines.slice(0, MAX_LINES2) : lines;
|
|
1211
|
+
const remainingCount = lines.length - MAX_LINES2;
|
|
1192
1212
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
1193
1213
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1194
1214
|
/* @__PURE__ */ jsx5(Text5, { color: "blue", children: "\u25B8" }),
|
|
@@ -1285,45 +1305,64 @@ var promptRenderers = {
|
|
|
1285
1305
|
|
|
1286
1306
|
// src/app/ui/ConfirmationPrompt.tsx
|
|
1287
1307
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1288
|
-
var
|
|
1308
|
+
var ConfirmationPromptComponent = ({ toolCalls, preview, onDecision }) => {
|
|
1289
1309
|
const toolCall = toolCalls && toolCalls.length > 0 ? toolCalls[0] : null;
|
|
1290
1310
|
if (!toolCall) {
|
|
1291
|
-
return /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, {
|
|
1311
|
+
return /* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "waiting..." }) });
|
|
1292
1312
|
}
|
|
1293
1313
|
const toolName = toolCall.function.name;
|
|
1294
1314
|
const renderFunction = promptRenderers[toolName] || renderGeneric;
|
|
1295
|
-
return (
|
|
1296
|
-
|
|
1297
|
-
/* @__PURE__ */
|
|
1298
|
-
|
|
1299
|
-
{
|
|
1300
|
-
borderStyle: "round",
|
|
1301
|
-
borderColor: "gray",
|
|
1302
|
-
flexDirection: "column",
|
|
1303
|
-
paddingX: 1,
|
|
1304
|
-
children: [
|
|
1305
|
-
renderFunction({ toolCall, preview }),
|
|
1306
|
-
/* @__PURE__ */ jsx6(InteractiveMenu, { onDecision })
|
|
1307
|
-
]
|
|
1308
|
-
}
|
|
1309
|
-
)
|
|
1310
|
-
);
|
|
1315
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
|
|
1316
|
+
renderFunction({ toolCall, preview }),
|
|
1317
|
+
/* @__PURE__ */ jsx6(InteractiveMenu, { onDecision })
|
|
1318
|
+
] });
|
|
1311
1319
|
};
|
|
1320
|
+
var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
1312
1321
|
|
|
1313
1322
|
// src/app/agent/agent.ts
|
|
1314
1323
|
import OpenAI from "openai";
|
|
1315
1324
|
import * as dotenv from "dotenv";
|
|
1316
|
-
import
|
|
1317
|
-
import
|
|
1325
|
+
import path15 from "path";
|
|
1326
|
+
import os9 from "os";
|
|
1318
1327
|
|
|
1319
1328
|
// src/app/agent/tool_invoker.ts
|
|
1320
|
-
import { promises as
|
|
1321
|
-
import
|
|
1329
|
+
import { promises as fs8 } from "fs";
|
|
1330
|
+
import path10 from "path";
|
|
1322
1331
|
import { fileURLToPath } from "url";
|
|
1323
1332
|
|
|
1324
1333
|
// src/app/agent/tools/natives/shell_command.ts
|
|
1325
1334
|
import os from "os";
|
|
1326
1335
|
import { spawn } from "child_process";
|
|
1336
|
+
var MAX_OUTPUT_SIZE = 1e5;
|
|
1337
|
+
var OUTPUT_TRUNCATION_MESSAGE = "\n\n[OUTPUT TRUNCATED - exceeded 100KB limit. Use pagination or redirect to file for full output.]";
|
|
1338
|
+
var DANGEROUS_PATTERNS = [
|
|
1339
|
+
/^sudo\s+/i,
|
|
1340
|
+
// sudo commands
|
|
1341
|
+
/^doas\s+/i,
|
|
1342
|
+
// doas (BSD sudo alternative)
|
|
1343
|
+
/^su\s+/i,
|
|
1344
|
+
// switch user
|
|
1345
|
+
/^pkexec\s+/i,
|
|
1346
|
+
// PolicyKit execution
|
|
1347
|
+
/\|\s*sudo\s+/i,
|
|
1348
|
+
// piped to sudo
|
|
1349
|
+
/;\s*sudo\s+/i,
|
|
1350
|
+
// chained with sudo
|
|
1351
|
+
/&&\s*sudo\s+/i
|
|
1352
|
+
// AND chained with sudo
|
|
1353
|
+
];
|
|
1354
|
+
var INTERACTIVE_PATTERNS = [
|
|
1355
|
+
/^(vim|vi|nano|emacs|less|more)\s*/i,
|
|
1356
|
+
// editors/pagers
|
|
1357
|
+
/^(top|htop|btop)\s*/i,
|
|
1358
|
+
// monitoring tools
|
|
1359
|
+
/^(ssh|telnet|ftp)\s+/i,
|
|
1360
|
+
// remote connections
|
|
1361
|
+
/^(mysql|psql|redis-cli|mongo)\s*/i,
|
|
1362
|
+
// database CLIs
|
|
1363
|
+
/^(python|node|ruby|irb)\s*$/i
|
|
1364
|
+
// REPLs without script
|
|
1365
|
+
];
|
|
1327
1366
|
function shellCommand(args) {
|
|
1328
1367
|
const {
|
|
1329
1368
|
command,
|
|
@@ -1335,6 +1374,45 @@ function shellCommand(args) {
|
|
|
1335
1374
|
return new Promise((resolve) => {
|
|
1336
1375
|
const startTime = Date.now();
|
|
1337
1376
|
const platform = os.platform();
|
|
1377
|
+
const commandTrimmed = command.trim();
|
|
1378
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
1379
|
+
if (pattern.test(commandTrimmed)) {
|
|
1380
|
+
const result = {
|
|
1381
|
+
status: "blocked",
|
|
1382
|
+
exitCode: null,
|
|
1383
|
+
stdout: "",
|
|
1384
|
+
stderr: `Command blocked: "${commandTrimmed}" requires elevated privileges (sudo/doas). These commands require interactive password input which cannot be handled. Alternatives:
|
|
1385
|
+
1. Run the command manually in terminal
|
|
1386
|
+
2. Configure passwordless sudo for this specific command
|
|
1387
|
+
3. Run BluMa as root (not recommended)`,
|
|
1388
|
+
command,
|
|
1389
|
+
cwd,
|
|
1390
|
+
platform,
|
|
1391
|
+
duration: Date.now() - startTime,
|
|
1392
|
+
blockedReason: "requires_sudo"
|
|
1393
|
+
};
|
|
1394
|
+
return resolve(formatResult(result, verbose));
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
1398
|
+
if (pattern.test(commandTrimmed)) {
|
|
1399
|
+
const result = {
|
|
1400
|
+
status: "blocked",
|
|
1401
|
+
exitCode: null,
|
|
1402
|
+
stdout: "",
|
|
1403
|
+
stderr: `Command blocked: "${commandTrimmed}" is an interactive command. Interactive commands require user input and cannot be handled by the agent. Alternatives:
|
|
1404
|
+
1. Use non-interactive alternatives (e.g., 'cat' instead of 'less')
|
|
1405
|
+
2. Run the command manually in terminal
|
|
1406
|
+
3. For editors, use edit_tool instead`,
|
|
1407
|
+
command,
|
|
1408
|
+
cwd,
|
|
1409
|
+
platform,
|
|
1410
|
+
duration: Date.now() - startTime,
|
|
1411
|
+
blockedReason: "interactive_command"
|
|
1412
|
+
};
|
|
1413
|
+
return resolve(formatResult(result, verbose));
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1338
1416
|
let shellCmd;
|
|
1339
1417
|
let shellArgs;
|
|
1340
1418
|
if (platform === "win32") {
|
|
@@ -1346,6 +1424,8 @@ function shellCommand(args) {
|
|
|
1346
1424
|
}
|
|
1347
1425
|
let stdout = "";
|
|
1348
1426
|
let stderr = "";
|
|
1427
|
+
let stdoutTruncated = false;
|
|
1428
|
+
let stderrTruncated = false;
|
|
1349
1429
|
let timedOut = false;
|
|
1350
1430
|
let finished = false;
|
|
1351
1431
|
const childProcess = spawn(shellCmd, shellArgs, {
|
|
@@ -1367,12 +1447,24 @@ function shellCommand(args) {
|
|
|
1367
1447
|
}, timeout * 1e3);
|
|
1368
1448
|
if (childProcess.stdout) {
|
|
1369
1449
|
childProcess.stdout.on("data", (data) => {
|
|
1370
|
-
|
|
1450
|
+
if (!stdoutTruncated) {
|
|
1451
|
+
stdout += data.toString();
|
|
1452
|
+
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
1453
|
+
stdout = stdout.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1454
|
+
stdoutTruncated = true;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1371
1457
|
});
|
|
1372
1458
|
}
|
|
1373
1459
|
if (childProcess.stderr) {
|
|
1374
1460
|
childProcess.stderr.on("data", (data) => {
|
|
1375
|
-
|
|
1461
|
+
if (!stderrTruncated) {
|
|
1462
|
+
stderr += data.toString();
|
|
1463
|
+
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
1464
|
+
stderr = stderr.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1465
|
+
stderrTruncated = true;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1376
1468
|
});
|
|
1377
1469
|
}
|
|
1378
1470
|
childProcess.on("error", (error) => {
|
|
@@ -1405,7 +1497,8 @@ ${stderr.trim()}` : stderr.trim(),
|
|
|
1405
1497
|
command,
|
|
1406
1498
|
cwd,
|
|
1407
1499
|
platform,
|
|
1408
|
-
duration: Date.now() - startTime
|
|
1500
|
+
duration: Date.now() - startTime,
|
|
1501
|
+
truncated: stdoutTruncated || stderrTruncated
|
|
1409
1502
|
};
|
|
1410
1503
|
resolve(formatResult(result, verbose));
|
|
1411
1504
|
}
|
|
@@ -1429,6 +1522,12 @@ function formatResult(result, verbose) {
|
|
|
1429
1522
|
if (result.status === "timeout") {
|
|
1430
1523
|
output.message = `Command exceeded timeout of ${result.duration / 1e3}s`;
|
|
1431
1524
|
}
|
|
1525
|
+
if (result.status === "blocked" && result.blockedReason) {
|
|
1526
|
+
output.blockedReason = result.blockedReason;
|
|
1527
|
+
}
|
|
1528
|
+
if (result.truncated) {
|
|
1529
|
+
output.warning = "Output was truncated due to size limits (100KB max per stream)";
|
|
1530
|
+
}
|
|
1432
1531
|
return JSON.stringify(output, null, 2);
|
|
1433
1532
|
}
|
|
1434
1533
|
|
|
@@ -1464,12 +1563,19 @@ function unescapeLlmString(inputString) {
|
|
|
1464
1563
|
return inputString;
|
|
1465
1564
|
}
|
|
1466
1565
|
}
|
|
1467
|
-
function
|
|
1468
|
-
if (search === "") return text;
|
|
1469
|
-
|
|
1470
|
-
|
|
1566
|
+
function replaceNOccurrences(text, search, replacement, count) {
|
|
1567
|
+
if (search === "" || count <= 0) return text;
|
|
1568
|
+
let result = text;
|
|
1569
|
+
let replaced = 0;
|
|
1570
|
+
let lastIndex = 0;
|
|
1571
|
+
while (replaced < count) {
|
|
1572
|
+
const index = result.indexOf(search, lastIndex);
|
|
1573
|
+
if (index === -1) break;
|
|
1574
|
+
result = result.substring(0, index) + replacement + result.substring(index + search.length);
|
|
1575
|
+
lastIndex = index + replacement.length;
|
|
1576
|
+
replaced++;
|
|
1471
1577
|
}
|
|
1472
|
-
return
|
|
1578
|
+
return result;
|
|
1473
1579
|
}
|
|
1474
1580
|
function countOccurrences(text, search) {
|
|
1475
1581
|
if (search === "" || text === "") return 0;
|
|
@@ -1483,10 +1589,63 @@ function ensureCorrectEdit(currentContent, originalOldString, originalNewString,
|
|
|
1483
1589
|
return [finalOldString, finalNewString, occurrences];
|
|
1484
1590
|
}
|
|
1485
1591
|
const candidates = [
|
|
1486
|
-
{
|
|
1487
|
-
|
|
1488
|
-
|
|
1592
|
+
{
|
|
1593
|
+
old: unescapeLlmString(originalOldString),
|
|
1594
|
+
new: unescapeLlmString(originalNewString),
|
|
1595
|
+
strategy: "unescape_llm"
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
old: originalOldString.trim(),
|
|
1599
|
+
new: originalNewString.trim(),
|
|
1600
|
+
strategy: "trim"
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
old: unescapeLlmString(originalOldString).trim(),
|
|
1604
|
+
new: unescapeLlmString(originalNewString).trim(),
|
|
1605
|
+
strategy: "unescape_and_trim"
|
|
1606
|
+
}
|
|
1489
1607
|
];
|
|
1608
|
+
const oldWithoutTrailing = originalOldString.split("\n").map((l) => l.trimEnd()).join("\n");
|
|
1609
|
+
const newWithoutTrailing = originalNewString.split("\n").map((l) => l.trimEnd()).join("\n");
|
|
1610
|
+
if (oldWithoutTrailing !== originalOldString) {
|
|
1611
|
+
candidates.push({
|
|
1612
|
+
old: oldWithoutTrailing,
|
|
1613
|
+
new: newWithoutTrailing,
|
|
1614
|
+
strategy: "trim_trailing"
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
const oldTabsToSpaces = originalOldString.replace(/\t/g, " ");
|
|
1618
|
+
const oldSpacesToTabs = originalOldString.replace(/ /g, " ");
|
|
1619
|
+
if (oldTabsToSpaces !== originalOldString) {
|
|
1620
|
+
candidates.push({
|
|
1621
|
+
old: oldTabsToSpaces,
|
|
1622
|
+
new: originalNewString.replace(/\t/g, " "),
|
|
1623
|
+
strategy: "tabs_to_spaces"
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
if (oldSpacesToTabs !== originalOldString) {
|
|
1627
|
+
candidates.push({
|
|
1628
|
+
old: oldSpacesToTabs,
|
|
1629
|
+
new: originalNewString.replace(/ /g, " "),
|
|
1630
|
+
strategy: "spaces_to_tabs"
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
const old2to4 = originalOldString.replace(/^( {2})/gm, " ");
|
|
1634
|
+
const old4to2 = originalOldString.replace(/^( {4})/gm, " ");
|
|
1635
|
+
if (old2to4 !== originalOldString) {
|
|
1636
|
+
candidates.push({
|
|
1637
|
+
old: old2to4,
|
|
1638
|
+
new: originalNewString.replace(/^( {2})/gm, " "),
|
|
1639
|
+
strategy: "indent_2_to_4"
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
if (old4to2 !== originalOldString) {
|
|
1643
|
+
candidates.push({
|
|
1644
|
+
old: old4to2,
|
|
1645
|
+
new: originalNewString.replace(/^( {4})/gm, " "),
|
|
1646
|
+
strategy: "indent_4_to_2"
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1490
1649
|
for (const candidate of candidates) {
|
|
1491
1650
|
if (candidate.old === originalOldString) continue;
|
|
1492
1651
|
const candidateOccurrences = countOccurrences(currentContent, candidate.old);
|
|
@@ -1585,7 +1744,7 @@ ${contentPreview}${currentContent.length > 500 ? "..." : ""}`,
|
|
|
1585
1744
|
if (isNewFile) {
|
|
1586
1745
|
newContentResult = normalizedNewString;
|
|
1587
1746
|
} else if (currentContent !== null) {
|
|
1588
|
-
newContentResult =
|
|
1747
|
+
newContentResult = replaceNOccurrences(currentContent, normalizedOldString, normalizedNewString, expectedReplacements);
|
|
1589
1748
|
}
|
|
1590
1749
|
}
|
|
1591
1750
|
return { currentContent, newContent: newContentResult, occurrences, error, isNewFile };
|
|
@@ -1652,10 +1811,11 @@ async function editTool(args) {
|
|
|
1652
1811
|
file_path
|
|
1653
1812
|
};
|
|
1654
1813
|
}
|
|
1655
|
-
|
|
1814
|
+
const cwd = process.cwd();
|
|
1815
|
+
if (!normalizedFilePath.startsWith(cwd) && !path3.isAbsolute(file_path)) {
|
|
1656
1816
|
return {
|
|
1657
1817
|
success: false,
|
|
1658
|
-
error: `Invalid parameters: file_path
|
|
1818
|
+
error: `Invalid parameters: file_path must be within the current working directory or be an absolute path.`,
|
|
1659
1819
|
file_path: normalizedFilePath
|
|
1660
1820
|
};
|
|
1661
1821
|
}
|
|
@@ -1873,151 +2033,1652 @@ async function countLines(args) {
|
|
|
1873
2033
|
}
|
|
1874
2034
|
|
|
1875
2035
|
// src/app/agent/tools/natives/todo.ts
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
return todos;
|
|
2036
|
+
import * as fs6 from "fs";
|
|
2037
|
+
import * as path5 from "path";
|
|
2038
|
+
var taskStore = [];
|
|
2039
|
+
var nextId = 1;
|
|
2040
|
+
function getTodoFilePath() {
|
|
2041
|
+
return path5.join(process.cwd(), ".bluma", "todo.json");
|
|
1883
2042
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2043
|
+
function loadTasksFromFile() {
|
|
2044
|
+
try {
|
|
2045
|
+
const filePath = getTodoFilePath();
|
|
2046
|
+
if (fs6.existsSync(filePath)) {
|
|
2047
|
+
const data = fs6.readFileSync(filePath, "utf-8");
|
|
2048
|
+
const parsed = JSON.parse(data);
|
|
2049
|
+
if (Array.isArray(parsed.tasks)) {
|
|
2050
|
+
taskStore = parsed.tasks;
|
|
2051
|
+
nextId = parsed.nextId || taskStore.length + 1;
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
} catch {
|
|
1894
2055
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
const __dirname = path5.dirname(__filename);
|
|
1903
|
-
const configPath = path5.resolve(__dirname, "config", "native_tools.json");
|
|
1904
|
-
const fileContent = await fs6.readFile(configPath, "utf-8");
|
|
1905
|
-
const config2 = JSON.parse(fileContent);
|
|
1906
|
-
this.toolDefinitions = config2.nativeTools;
|
|
1907
|
-
} catch (error) {
|
|
1908
|
-
console.error("[ToolInvoker] Erro cr\xEDtico ao carregar 'native_tools.json'. As ferramentas nativas n\xE3o estar\xE3o dispon\xEDveis.", error);
|
|
1909
|
-
this.toolDefinitions = [];
|
|
2056
|
+
}
|
|
2057
|
+
function saveTasksToFile() {
|
|
2058
|
+
try {
|
|
2059
|
+
const filePath = getTodoFilePath();
|
|
2060
|
+
const dir = path5.dirname(filePath);
|
|
2061
|
+
if (!fs6.existsSync(dir)) {
|
|
2062
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
1910
2063
|
}
|
|
2064
|
+
fs6.writeFileSync(filePath, JSON.stringify({
|
|
2065
|
+
tasks: taskStore,
|
|
2066
|
+
nextId,
|
|
2067
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2068
|
+
}, null, 2));
|
|
2069
|
+
} catch {
|
|
1911
2070
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
this.toolImplementations.set("agent_end_turn", async () => ({ success: true, message: "Task ended by agent." }));
|
|
2071
|
+
}
|
|
2072
|
+
function calculateStats() {
|
|
2073
|
+
const total = taskStore.length;
|
|
2074
|
+
const pending = taskStore.filter((t) => t.status === "pending").length;
|
|
2075
|
+
const inProgress = taskStore.filter((t) => t.status === "in_progress").length;
|
|
2076
|
+
const completed = taskStore.filter((t) => t.status === "completed").length;
|
|
2077
|
+
const progress = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
2078
|
+
return { total, pending, inProgress, completed, progress };
|
|
2079
|
+
}
|
|
2080
|
+
function validateDescription(desc) {
|
|
2081
|
+
if (!desc || typeof desc !== "string") {
|
|
2082
|
+
return "Description is required";
|
|
1925
2083
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
* O MCPClient usará esta função para obter a lista de ferramentas locais.
|
|
1929
|
-
*/
|
|
1930
|
-
getToolDefinitions() {
|
|
1931
|
-
return this.toolDefinitions;
|
|
2084
|
+
if (desc.trim().length === 0) {
|
|
2085
|
+
return "Description cannot be empty";
|
|
1932
2086
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2087
|
+
if (desc.length > 500) {
|
|
2088
|
+
return "Description too long (max 500 chars)";
|
|
2089
|
+
}
|
|
2090
|
+
return null;
|
|
2091
|
+
}
|
|
2092
|
+
function createResult(success, message) {
|
|
2093
|
+
return {
|
|
2094
|
+
success,
|
|
2095
|
+
message,
|
|
2096
|
+
tasks: taskStore,
|
|
2097
|
+
stats: calculateStats()
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
function listTasks() {
|
|
2101
|
+
loadTasksFromFile();
|
|
2102
|
+
const stats = calculateStats();
|
|
2103
|
+
if (taskStore.length === 0) {
|
|
2104
|
+
return createResult(true, "No tasks yet. Use add action to create tasks.");
|
|
2105
|
+
}
|
|
2106
|
+
const progressBar = generateProgressBar(stats.progress);
|
|
2107
|
+
return createResult(true, `${stats.total} tasks ${progressBar} ${stats.progress}%`);
|
|
2108
|
+
}
|
|
2109
|
+
function addTasks(tasks) {
|
|
2110
|
+
if (!tasks || tasks.length === 0) {
|
|
2111
|
+
return createResult(false, "No tasks provided");
|
|
2112
|
+
}
|
|
2113
|
+
loadTasksFromFile();
|
|
2114
|
+
for (const task of tasks) {
|
|
2115
|
+
const error = validateDescription(task.description);
|
|
2116
|
+
if (error) {
|
|
2117
|
+
return createResult(false, `Invalid task: ${error}`);
|
|
1943
2118
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2119
|
+
const newTask = {
|
|
2120
|
+
id: nextId++,
|
|
2121
|
+
description: task.description.trim(),
|
|
2122
|
+
status: task.isComplete ? "completed" : "pending",
|
|
2123
|
+
priority: task.priority || "medium",
|
|
2124
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2125
|
+
completedAt: task.isComplete ? (/* @__PURE__ */ new Date()).toISOString() : void 0
|
|
2126
|
+
};
|
|
2127
|
+
taskStore.push(newTask);
|
|
2128
|
+
}
|
|
2129
|
+
saveTasksToFile();
|
|
2130
|
+
return createResult(true, `Added ${tasks.length} task(s)`);
|
|
2131
|
+
}
|
|
2132
|
+
function completeTask(taskId) {
|
|
2133
|
+
loadTasksFromFile();
|
|
2134
|
+
const task = taskStore.find((t) => t.id === taskId);
|
|
2135
|
+
if (!task) {
|
|
2136
|
+
return createResult(false, `Task #${taskId} not found`);
|
|
2137
|
+
}
|
|
2138
|
+
task.status = "completed";
|
|
2139
|
+
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2140
|
+
saveTasksToFile();
|
|
2141
|
+
return createResult(true, `Completed: ${task.description}`);
|
|
2142
|
+
}
|
|
2143
|
+
function updateTask(taskId, description, priority) {
|
|
2144
|
+
loadTasksFromFile();
|
|
2145
|
+
const task = taskStore.find((t) => t.id === taskId);
|
|
2146
|
+
if (!task) {
|
|
2147
|
+
return createResult(false, `Task #${taskId} not found`);
|
|
2148
|
+
}
|
|
2149
|
+
if (description) {
|
|
2150
|
+
const error = validateDescription(description);
|
|
2151
|
+
if (error) {
|
|
2152
|
+
return createResult(false, error);
|
|
1949
2153
|
}
|
|
2154
|
+
task.description = description.trim();
|
|
1950
2155
|
}
|
|
1951
|
-
|
|
2156
|
+
if (priority) {
|
|
2157
|
+
task.priority = priority;
|
|
2158
|
+
}
|
|
2159
|
+
saveTasksToFile();
|
|
2160
|
+
return createResult(true, `Updated task #${taskId}`);
|
|
2161
|
+
}
|
|
2162
|
+
function removeTask(taskId) {
|
|
2163
|
+
loadTasksFromFile();
|
|
2164
|
+
const index = taskStore.findIndex((t) => t.id === taskId);
|
|
2165
|
+
if (index === -1) {
|
|
2166
|
+
return createResult(false, `Task #${taskId} not found`);
|
|
2167
|
+
}
|
|
2168
|
+
const removed = taskStore.splice(index, 1)[0];
|
|
2169
|
+
saveTasksToFile();
|
|
2170
|
+
return createResult(true, `Removed: ${removed.description}`);
|
|
2171
|
+
}
|
|
2172
|
+
function clearCompleted() {
|
|
2173
|
+
loadTasksFromFile();
|
|
2174
|
+
const before = taskStore.length;
|
|
2175
|
+
taskStore = taskStore.filter((t) => t.status !== "completed");
|
|
2176
|
+
const removed = before - taskStore.length;
|
|
2177
|
+
saveTasksToFile();
|
|
2178
|
+
return createResult(true, `Cleared ${removed} completed task(s)`);
|
|
2179
|
+
}
|
|
2180
|
+
function generateProgressBar(percent) {
|
|
2181
|
+
const width = 10;
|
|
2182
|
+
const filled = Math.round(percent / 100 * width);
|
|
2183
|
+
const empty = width - filled;
|
|
2184
|
+
return "[" + "=".repeat(filled) + " ".repeat(empty) + "]";
|
|
2185
|
+
}
|
|
2186
|
+
async function todo(args) {
|
|
2187
|
+
if ("tasks" in args && !("action" in args)) {
|
|
2188
|
+
loadTasksFromFile();
|
|
2189
|
+
taskStore = [];
|
|
2190
|
+
nextId = 1;
|
|
2191
|
+
if (args.tasks && args.tasks.length > 0) {
|
|
2192
|
+
for (const task of args.tasks) {
|
|
2193
|
+
taskStore.push({
|
|
2194
|
+
id: nextId++,
|
|
2195
|
+
description: task.description,
|
|
2196
|
+
status: task.isComplete ? "completed" : "pending",
|
|
2197
|
+
priority: "medium",
|
|
2198
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2199
|
+
completedAt: task.isComplete ? (/* @__PURE__ */ new Date()).toISOString() : void 0
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
saveTasksToFile();
|
|
2204
|
+
const stats = calculateStats();
|
|
2205
|
+
const progressBar = generateProgressBar(stats.progress);
|
|
2206
|
+
return createResult(true, `Synced ${taskStore.length} tasks ${progressBar} ${stats.progress}%`);
|
|
2207
|
+
}
|
|
2208
|
+
const fullArgs = args;
|
|
2209
|
+
switch (fullArgs.action) {
|
|
2210
|
+
case "list":
|
|
2211
|
+
return listTasks();
|
|
2212
|
+
case "add":
|
|
2213
|
+
return addTasks(fullArgs.tasks);
|
|
2214
|
+
case "complete":
|
|
2215
|
+
if (!fullArgs.taskId) {
|
|
2216
|
+
return createResult(false, "taskId required for complete action");
|
|
2217
|
+
}
|
|
2218
|
+
return completeTask(fullArgs.taskId);
|
|
2219
|
+
case "update":
|
|
2220
|
+
if (!fullArgs.taskId) {
|
|
2221
|
+
return createResult(false, "taskId required for update action");
|
|
2222
|
+
}
|
|
2223
|
+
return updateTask(fullArgs.taskId, fullArgs.description, fullArgs.priority);
|
|
2224
|
+
case "remove":
|
|
2225
|
+
if (!fullArgs.taskId) {
|
|
2226
|
+
return createResult(false, "taskId required for remove action");
|
|
2227
|
+
}
|
|
2228
|
+
return removeTask(fullArgs.taskId);
|
|
2229
|
+
case "clear":
|
|
2230
|
+
return clearCompleted();
|
|
2231
|
+
case "sync":
|
|
2232
|
+
return addTasks(fullArgs.tasks);
|
|
2233
|
+
default:
|
|
2234
|
+
return createResult(false, `Unknown action: ${fullArgs.action}`);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
1952
2237
|
|
|
1953
|
-
// src/app/agent/tools/
|
|
1954
|
-
import { promises as fs7 } from "fs";
|
|
2238
|
+
// src/app/agent/tools/natives/find_by_name.ts
|
|
1955
2239
|
import path6 from "path";
|
|
1956
|
-
import
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2240
|
+
import { promises as fsPromises } from "fs";
|
|
2241
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
2242
|
+
"node_modules",
|
|
2243
|
+
".git",
|
|
2244
|
+
".venv",
|
|
2245
|
+
"venv",
|
|
2246
|
+
"__pycache__",
|
|
2247
|
+
".cache",
|
|
2248
|
+
"dist",
|
|
2249
|
+
"build",
|
|
2250
|
+
".next",
|
|
2251
|
+
".nuxt",
|
|
2252
|
+
"coverage",
|
|
2253
|
+
".nyc_output",
|
|
2254
|
+
".pytest_cache",
|
|
2255
|
+
"target",
|
|
2256
|
+
// Rust
|
|
2257
|
+
"vendor"
|
|
2258
|
+
// Go, PHP
|
|
2259
|
+
];
|
|
2260
|
+
var MAX_RESULTS2 = 100;
|
|
2261
|
+
var MAX_DEPTH_DEFAULT = 10;
|
|
2262
|
+
function globToRegex(glob) {
|
|
2263
|
+
let regex = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{DOUBLESTAR}}/g, ".*");
|
|
2264
|
+
return new RegExp(`^${regex}$`, "i");
|
|
2265
|
+
}
|
|
2266
|
+
function shouldIgnore(name, ignorePatterns, includeHidden) {
|
|
2267
|
+
if (!includeHidden && name.startsWith(".")) {
|
|
2268
|
+
return true;
|
|
1970
2269
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
this.globalToolsForLlm.push(...nativeTools);
|
|
1975
|
-
for (const tool of nativeTools) {
|
|
1976
|
-
const toolName = tool.function.name;
|
|
1977
|
-
this.toolToServerMap.set(toolName, {
|
|
1978
|
-
server: "native",
|
|
1979
|
-
originalName: toolName
|
|
1980
|
-
});
|
|
2270
|
+
for (const pattern of ignorePatterns) {
|
|
2271
|
+
if (name === pattern || name.match(globToRegex(pattern))) {
|
|
2272
|
+
return true;
|
|
1981
2273
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2274
|
+
}
|
|
2275
|
+
return false;
|
|
2276
|
+
}
|
|
2277
|
+
function matchesExtensions(filename, extensions) {
|
|
2278
|
+
if (!extensions || extensions.length === 0) return true;
|
|
2279
|
+
const ext = path6.extname(filename).toLowerCase();
|
|
2280
|
+
return extensions.some((e) => {
|
|
2281
|
+
const normalizedExt = e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`;
|
|
2282
|
+
return ext === normalizedExt;
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
async function searchDirectory(dir, pattern, baseDir, options, results) {
|
|
2286
|
+
if (options.currentDepth > options.maxDepth || results.length >= MAX_RESULTS2) {
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
let entries;
|
|
2290
|
+
try {
|
|
2291
|
+
entries = await fsPromises.readdir(dir, { withFileTypes: true });
|
|
2292
|
+
} catch (error) {
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
for (const entry of entries) {
|
|
2296
|
+
if (results.length >= MAX_RESULTS2) break;
|
|
2297
|
+
const name = entry.name;
|
|
2298
|
+
if (shouldIgnore(name, options.ignorePatterns, options.includeHidden)) {
|
|
2299
|
+
continue;
|
|
1996
2300
|
}
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2301
|
+
const fullPath = path6.join(dir, name);
|
|
2302
|
+
const relativePath = path6.relative(baseDir, fullPath);
|
|
2303
|
+
if (entry.isDirectory()) {
|
|
2304
|
+
if (pattern.test(name)) {
|
|
2305
|
+
results.push({
|
|
2306
|
+
path: fullPath,
|
|
2307
|
+
relative_path: relativePath,
|
|
2308
|
+
type: "directory"
|
|
2003
2309
|
});
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2310
|
+
}
|
|
2311
|
+
await searchDirectory(fullPath, pattern, baseDir, {
|
|
2312
|
+
...options,
|
|
2313
|
+
currentDepth: options.currentDepth + 1
|
|
2314
|
+
}, results);
|
|
2315
|
+
} else if (entry.isFile()) {
|
|
2316
|
+
if (pattern.test(name) && matchesExtensions(name, options.extensions)) {
|
|
2317
|
+
try {
|
|
2318
|
+
const stats = await fsPromises.stat(fullPath);
|
|
2319
|
+
results.push({
|
|
2320
|
+
path: fullPath,
|
|
2321
|
+
relative_path: relativePath,
|
|
2322
|
+
type: "file",
|
|
2323
|
+
size: stats.size,
|
|
2324
|
+
modified: stats.mtime.toISOString()
|
|
2325
|
+
});
|
|
2326
|
+
} catch {
|
|
2327
|
+
results.push({
|
|
2328
|
+
path: fullPath,
|
|
2329
|
+
relative_path: relativePath,
|
|
2330
|
+
type: "file"
|
|
2331
|
+
});
|
|
2008
2332
|
}
|
|
2009
|
-
} catch (error) {
|
|
2010
|
-
this.eventBus.emit("backend_message", {
|
|
2011
|
-
type: "error",
|
|
2012
|
-
message: `Failed to connect to server '${serverName}'.`
|
|
2013
|
-
});
|
|
2014
2333
|
}
|
|
2015
2334
|
}
|
|
2016
2335
|
}
|
|
2017
|
-
|
|
2336
|
+
}
|
|
2337
|
+
async function findByName(args) {
|
|
2338
|
+
try {
|
|
2339
|
+
const {
|
|
2340
|
+
pattern,
|
|
2341
|
+
directory = process.cwd(),
|
|
2342
|
+
extensions,
|
|
2343
|
+
max_depth = MAX_DEPTH_DEFAULT,
|
|
2344
|
+
include_hidden = false,
|
|
2345
|
+
exclude_patterns = []
|
|
2346
|
+
} = args;
|
|
2347
|
+
if (!pattern || typeof pattern !== "string") {
|
|
2348
|
+
return {
|
|
2349
|
+
success: false,
|
|
2350
|
+
pattern: pattern || "",
|
|
2351
|
+
directory,
|
|
2352
|
+
results: [],
|
|
2353
|
+
total_found: 0,
|
|
2354
|
+
truncated: false,
|
|
2355
|
+
error: "Pattern is required and must be a string"
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
const resolvedDir = path6.resolve(directory);
|
|
2018
2359
|
try {
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2360
|
+
const stats = await fsPromises.stat(resolvedDir);
|
|
2361
|
+
if (!stats.isDirectory()) {
|
|
2362
|
+
return {
|
|
2363
|
+
success: false,
|
|
2364
|
+
pattern,
|
|
2365
|
+
directory: resolvedDir,
|
|
2366
|
+
results: [],
|
|
2367
|
+
total_found: 0,
|
|
2368
|
+
truncated: false,
|
|
2369
|
+
error: `Path is not a directory: ${resolvedDir}`
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
return {
|
|
2374
|
+
success: false,
|
|
2375
|
+
pattern,
|
|
2376
|
+
directory: resolvedDir,
|
|
2377
|
+
results: [],
|
|
2378
|
+
total_found: 0,
|
|
2379
|
+
truncated: false,
|
|
2380
|
+
error: `Directory not found: ${resolvedDir}`
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
const ignorePatterns = [...DEFAULT_IGNORE_PATTERNS, ...exclude_patterns];
|
|
2384
|
+
const patternRegex = globToRegex(pattern);
|
|
2385
|
+
const results = [];
|
|
2386
|
+
await searchDirectory(resolvedDir, patternRegex, resolvedDir, {
|
|
2387
|
+
extensions,
|
|
2388
|
+
maxDepth: max_depth,
|
|
2389
|
+
currentDepth: 0,
|
|
2390
|
+
includeHidden: include_hidden,
|
|
2391
|
+
ignorePatterns
|
|
2392
|
+
}, results);
|
|
2393
|
+
results.sort((a, b) => {
|
|
2394
|
+
if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
|
|
2395
|
+
return a.relative_path.localeCompare(b.relative_path);
|
|
2396
|
+
});
|
|
2397
|
+
return {
|
|
2398
|
+
success: true,
|
|
2399
|
+
pattern,
|
|
2400
|
+
directory: resolvedDir,
|
|
2401
|
+
results,
|
|
2402
|
+
total_found: results.length,
|
|
2403
|
+
truncated: results.length >= MAX_RESULTS2
|
|
2404
|
+
};
|
|
2405
|
+
} catch (error) {
|
|
2406
|
+
return {
|
|
2407
|
+
success: false,
|
|
2408
|
+
pattern: args.pattern || "",
|
|
2409
|
+
directory: args.directory || process.cwd(),
|
|
2410
|
+
results: [],
|
|
2411
|
+
total_found: 0,
|
|
2412
|
+
truncated: false,
|
|
2413
|
+
error: `Unexpected error: ${error.message}`
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// src/app/agent/tools/natives/grep_search.ts
|
|
2419
|
+
import path7 from "path";
|
|
2420
|
+
import { promises as fsPromises2 } from "fs";
|
|
2421
|
+
var MAX_RESULTS3 = 100;
|
|
2422
|
+
var MAX_FILE_SIZE2 = 1024 * 1024;
|
|
2423
|
+
var MAX_LINE_LENGTH = 500;
|
|
2424
|
+
var DEFAULT_IGNORE2 = [
|
|
2425
|
+
"node_modules",
|
|
2426
|
+
".git",
|
|
2427
|
+
".venv",
|
|
2428
|
+
"venv",
|
|
2429
|
+
"__pycache__",
|
|
2430
|
+
".cache",
|
|
2431
|
+
"dist",
|
|
2432
|
+
"build",
|
|
2433
|
+
".next",
|
|
2434
|
+
"coverage",
|
|
2435
|
+
"*.min.js",
|
|
2436
|
+
"*.min.css",
|
|
2437
|
+
"*.map",
|
|
2438
|
+
"*.lock",
|
|
2439
|
+
"package-lock.json",
|
|
2440
|
+
"yarn.lock",
|
|
2441
|
+
"pnpm-lock.yaml"
|
|
2442
|
+
];
|
|
2443
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2444
|
+
".ts",
|
|
2445
|
+
".tsx",
|
|
2446
|
+
".js",
|
|
2447
|
+
".jsx",
|
|
2448
|
+
".mjs",
|
|
2449
|
+
".cjs",
|
|
2450
|
+
".py",
|
|
2451
|
+
".pyw",
|
|
2452
|
+
".java",
|
|
2453
|
+
".kt",
|
|
2454
|
+
".scala",
|
|
2455
|
+
".go",
|
|
2456
|
+
".rs",
|
|
2457
|
+
".rb",
|
|
2458
|
+
".php",
|
|
2459
|
+
".cs",
|
|
2460
|
+
".cpp",
|
|
2461
|
+
".c",
|
|
2462
|
+
".h",
|
|
2463
|
+
".hpp",
|
|
2464
|
+
".swift",
|
|
2465
|
+
".vue",
|
|
2466
|
+
".svelte",
|
|
2467
|
+
".html",
|
|
2468
|
+
".htm",
|
|
2469
|
+
".xml",
|
|
2470
|
+
".svg",
|
|
2471
|
+
".css",
|
|
2472
|
+
".scss",
|
|
2473
|
+
".sass",
|
|
2474
|
+
".less",
|
|
2475
|
+
".json",
|
|
2476
|
+
".yaml",
|
|
2477
|
+
".yml",
|
|
2478
|
+
".toml",
|
|
2479
|
+
".md",
|
|
2480
|
+
".mdx",
|
|
2481
|
+
".txt",
|
|
2482
|
+
".rst",
|
|
2483
|
+
".sh",
|
|
2484
|
+
".bash",
|
|
2485
|
+
".zsh",
|
|
2486
|
+
".fish",
|
|
2487
|
+
".sql",
|
|
2488
|
+
".graphql",
|
|
2489
|
+
".gql",
|
|
2490
|
+
".env",
|
|
2491
|
+
".env.local",
|
|
2492
|
+
".env.example",
|
|
2493
|
+
".gitignore",
|
|
2494
|
+
".dockerignore",
|
|
2495
|
+
".eslintrc",
|
|
2496
|
+
".prettierrc",
|
|
2497
|
+
"Dockerfile",
|
|
2498
|
+
"Makefile",
|
|
2499
|
+
"Rakefile"
|
|
2500
|
+
]);
|
|
2501
|
+
function isTextFile(filepath) {
|
|
2502
|
+
const ext = path7.extname(filepath).toLowerCase();
|
|
2503
|
+
const basename = path7.basename(filepath);
|
|
2504
|
+
if (TEXT_EXTENSIONS.has(ext)) return true;
|
|
2505
|
+
if (TEXT_EXTENSIONS.has(basename)) return true;
|
|
2506
|
+
if (basename.startsWith(".") && !ext) return true;
|
|
2507
|
+
return false;
|
|
2508
|
+
}
|
|
2509
|
+
function shouldIgnore2(name) {
|
|
2510
|
+
for (const pattern of DEFAULT_IGNORE2) {
|
|
2511
|
+
if (pattern.includes("*")) {
|
|
2512
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
2513
|
+
if (regex.test(name)) return true;
|
|
2514
|
+
} else if (name === pattern) {
|
|
2515
|
+
return true;
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
return false;
|
|
2519
|
+
}
|
|
2520
|
+
function createSearchPattern(query, isRegex, caseInsensitive) {
|
|
2521
|
+
try {
|
|
2522
|
+
const flags = caseInsensitive ? "gi" : "g";
|
|
2523
|
+
if (isRegex) {
|
|
2524
|
+
return new RegExp(query, flags);
|
|
2525
|
+
} else {
|
|
2526
|
+
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2527
|
+
return new RegExp(escaped, flags);
|
|
2528
|
+
}
|
|
2529
|
+
} catch (error) {
|
|
2530
|
+
throw new Error(`Invalid regex pattern: ${error.message}`);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
function matchesIncludePattern(filename, includePatterns) {
|
|
2534
|
+
if (!includePatterns || includePatterns.length === 0) return true;
|
|
2535
|
+
for (const pattern of includePatterns) {
|
|
2536
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$", "i");
|
|
2537
|
+
if (regex.test(filename)) return true;
|
|
2538
|
+
}
|
|
2539
|
+
return false;
|
|
2540
|
+
}
|
|
2541
|
+
async function searchFile(filepath, baseDir, pattern, contextLines, matches, maxResults) {
|
|
2542
|
+
let fileMatches = 0;
|
|
2543
|
+
try {
|
|
2544
|
+
const stats = await fsPromises2.stat(filepath);
|
|
2545
|
+
if (stats.size > MAX_FILE_SIZE2) return 0;
|
|
2546
|
+
const content = await fsPromises2.readFile(filepath, "utf-8");
|
|
2547
|
+
const lines = content.split("\n");
|
|
2548
|
+
const relativePath = path7.relative(baseDir, filepath);
|
|
2549
|
+
for (let i = 0; i < lines.length && matches.length < maxResults; i++) {
|
|
2550
|
+
const line = lines[i];
|
|
2551
|
+
pattern.lastIndex = 0;
|
|
2552
|
+
let match;
|
|
2553
|
+
while ((match = pattern.exec(line)) !== null && matches.length < maxResults) {
|
|
2554
|
+
const lineContent = line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) + "..." : line;
|
|
2555
|
+
const searchMatch = {
|
|
2556
|
+
file: filepath,
|
|
2557
|
+
relative_path: relativePath,
|
|
2558
|
+
line_number: i + 1,
|
|
2559
|
+
line_content: lineContent.trim(),
|
|
2560
|
+
match_start: match.index,
|
|
2561
|
+
match_end: match.index + match[0].length
|
|
2562
|
+
};
|
|
2563
|
+
if (contextLines > 0) {
|
|
2564
|
+
searchMatch.context_before = lines.slice(Math.max(0, i - contextLines), i).map((l) => l.length > MAX_LINE_LENGTH ? l.substring(0, MAX_LINE_LENGTH) + "..." : l);
|
|
2565
|
+
searchMatch.context_after = lines.slice(i + 1, i + 1 + contextLines).map((l) => l.length > MAX_LINE_LENGTH ? l.substring(0, MAX_LINE_LENGTH) + "..." : l);
|
|
2566
|
+
}
|
|
2567
|
+
matches.push(searchMatch);
|
|
2568
|
+
fileMatches++;
|
|
2569
|
+
if (!pattern.global) break;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
} catch (error) {
|
|
2573
|
+
}
|
|
2574
|
+
return fileMatches;
|
|
2575
|
+
}
|
|
2576
|
+
async function searchDirectory2(dir, baseDir, pattern, includePatterns, contextLines, matches, maxResults, stats) {
|
|
2577
|
+
if (matches.length >= maxResults) return;
|
|
2578
|
+
let entries;
|
|
2579
|
+
try {
|
|
2580
|
+
entries = await fsPromises2.readdir(dir, { withFileTypes: true });
|
|
2581
|
+
} catch {
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
for (const entry of entries) {
|
|
2585
|
+
if (matches.length >= maxResults) break;
|
|
2586
|
+
const name = entry.name;
|
|
2587
|
+
if (shouldIgnore2(name)) continue;
|
|
2588
|
+
const fullPath = path7.join(dir, name);
|
|
2589
|
+
if (entry.isDirectory()) {
|
|
2590
|
+
await searchDirectory2(fullPath, baseDir, pattern, includePatterns, contextLines, matches, maxResults, stats);
|
|
2591
|
+
} else if (entry.isFile()) {
|
|
2592
|
+
if (!isTextFile(name)) continue;
|
|
2593
|
+
if (!matchesIncludePattern(name, includePatterns)) continue;
|
|
2594
|
+
stats.filesSearched++;
|
|
2595
|
+
const found = await searchFile(fullPath, baseDir, pattern, contextLines, matches, maxResults);
|
|
2596
|
+
if (found > 0) stats.filesWithMatches++;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
async function grepSearch(args) {
|
|
2601
|
+
try {
|
|
2602
|
+
const {
|
|
2603
|
+
query,
|
|
2604
|
+
path: searchPath,
|
|
2605
|
+
case_insensitive = true,
|
|
2606
|
+
is_regex = false,
|
|
2607
|
+
include_patterns,
|
|
2608
|
+
max_results = MAX_RESULTS3,
|
|
2609
|
+
context_lines = 0
|
|
2610
|
+
} = args;
|
|
2611
|
+
if (!query || typeof query !== "string") {
|
|
2612
|
+
return {
|
|
2613
|
+
success: false,
|
|
2614
|
+
query: query || "",
|
|
2615
|
+
search_path: searchPath || "",
|
|
2616
|
+
matches: [],
|
|
2617
|
+
files_searched: 0,
|
|
2618
|
+
files_with_matches: 0,
|
|
2619
|
+
total_matches: 0,
|
|
2620
|
+
truncated: false,
|
|
2621
|
+
error: "Query is required and must be a string"
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
if (!searchPath) {
|
|
2625
|
+
return {
|
|
2626
|
+
success: false,
|
|
2627
|
+
query,
|
|
2628
|
+
search_path: "",
|
|
2629
|
+
matches: [],
|
|
2630
|
+
files_searched: 0,
|
|
2631
|
+
files_with_matches: 0,
|
|
2632
|
+
total_matches: 0,
|
|
2633
|
+
truncated: false,
|
|
2634
|
+
error: "Search path is required"
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
const resolvedPath = path7.resolve(searchPath);
|
|
2638
|
+
let stats;
|
|
2639
|
+
try {
|
|
2640
|
+
stats = await fsPromises2.stat(resolvedPath);
|
|
2641
|
+
} catch {
|
|
2642
|
+
return {
|
|
2643
|
+
success: false,
|
|
2644
|
+
query,
|
|
2645
|
+
search_path: resolvedPath,
|
|
2646
|
+
matches: [],
|
|
2647
|
+
files_searched: 0,
|
|
2648
|
+
files_with_matches: 0,
|
|
2649
|
+
total_matches: 0,
|
|
2650
|
+
truncated: false,
|
|
2651
|
+
error: `Path not found: ${resolvedPath}`
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
let pattern;
|
|
2655
|
+
try {
|
|
2656
|
+
pattern = createSearchPattern(query, is_regex, case_insensitive);
|
|
2657
|
+
} catch (error) {
|
|
2658
|
+
return {
|
|
2659
|
+
success: false,
|
|
2660
|
+
query,
|
|
2661
|
+
search_path: resolvedPath,
|
|
2662
|
+
matches: [],
|
|
2663
|
+
files_searched: 0,
|
|
2664
|
+
files_with_matches: 0,
|
|
2665
|
+
total_matches: 0,
|
|
2666
|
+
truncated: false,
|
|
2667
|
+
error: error.message
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
const matches = [];
|
|
2671
|
+
const searchStats = { filesSearched: 0, filesWithMatches: 0 };
|
|
2672
|
+
if (stats.isDirectory()) {
|
|
2673
|
+
await searchDirectory2(
|
|
2674
|
+
resolvedPath,
|
|
2675
|
+
resolvedPath,
|
|
2676
|
+
pattern,
|
|
2677
|
+
include_patterns,
|
|
2678
|
+
context_lines,
|
|
2679
|
+
matches,
|
|
2680
|
+
max_results,
|
|
2681
|
+
searchStats
|
|
2682
|
+
);
|
|
2683
|
+
} else if (stats.isFile()) {
|
|
2684
|
+
searchStats.filesSearched = 1;
|
|
2685
|
+
const found = await searchFile(resolvedPath, path7.dirname(resolvedPath), pattern, context_lines, matches, max_results);
|
|
2686
|
+
if (found > 0) searchStats.filesWithMatches = 1;
|
|
2687
|
+
}
|
|
2688
|
+
return {
|
|
2689
|
+
success: true,
|
|
2690
|
+
query,
|
|
2691
|
+
search_path: resolvedPath,
|
|
2692
|
+
matches,
|
|
2693
|
+
files_searched: searchStats.filesSearched,
|
|
2694
|
+
files_with_matches: searchStats.filesWithMatches,
|
|
2695
|
+
total_matches: matches.length,
|
|
2696
|
+
truncated: matches.length >= max_results
|
|
2697
|
+
};
|
|
2698
|
+
} catch (error) {
|
|
2699
|
+
return {
|
|
2700
|
+
success: false,
|
|
2701
|
+
query: args.query || "",
|
|
2702
|
+
search_path: args.path || "",
|
|
2703
|
+
matches: [],
|
|
2704
|
+
files_searched: 0,
|
|
2705
|
+
files_with_matches: 0,
|
|
2706
|
+
total_matches: 0,
|
|
2707
|
+
truncated: false,
|
|
2708
|
+
error: `Unexpected error: ${error.message}`
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// src/app/agent/tools/natives/view_file_outline.ts
|
|
2714
|
+
import path8 from "path";
|
|
2715
|
+
import { promises as fsPromises3 } from "fs";
|
|
2716
|
+
var LANGUAGE_MAP = {
|
|
2717
|
+
".ts": "typescript",
|
|
2718
|
+
".tsx": "typescript",
|
|
2719
|
+
".js": "javascript",
|
|
2720
|
+
".jsx": "javascript",
|
|
2721
|
+
".mjs": "javascript",
|
|
2722
|
+
".cjs": "javascript",
|
|
2723
|
+
".py": "python",
|
|
2724
|
+
".java": "java",
|
|
2725
|
+
".go": "go",
|
|
2726
|
+
".rs": "rust",
|
|
2727
|
+
".rb": "ruby",
|
|
2728
|
+
".php": "php",
|
|
2729
|
+
".cs": "csharp",
|
|
2730
|
+
".cpp": "cpp",
|
|
2731
|
+
".c": "c",
|
|
2732
|
+
".h": "c",
|
|
2733
|
+
".hpp": "cpp",
|
|
2734
|
+
".swift": "swift",
|
|
2735
|
+
".kt": "kotlin",
|
|
2736
|
+
".scala": "scala"
|
|
2737
|
+
};
|
|
2738
|
+
var PATTERNS = {
|
|
2739
|
+
typescript: [
|
|
2740
|
+
// Classes
|
|
2741
|
+
/^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w,\s]+)?/,
|
|
2742
|
+
// Interfaces
|
|
2743
|
+
/^(?:export\s+)?interface\s+(\w+)/,
|
|
2744
|
+
// Types
|
|
2745
|
+
/^(?:export\s+)?type\s+(\w+)/,
|
|
2746
|
+
// Functions (standalone)
|
|
2747
|
+
/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
|
|
2748
|
+
// Arrow functions assigned to const/let
|
|
2749
|
+
/^(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/,
|
|
2750
|
+
// Methods inside classes (indented)
|
|
2751
|
+
/^\s+(?:public|private|protected|static|async|readonly|\s)*(\w+)\s*\([^)]*\)\s*(?::\s*[\w<>\[\]|&\s]+)?\s*\{/,
|
|
2752
|
+
// Const exports
|
|
2753
|
+
/^(?:export\s+)?const\s+(\w+)\s*[:=]/
|
|
2754
|
+
],
|
|
2755
|
+
javascript: [
|
|
2756
|
+
/^(?:export\s+)?(?:default\s+)?class\s+(\w+)/,
|
|
2757
|
+
/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
|
|
2758
|
+
/^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/,
|
|
2759
|
+
/^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/,
|
|
2760
|
+
/^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=/
|
|
2761
|
+
],
|
|
2762
|
+
python: [
|
|
2763
|
+
/^class\s+(\w+)(?:\([^)]*\))?:/,
|
|
2764
|
+
/^(?:async\s+)?def\s+(\w+)\s*\(/,
|
|
2765
|
+
/^(\w+)\s*=\s*(?:lambda|def)/
|
|
2766
|
+
],
|
|
2767
|
+
java: [
|
|
2768
|
+
/^(?:public|private|protected)?\s*(?:static)?\s*(?:final)?\s*(?:abstract)?\s*class\s+(\w+)/,
|
|
2769
|
+
/^(?:public|private|protected)?\s*(?:static)?\s*(?:final)?\s*interface\s+(\w+)/,
|
|
2770
|
+
/^\s*(?:public|private|protected)?\s*(?:static)?\s*(?:final)?\s*(?:synchronized)?\s*(?:\w+(?:<[^>]+>)?)\s+(\w+)\s*\(/
|
|
2771
|
+
],
|
|
2772
|
+
go: [
|
|
2773
|
+
/^type\s+(\w+)\s+struct\s*\{/,
|
|
2774
|
+
/^type\s+(\w+)\s+interface\s*\{/,
|
|
2775
|
+
/^func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/
|
|
2776
|
+
],
|
|
2777
|
+
rust: [
|
|
2778
|
+
/^(?:pub\s+)?struct\s+(\w+)/,
|
|
2779
|
+
/^(?:pub\s+)?enum\s+(\w+)/,
|
|
2780
|
+
/^(?:pub\s+)?trait\s+(\w+)/,
|
|
2781
|
+
/^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/,
|
|
2782
|
+
/^impl(?:<[^>]+>)?\s+(?:(\w+)|for\s+(\w+))/
|
|
2783
|
+
],
|
|
2784
|
+
ruby: [
|
|
2785
|
+
/^class\s+(\w+)/,
|
|
2786
|
+
/^module\s+(\w+)/,
|
|
2787
|
+
/^def\s+(\w+)/
|
|
2788
|
+
],
|
|
2789
|
+
php: [
|
|
2790
|
+
/^(?:abstract\s+)?class\s+(\w+)/,
|
|
2791
|
+
/^interface\s+(\w+)/,
|
|
2792
|
+
/^(?:public|private|protected)?\s*(?:static)?\s*function\s+(\w+)/
|
|
2793
|
+
],
|
|
2794
|
+
csharp: [
|
|
2795
|
+
/^(?:public|private|protected|internal)?\s*(?:static)?\s*(?:partial)?\s*class\s+(\w+)/,
|
|
2796
|
+
/^(?:public|private|protected|internal)?\s*interface\s+(\w+)/,
|
|
2797
|
+
/^\s*(?:public|private|protected|internal)?\s*(?:static)?\s*(?:async)?\s*(?:\w+(?:<[^>]+>)?)\s+(\w+)\s*\(/
|
|
2798
|
+
],
|
|
2799
|
+
swift: [
|
|
2800
|
+
/^(?:public|private|internal|open)?\s*class\s+(\w+)/,
|
|
2801
|
+
/^(?:public|private|internal)?\s*struct\s+(\w+)/,
|
|
2802
|
+
/^(?:public|private|internal)?\s*protocol\s+(\w+)/,
|
|
2803
|
+
/^(?:public|private|internal)?\s*(?:static)?\s*func\s+(\w+)/
|
|
2804
|
+
],
|
|
2805
|
+
kotlin: [
|
|
2806
|
+
/^(?:open|abstract|sealed)?\s*class\s+(\w+)/,
|
|
2807
|
+
/^interface\s+(\w+)/,
|
|
2808
|
+
/^(?:private|public|internal)?\s*fun\s+(\w+)/
|
|
2809
|
+
],
|
|
2810
|
+
scala: [
|
|
2811
|
+
/^(?:sealed\s+)?(?:abstract\s+)?class\s+(\w+)/,
|
|
2812
|
+
/^object\s+(\w+)/,
|
|
2813
|
+
/^trait\s+(\w+)/,
|
|
2814
|
+
/^def\s+(\w+)/
|
|
2815
|
+
],
|
|
2816
|
+
c: [
|
|
2817
|
+
/^(?:static\s+)?(?:inline\s+)?(?:const\s+)?(?:\w+(?:\s*\*)*)\s+(\w+)\s*\([^)]*\)\s*\{/,
|
|
2818
|
+
/^typedef\s+struct\s*\w*\s*\{[^}]*\}\s*(\w+)/,
|
|
2819
|
+
/^struct\s+(\w+)\s*\{/
|
|
2820
|
+
],
|
|
2821
|
+
cpp: [
|
|
2822
|
+
/^class\s+(\w+)/,
|
|
2823
|
+
/^(?:virtual\s+)?(?:\w+(?:\s*[*&])*)\s+(\w+)\s*\([^)]*\)\s*(?:const)?\s*(?:override)?\s*\{/,
|
|
2824
|
+
/^namespace\s+(\w+)\s*\{/
|
|
2825
|
+
]
|
|
2826
|
+
};
|
|
2827
|
+
function detectLanguage(filepath) {
|
|
2828
|
+
const ext = path8.extname(filepath).toLowerCase();
|
|
2829
|
+
return LANGUAGE_MAP[ext] || "unknown";
|
|
2830
|
+
}
|
|
2831
|
+
function determineItemType(line, language) {
|
|
2832
|
+
const normalizedLine = line.trim().toLowerCase();
|
|
2833
|
+
if (normalizedLine.includes("class ")) return "class";
|
|
2834
|
+
if (normalizedLine.includes("interface ")) return "interface";
|
|
2835
|
+
if (normalizedLine.includes("type ")) return "type";
|
|
2836
|
+
if (normalizedLine.startsWith("import ")) return "import";
|
|
2837
|
+
if (normalizedLine.includes("export ") && !normalizedLine.includes("function") && !normalizedLine.includes("class")) return "export";
|
|
2838
|
+
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
2839
|
+
if (normalizedLine.includes("function") || normalizedLine.includes("def ") || normalizedLine.includes("fn ") || normalizedLine.includes("func ")) {
|
|
2840
|
+
return "method";
|
|
2841
|
+
}
|
|
2842
|
+
if (/^\s+\w+\s*\(/.test(line)) return "method";
|
|
2843
|
+
}
|
|
2844
|
+
if (normalizedLine.includes("function ") || normalizedLine.includes("def ") || normalizedLine.includes("fn ") || normalizedLine.includes("func ")) {
|
|
2845
|
+
return "function";
|
|
2846
|
+
}
|
|
2847
|
+
if (normalizedLine.includes("const ") || normalizedLine.includes("let ") || normalizedLine.includes("var ")) {
|
|
2848
|
+
return normalizedLine.includes("=>") ? "function" : "const";
|
|
2849
|
+
}
|
|
2850
|
+
return "function";
|
|
2851
|
+
}
|
|
2852
|
+
async function extractOutline(filepath, content, language) {
|
|
2853
|
+
const items = [];
|
|
2854
|
+
const lines = content.split("\n");
|
|
2855
|
+
const patterns = PATTERNS[language] || PATTERNS.javascript;
|
|
2856
|
+
let currentClass = null;
|
|
2857
|
+
let braceCount = 0;
|
|
2858
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2859
|
+
const line = lines[i];
|
|
2860
|
+
const lineNumber = i + 1;
|
|
2861
|
+
const openBraces = (line.match(/\{/g) || []).length;
|
|
2862
|
+
const closeBraces = (line.match(/\}/g) || []).length;
|
|
2863
|
+
braceCount += openBraces - closeBraces;
|
|
2864
|
+
if (braceCount <= 0 && currentClass) {
|
|
2865
|
+
currentClass = null;
|
|
2866
|
+
}
|
|
2867
|
+
for (const pattern of patterns) {
|
|
2868
|
+
const match = line.match(pattern);
|
|
2869
|
+
if (match) {
|
|
2870
|
+
const name = match[1] || match[2];
|
|
2871
|
+
if (!name) continue;
|
|
2872
|
+
const itemType = determineItemType(line, language);
|
|
2873
|
+
if (!itemType) continue;
|
|
2874
|
+
const exported = line.includes("export ");
|
|
2875
|
+
const isInsideClass = currentClass && (line.startsWith(" ") || line.startsWith(" "));
|
|
2876
|
+
const item = {
|
|
2877
|
+
type: itemType,
|
|
2878
|
+
name,
|
|
2879
|
+
line_start: lineNumber,
|
|
2880
|
+
signature: line.trim().substring(0, 120),
|
|
2881
|
+
exported
|
|
2882
|
+
};
|
|
2883
|
+
if (isInsideClass && itemType === "method" && currentClass) {
|
|
2884
|
+
item.parent = currentClass;
|
|
2885
|
+
}
|
|
2886
|
+
if (itemType === "class") {
|
|
2887
|
+
currentClass = name;
|
|
2888
|
+
}
|
|
2889
|
+
items.push(item);
|
|
2890
|
+
break;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
return items;
|
|
2895
|
+
}
|
|
2896
|
+
function generateSummary(items, totalLines) {
|
|
2897
|
+
const counts = {};
|
|
2898
|
+
for (const item of items) {
|
|
2899
|
+
counts[item.type] = (counts[item.type] || 0) + 1;
|
|
2900
|
+
}
|
|
2901
|
+
const parts = [`${totalLines} lines`];
|
|
2902
|
+
if (counts.class) parts.push(`${counts.class} class${counts.class > 1 ? "es" : ""}`);
|
|
2903
|
+
if (counts.interface) parts.push(`${counts.interface} interface${counts.interface > 1 ? "s" : ""}`);
|
|
2904
|
+
if (counts.function) parts.push(`${counts.function} function${counts.function > 1 ? "s" : ""}`);
|
|
2905
|
+
if (counts.method) parts.push(`${counts.method} method${counts.method > 1 ? "s" : ""}`);
|
|
2906
|
+
if (counts.type) parts.push(`${counts.type} type${counts.type > 1 ? "s" : ""}`);
|
|
2907
|
+
if (counts.const) parts.push(`${counts.const} const${counts.const > 1 ? "s" : ""}`);
|
|
2908
|
+
return parts.join(", ");
|
|
2909
|
+
}
|
|
2910
|
+
async function viewFileOutline(args) {
|
|
2911
|
+
try {
|
|
2912
|
+
const { file_path } = args;
|
|
2913
|
+
if (!file_path || typeof file_path !== "string") {
|
|
2914
|
+
return {
|
|
2915
|
+
success: false,
|
|
2916
|
+
file_path: file_path || "",
|
|
2917
|
+
language: "unknown",
|
|
2918
|
+
total_lines: 0,
|
|
2919
|
+
items: [],
|
|
2920
|
+
summary: "",
|
|
2921
|
+
error: "file_path is required and must be a string"
|
|
2922
|
+
};
|
|
2923
|
+
}
|
|
2924
|
+
const resolvedPath = path8.resolve(file_path);
|
|
2925
|
+
let content;
|
|
2926
|
+
try {
|
|
2927
|
+
content = await fsPromises3.readFile(resolvedPath, "utf-8");
|
|
2928
|
+
} catch (error) {
|
|
2929
|
+
return {
|
|
2930
|
+
success: false,
|
|
2931
|
+
file_path: resolvedPath,
|
|
2932
|
+
language: "unknown",
|
|
2933
|
+
total_lines: 0,
|
|
2934
|
+
items: [],
|
|
2935
|
+
summary: "",
|
|
2936
|
+
error: error.code === "ENOENT" ? `File not found: ${resolvedPath}` : `Error reading file: ${error.message}`
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
const language = detectLanguage(resolvedPath);
|
|
2940
|
+
const lines = content.split("\n");
|
|
2941
|
+
const totalLines = lines.length;
|
|
2942
|
+
const items = await extractOutline(resolvedPath, content, language);
|
|
2943
|
+
return {
|
|
2944
|
+
success: true,
|
|
2945
|
+
file_path: resolvedPath,
|
|
2946
|
+
language,
|
|
2947
|
+
total_lines: totalLines,
|
|
2948
|
+
items,
|
|
2949
|
+
summary: generateSummary(items, totalLines)
|
|
2950
|
+
};
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
return {
|
|
2953
|
+
success: false,
|
|
2954
|
+
file_path: args.file_path || "",
|
|
2955
|
+
language: "unknown",
|
|
2956
|
+
total_lines: 0,
|
|
2957
|
+
items: [],
|
|
2958
|
+
summary: "",
|
|
2959
|
+
error: `Unexpected error: ${error.message}`
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
|
|
2964
|
+
// src/app/agent/tools/natives/async_command.ts
|
|
2965
|
+
import os3 from "os";
|
|
2966
|
+
import { spawn as spawn2 } from "child_process";
|
|
2967
|
+
import { v4 as uuidv42 } from "uuid";
|
|
2968
|
+
var runningCommands = /* @__PURE__ */ new Map();
|
|
2969
|
+
var MAX_OUTPUT_SIZE2 = 1e5;
|
|
2970
|
+
var MAX_STORED_COMMANDS = 50;
|
|
2971
|
+
var OUTPUT_TRUNCATION_MSG = "\n[OUTPUT TRUNCATED]";
|
|
2972
|
+
var DANGEROUS_PATTERNS2 = [
|
|
2973
|
+
/^sudo\s+/i,
|
|
2974
|
+
/^doas\s+/i,
|
|
2975
|
+
/^su\s+/i,
|
|
2976
|
+
/\|\s*sudo\s+/i
|
|
2977
|
+
];
|
|
2978
|
+
var INTERACTIVE_PATTERNS2 = [
|
|
2979
|
+
/^(vim|vi|nano|emacs|less|more)\s*/i,
|
|
2980
|
+
/^(top|htop|btop)\s*/i,
|
|
2981
|
+
/^(mysql|psql|redis-cli|mongo)\s*$/i
|
|
2982
|
+
];
|
|
2983
|
+
function cleanupOldCommands() {
|
|
2984
|
+
if (runningCommands.size <= MAX_STORED_COMMANDS) return;
|
|
2985
|
+
const commands = Array.from(runningCommands.entries()).filter(([_, cmd]) => cmd.status !== "running").sort((a, b) => (a[1].endTime || 0) - (b[1].endTime || 0));
|
|
2986
|
+
const toRemove = commands.slice(0, commands.length - MAX_STORED_COMMANDS + 10);
|
|
2987
|
+
for (const [id] of toRemove) {
|
|
2988
|
+
runningCommands.delete(id);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
function isDangerousCommand(command) {
|
|
2992
|
+
const trimmed = command.trim();
|
|
2993
|
+
for (const pattern of DANGEROUS_PATTERNS2) {
|
|
2994
|
+
if (pattern.test(trimmed)) {
|
|
2995
|
+
return "Command requires elevated privileges (sudo/doas) which cannot be handled in async mode";
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
for (const pattern of INTERACTIVE_PATTERNS2) {
|
|
2999
|
+
if (pattern.test(trimmed)) {
|
|
3000
|
+
return "Interactive commands are not supported in async mode";
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return null;
|
|
3004
|
+
}
|
|
3005
|
+
async function runCommandAsync(args) {
|
|
3006
|
+
try {
|
|
3007
|
+
const {
|
|
3008
|
+
command,
|
|
3009
|
+
cwd = process.cwd(),
|
|
3010
|
+
timeout = 0
|
|
3011
|
+
} = args;
|
|
3012
|
+
if (!command || typeof command !== "string") {
|
|
3013
|
+
return {
|
|
3014
|
+
success: false,
|
|
3015
|
+
error: "command is required and must be a string"
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
const dangerReason = isDangerousCommand(command);
|
|
3019
|
+
if (dangerReason) {
|
|
3020
|
+
return {
|
|
3021
|
+
success: false,
|
|
3022
|
+
error: dangerReason
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
const commandId = uuidv42().substring(0, 8);
|
|
3026
|
+
const platform = os3.platform();
|
|
3027
|
+
let shellCmd;
|
|
3028
|
+
let shellArgs;
|
|
3029
|
+
if (platform === "win32") {
|
|
3030
|
+
shellCmd = process.env.COMSPEC || "cmd.exe";
|
|
3031
|
+
shellArgs = ["/c", command];
|
|
3032
|
+
} else {
|
|
3033
|
+
shellCmd = process.env.SHELL || "/bin/bash";
|
|
3034
|
+
shellArgs = ["-c", command];
|
|
3035
|
+
}
|
|
3036
|
+
const entry = {
|
|
3037
|
+
id: commandId,
|
|
3038
|
+
command,
|
|
3039
|
+
status: "running",
|
|
3040
|
+
stdout: "",
|
|
3041
|
+
stderr: "",
|
|
3042
|
+
exitCode: null,
|
|
3043
|
+
startTime: Date.now(),
|
|
3044
|
+
process: null
|
|
3045
|
+
};
|
|
3046
|
+
const child = spawn2(shellCmd, shellArgs, {
|
|
3047
|
+
cwd,
|
|
3048
|
+
env: process.env,
|
|
3049
|
+
windowsHide: true
|
|
3050
|
+
});
|
|
3051
|
+
entry.process = child;
|
|
3052
|
+
let stdoutTruncated = false;
|
|
3053
|
+
child.stdout?.on("data", (data) => {
|
|
3054
|
+
if (!stdoutTruncated) {
|
|
3055
|
+
entry.stdout += data.toString();
|
|
3056
|
+
if (entry.stdout.length > MAX_OUTPUT_SIZE2) {
|
|
3057
|
+
entry.stdout = entry.stdout.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MSG;
|
|
3058
|
+
stdoutTruncated = true;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
let stderrTruncated = false;
|
|
3063
|
+
child.stderr?.on("data", (data) => {
|
|
3064
|
+
if (!stderrTruncated) {
|
|
3065
|
+
entry.stderr += data.toString();
|
|
3066
|
+
if (entry.stderr.length > MAX_OUTPUT_SIZE2) {
|
|
3067
|
+
entry.stderr = entry.stderr.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MSG;
|
|
3068
|
+
stderrTruncated = true;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
});
|
|
3072
|
+
child.on("close", (code) => {
|
|
3073
|
+
entry.exitCode = code;
|
|
3074
|
+
entry.status = code === 0 ? "completed" : "error";
|
|
3075
|
+
entry.endTime = Date.now();
|
|
3076
|
+
entry.process = null;
|
|
3077
|
+
});
|
|
3078
|
+
child.on("error", (error) => {
|
|
3079
|
+
entry.stderr += `
|
|
3080
|
+
Process error: ${error.message}`;
|
|
3081
|
+
entry.status = "error";
|
|
3082
|
+
entry.endTime = Date.now();
|
|
3083
|
+
entry.process = null;
|
|
3084
|
+
});
|
|
3085
|
+
if (timeout > 0) {
|
|
3086
|
+
setTimeout(() => {
|
|
3087
|
+
if (entry.status === "running") {
|
|
3088
|
+
child.kill("SIGTERM");
|
|
3089
|
+
setTimeout(() => {
|
|
3090
|
+
if (entry.status === "running") {
|
|
3091
|
+
child.kill("SIGKILL");
|
|
3092
|
+
}
|
|
3093
|
+
}, 2e3);
|
|
3094
|
+
entry.status = "timeout";
|
|
3095
|
+
entry.stderr += `
|
|
3096
|
+
Command timed out after ${timeout} seconds`;
|
|
3097
|
+
}
|
|
3098
|
+
}, timeout * 1e3);
|
|
3099
|
+
}
|
|
3100
|
+
runningCommands.set(commandId, entry);
|
|
3101
|
+
cleanupOldCommands();
|
|
3102
|
+
return {
|
|
3103
|
+
success: true,
|
|
3104
|
+
command_id: commandId,
|
|
3105
|
+
command,
|
|
3106
|
+
message: `Command started in background. Use command_status with id "${commandId}" to check progress.`
|
|
3107
|
+
};
|
|
3108
|
+
} catch (error) {
|
|
3109
|
+
return {
|
|
3110
|
+
success: false,
|
|
3111
|
+
error: `Unexpected error: ${error.message}`
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
async function commandStatus(args) {
|
|
3116
|
+
try {
|
|
3117
|
+
const {
|
|
3118
|
+
command_id,
|
|
3119
|
+
wait_seconds = 0,
|
|
3120
|
+
output_limit = 1e4
|
|
3121
|
+
} = args;
|
|
3122
|
+
if (!command_id) {
|
|
3123
|
+
return {
|
|
3124
|
+
success: false,
|
|
3125
|
+
command_id: "",
|
|
3126
|
+
status: "error",
|
|
3127
|
+
error: "command_id is required"
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
const entry = runningCommands.get(command_id);
|
|
3131
|
+
if (!entry) {
|
|
3132
|
+
return {
|
|
3133
|
+
success: false,
|
|
3134
|
+
command_id,
|
|
3135
|
+
status: "not_found",
|
|
3136
|
+
error: `Command with id "${command_id}" not found. It may have expired or never existed.`
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
if (wait_seconds > 0 && entry.status === "running") {
|
|
3140
|
+
await new Promise((resolve) => {
|
|
3141
|
+
const checkInterval = setInterval(() => {
|
|
3142
|
+
if (entry.status !== "running") {
|
|
3143
|
+
clearInterval(checkInterval);
|
|
3144
|
+
resolve();
|
|
3145
|
+
}
|
|
3146
|
+
}, 100);
|
|
3147
|
+
setTimeout(() => {
|
|
3148
|
+
clearInterval(checkInterval);
|
|
3149
|
+
resolve();
|
|
3150
|
+
}, wait_seconds * 1e3);
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
let stdout = entry.stdout;
|
|
3154
|
+
let stderr = entry.stderr;
|
|
3155
|
+
let truncated = false;
|
|
3156
|
+
if (stdout.length > output_limit) {
|
|
3157
|
+
stdout = "..." + stdout.substring(stdout.length - output_limit);
|
|
3158
|
+
truncated = true;
|
|
3159
|
+
}
|
|
3160
|
+
if (stderr.length > output_limit) {
|
|
3161
|
+
stderr = "..." + stderr.substring(stderr.length - output_limit);
|
|
3162
|
+
truncated = true;
|
|
3163
|
+
}
|
|
3164
|
+
const duration = entry.endTime ? (entry.endTime - entry.startTime) / 1e3 : (Date.now() - entry.startTime) / 1e3;
|
|
3165
|
+
return {
|
|
3166
|
+
success: true,
|
|
3167
|
+
command_id,
|
|
3168
|
+
status: entry.status,
|
|
3169
|
+
stdout: stdout || void 0,
|
|
3170
|
+
stderr: stderr || void 0,
|
|
3171
|
+
exit_code: entry.exitCode,
|
|
3172
|
+
duration_seconds: Math.round(duration * 10) / 10,
|
|
3173
|
+
truncated
|
|
3174
|
+
};
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
return {
|
|
3177
|
+
success: false,
|
|
3178
|
+
command_id: args.command_id || "",
|
|
3179
|
+
status: "error",
|
|
3180
|
+
error: `Unexpected error: ${error.message}`
|
|
3181
|
+
};
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
async function sendCommandInput(args) {
|
|
3185
|
+
try {
|
|
3186
|
+
const { command_id, input } = args;
|
|
3187
|
+
if (!command_id || input === void 0) {
|
|
3188
|
+
return {
|
|
3189
|
+
success: false,
|
|
3190
|
+
error: "command_id and input are required"
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
const entry = runningCommands.get(command_id);
|
|
3194
|
+
if (!entry) {
|
|
3195
|
+
return {
|
|
3196
|
+
success: false,
|
|
3197
|
+
error: `Command with id "${command_id}" not found`
|
|
3198
|
+
};
|
|
3199
|
+
}
|
|
3200
|
+
if (entry.status !== "running" || !entry.process) {
|
|
3201
|
+
return {
|
|
3202
|
+
success: false,
|
|
3203
|
+
error: `Command is not running (status: ${entry.status})`
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
entry.process.stdin?.write(input);
|
|
3207
|
+
return {
|
|
3208
|
+
success: true,
|
|
3209
|
+
message: `Sent ${input.length} characters to command ${command_id}`
|
|
3210
|
+
};
|
|
3211
|
+
} catch (error) {
|
|
3212
|
+
return {
|
|
3213
|
+
success: false,
|
|
3214
|
+
error: `Failed to send input: ${error.message}`
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
async function killCommand(args) {
|
|
3219
|
+
try {
|
|
3220
|
+
const { command_id } = args;
|
|
3221
|
+
const entry = runningCommands.get(command_id);
|
|
3222
|
+
if (!entry) {
|
|
3223
|
+
return {
|
|
3224
|
+
success: false,
|
|
3225
|
+
error: `Command with id "${command_id}" not found`
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
if (entry.status !== "running" || !entry.process) {
|
|
3229
|
+
return {
|
|
3230
|
+
success: false,
|
|
3231
|
+
error: `Command is not running (status: ${entry.status})`
|
|
3232
|
+
};
|
|
3233
|
+
}
|
|
3234
|
+
entry.process.kill("SIGTERM");
|
|
3235
|
+
entry.status = "killed";
|
|
3236
|
+
entry.endTime = Date.now();
|
|
3237
|
+
return {
|
|
3238
|
+
success: true,
|
|
3239
|
+
message: `Command ${command_id} killed`
|
|
3240
|
+
};
|
|
3241
|
+
} catch (error) {
|
|
3242
|
+
return {
|
|
3243
|
+
success: false,
|
|
3244
|
+
error: `Failed to kill command: ${error.message}`
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
// src/app/agent/tools/natives/task_boundary.ts
|
|
3250
|
+
import path9 from "path";
|
|
3251
|
+
import { promises as fs7 } from "fs";
|
|
3252
|
+
import os4 from "os";
|
|
3253
|
+
var currentTask = null;
|
|
3254
|
+
var artifactsDir = null;
|
|
3255
|
+
async function getArtifactsDir() {
|
|
3256
|
+
if (artifactsDir) return artifactsDir;
|
|
3257
|
+
const homeDir = os4.homedir();
|
|
3258
|
+
const baseDir = path9.join(homeDir, ".bluma", "artifacts");
|
|
3259
|
+
const sessionId2 = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
3260
|
+
artifactsDir = path9.join(baseDir, sessionId2);
|
|
3261
|
+
await fs7.mkdir(artifactsDir, { recursive: true });
|
|
3262
|
+
return artifactsDir;
|
|
3263
|
+
}
|
|
3264
|
+
async function updateTaskFile(task) {
|
|
3265
|
+
const dir = await getArtifactsDir();
|
|
3266
|
+
const taskFile = path9.join(dir, "task.md");
|
|
3267
|
+
const content = `# ${task.taskName}
|
|
3268
|
+
|
|
3269
|
+
**Mode:** ${task.mode}
|
|
3270
|
+
**Status:** ${task.status}
|
|
3271
|
+
**Started:** ${new Date(task.startTime).toISOString()}
|
|
3272
|
+
**Updated:** ${new Date(task.updateTime).toISOString()}
|
|
3273
|
+
**Steps:** ${task.stepCount}
|
|
3274
|
+
|
|
3275
|
+
## Summary
|
|
3276
|
+
${task.summary}
|
|
3277
|
+
`;
|
|
3278
|
+
await fs7.writeFile(taskFile, content, "utf-8");
|
|
3279
|
+
}
|
|
3280
|
+
async function taskBoundary(args) {
|
|
3281
|
+
try {
|
|
3282
|
+
const { task_name, mode, task_status, task_summary } = args;
|
|
3283
|
+
if (!task_name || typeof task_name !== "string") {
|
|
3284
|
+
return {
|
|
3285
|
+
success: false,
|
|
3286
|
+
task_name: "",
|
|
3287
|
+
mode: "EXECUTION",
|
|
3288
|
+
status: "",
|
|
3289
|
+
message: "task_name is required"
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
if (!["PLANNING", "EXECUTION", "VERIFICATION"].includes(mode)) {
|
|
3293
|
+
return {
|
|
3294
|
+
success: false,
|
|
3295
|
+
task_name,
|
|
3296
|
+
mode: "EXECUTION",
|
|
3297
|
+
status: "",
|
|
3298
|
+
message: `Invalid mode: ${mode}. Must be PLANNING, EXECUTION, or VERIFICATION`
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
if (!task_status || typeof task_status !== "string") {
|
|
3302
|
+
return {
|
|
3303
|
+
success: false,
|
|
3304
|
+
task_name,
|
|
3305
|
+
mode,
|
|
3306
|
+
status: "",
|
|
3307
|
+
message: "task_status is required"
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
const now = Date.now();
|
|
3311
|
+
if (currentTask && currentTask.taskName === task_name) {
|
|
3312
|
+
currentTask.mode = mode;
|
|
3313
|
+
currentTask.status = task_status;
|
|
3314
|
+
currentTask.summary = task_summary || currentTask.summary;
|
|
3315
|
+
currentTask.updateTime = now;
|
|
3316
|
+
currentTask.stepCount++;
|
|
3317
|
+
} else {
|
|
3318
|
+
currentTask = {
|
|
3319
|
+
taskName: task_name,
|
|
3320
|
+
mode,
|
|
3321
|
+
status: task_status,
|
|
3322
|
+
summary: task_summary || "",
|
|
3323
|
+
startTime: now,
|
|
3324
|
+
updateTime: now,
|
|
3325
|
+
stepCount: 1
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
await updateTaskFile(currentTask);
|
|
3329
|
+
const dir = await getArtifactsDir();
|
|
3330
|
+
return {
|
|
3331
|
+
success: true,
|
|
3332
|
+
task_name: currentTask.taskName,
|
|
3333
|
+
mode: currentTask.mode,
|
|
3334
|
+
status: currentTask.status,
|
|
3335
|
+
message: `Task "${task_name}" is now in ${mode} mode. Status: ${task_status}`,
|
|
3336
|
+
artifacts_dir: dir
|
|
3337
|
+
};
|
|
3338
|
+
} catch (error) {
|
|
3339
|
+
return {
|
|
3340
|
+
success: false,
|
|
3341
|
+
task_name: args.task_name || "",
|
|
3342
|
+
mode: args.mode || "EXECUTION",
|
|
3343
|
+
status: args.task_status || "",
|
|
3344
|
+
message: `Error: ${error.message}`
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
// src/app/agent/tools/natives/search_web.ts
|
|
3350
|
+
import https from "https";
|
|
3351
|
+
import http from "http";
|
|
3352
|
+
var DEFAULT_SOURCES = ["reddit", "github", "stackoverflow"];
|
|
3353
|
+
var MAX_RESULTS_DEFAULT = 10;
|
|
3354
|
+
var REQUEST_TIMEOUT = 1e4;
|
|
3355
|
+
function httpGet(url, customHeaders) {
|
|
3356
|
+
return new Promise((resolve, reject) => {
|
|
3357
|
+
const protocol = url.startsWith("https") ? https : http;
|
|
3358
|
+
const defaultHeaders = {
|
|
3359
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
3360
|
+
"Accept": "application/json, text/html, */*",
|
|
3361
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
3362
|
+
"Accept-Encoding": "identity",
|
|
3363
|
+
// Não usar gzip para simplificar
|
|
3364
|
+
"Cache-Control": "no-cache",
|
|
3365
|
+
"Connection": "keep-alive",
|
|
3366
|
+
...customHeaders
|
|
3367
|
+
};
|
|
3368
|
+
const req = protocol.get(url, {
|
|
3369
|
+
headers: defaultHeaders,
|
|
3370
|
+
timeout: REQUEST_TIMEOUT
|
|
3371
|
+
}, (res) => {
|
|
3372
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
3373
|
+
httpGet(res.headers.location, customHeaders).then(resolve).catch(reject);
|
|
3374
|
+
return;
|
|
3375
|
+
}
|
|
3376
|
+
let data = "";
|
|
3377
|
+
res.on("data", (chunk) => data += chunk);
|
|
3378
|
+
res.on("end", () => {
|
|
3379
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
3380
|
+
resolve(data);
|
|
3381
|
+
} else {
|
|
3382
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data.substring(0, 200)}`));
|
|
3383
|
+
}
|
|
3384
|
+
});
|
|
3385
|
+
});
|
|
3386
|
+
req.on("error", reject);
|
|
3387
|
+
req.on("timeout", () => {
|
|
3388
|
+
req.destroy();
|
|
3389
|
+
reject(new Error("Request timeout"));
|
|
3390
|
+
});
|
|
3391
|
+
});
|
|
3392
|
+
}
|
|
3393
|
+
async function searchReddit(query, limit) {
|
|
3394
|
+
const results = [];
|
|
3395
|
+
try {
|
|
3396
|
+
const subreddits = "programming+webdev+javascript+typescript+python+node+reactjs+learnprogramming";
|
|
3397
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3398
|
+
const url = `https://www.reddit.com/r/${subreddits}/search.json?q=${encodedQuery}&sort=relevance&limit=${limit}&restrict_sr=on`;
|
|
3399
|
+
const response = await httpGet(url);
|
|
3400
|
+
const data = JSON.parse(response);
|
|
3401
|
+
if (data.data?.children) {
|
|
3402
|
+
for (const child of data.data.children.slice(0, limit)) {
|
|
3403
|
+
const post = child.data;
|
|
3404
|
+
results.push({
|
|
3405
|
+
title: post.title || "",
|
|
3406
|
+
url: `https://reddit.com${post.permalink}`,
|
|
3407
|
+
source: "reddit",
|
|
3408
|
+
snippet: post.selftext?.substring(0, 200) || `r/${post.subreddit} - ${post.score} upvotes`,
|
|
3409
|
+
score: post.score
|
|
3410
|
+
});
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
} catch (error) {
|
|
3414
|
+
console.error(`[search_web] Reddit error: ${error.message}`);
|
|
3415
|
+
}
|
|
3416
|
+
return results;
|
|
3417
|
+
}
|
|
3418
|
+
async function searchGitHub(query, limit) {
|
|
3419
|
+
const results = [];
|
|
3420
|
+
try {
|
|
3421
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3422
|
+
const url = `https://api.github.com/search/issues?q=${encodedQuery}+is:issue&sort=reactions&order=desc&per_page=${limit}`;
|
|
3423
|
+
const response = await httpGet(url);
|
|
3424
|
+
const data = JSON.parse(response);
|
|
3425
|
+
if (data.items) {
|
|
3426
|
+
for (const item of data.items.slice(0, limit)) {
|
|
3427
|
+
results.push({
|
|
3428
|
+
title: item.title || "",
|
|
3429
|
+
url: item.html_url || "",
|
|
3430
|
+
source: "github",
|
|
3431
|
+
snippet: item.body?.substring(0, 200) || `${item.comments} comments`,
|
|
3432
|
+
score: item.reactions?.total_count || 0
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
} catch (error) {
|
|
3437
|
+
console.error(`[search_web] GitHub error: ${error.message}`);
|
|
3438
|
+
}
|
|
3439
|
+
return results;
|
|
3440
|
+
}
|
|
3441
|
+
async function searchStackOverflow(query, limit) {
|
|
3442
|
+
const results = [];
|
|
3443
|
+
try {
|
|
3444
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3445
|
+
const url = `https://api.stackexchange.com/2.3/search?order=desc&sort=relevance&intitle=${encodedQuery}&site=stackoverflow&pagesize=${limit}`;
|
|
3446
|
+
const response = await httpGet(url);
|
|
3447
|
+
const data = JSON.parse(response);
|
|
3448
|
+
if (data.items) {
|
|
3449
|
+
for (const item of data.items.slice(0, limit)) {
|
|
3450
|
+
results.push({
|
|
3451
|
+
title: item.title || "",
|
|
3452
|
+
url: item.link || "",
|
|
3453
|
+
source: "stackoverflow",
|
|
3454
|
+
snippet: `${item.score} votes, ${item.answer_count} answers${item.is_answered ? " \u2713" : ""}`,
|
|
3455
|
+
score: item.score
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
} catch (error) {
|
|
3460
|
+
console.error(`[search_web] StackOverflow error: ${error.message}`);
|
|
3461
|
+
}
|
|
3462
|
+
return results;
|
|
3463
|
+
}
|
|
3464
|
+
function getXSearchUrl(query) {
|
|
3465
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3466
|
+
return {
|
|
3467
|
+
title: `Search X for: "${query}"`,
|
|
3468
|
+
url: `https://twitter.com/search?q=${encodedQuery}&src=typed_query&f=live`,
|
|
3469
|
+
source: "x",
|
|
3470
|
+
snippet: "X/Twitter requires authentication. Click the link to search manually."
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
async function searchWeb(args) {
|
|
3474
|
+
try {
|
|
3475
|
+
const {
|
|
3476
|
+
query,
|
|
3477
|
+
sources = DEFAULT_SOURCES,
|
|
3478
|
+
max_results = MAX_RESULTS_DEFAULT
|
|
3479
|
+
} = args;
|
|
3480
|
+
if (!query || typeof query !== "string") {
|
|
3481
|
+
return {
|
|
3482
|
+
success: false,
|
|
3483
|
+
query: query || "",
|
|
3484
|
+
results: [],
|
|
3485
|
+
sources_searched: [],
|
|
3486
|
+
total_results: 0,
|
|
3487
|
+
error: "query is required and must be a string"
|
|
3488
|
+
};
|
|
3489
|
+
}
|
|
3490
|
+
const allResults = [];
|
|
3491
|
+
const sourcesSearched = [];
|
|
3492
|
+
const resultsPerSource = Math.ceil(max_results / sources.length);
|
|
3493
|
+
const searches = [];
|
|
3494
|
+
for (const source of sources) {
|
|
3495
|
+
sourcesSearched.push(source);
|
|
3496
|
+
switch (source) {
|
|
3497
|
+
case "reddit":
|
|
3498
|
+
searches.push(searchReddit(query, resultsPerSource));
|
|
3499
|
+
break;
|
|
3500
|
+
case "github":
|
|
3501
|
+
searches.push(searchGitHub(query, resultsPerSource));
|
|
3502
|
+
break;
|
|
3503
|
+
case "stackoverflow":
|
|
3504
|
+
searches.push(searchStackOverflow(query, resultsPerSource));
|
|
3505
|
+
break;
|
|
3506
|
+
case "x":
|
|
3507
|
+
allResults.push(getXSearchUrl(query));
|
|
3508
|
+
break;
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
const searchResults = await Promise.all(searches);
|
|
3512
|
+
for (const results of searchResults) {
|
|
3513
|
+
allResults.push(...results);
|
|
3514
|
+
}
|
|
3515
|
+
allResults.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
3516
|
+
const limitedResults = allResults.slice(0, max_results);
|
|
3517
|
+
return {
|
|
3518
|
+
success: true,
|
|
3519
|
+
query,
|
|
3520
|
+
results: limitedResults,
|
|
3521
|
+
sources_searched: sourcesSearched,
|
|
3522
|
+
total_results: limitedResults.length,
|
|
3523
|
+
note: sources.includes("x") ? "X/Twitter requires manual search due to API restrictions" : void 0
|
|
3524
|
+
};
|
|
3525
|
+
} catch (error) {
|
|
3526
|
+
return {
|
|
3527
|
+
success: false,
|
|
3528
|
+
query: args.query || "",
|
|
3529
|
+
results: [],
|
|
3530
|
+
sources_searched: [],
|
|
3531
|
+
total_results: 0,
|
|
3532
|
+
error: `Unexpected error: ${error.message}`
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
// src/app/agent/tool_invoker.ts
|
|
3538
|
+
var ToolInvoker = class {
|
|
3539
|
+
// Mapa privado para associar nomes de ferramentas às suas funções de implementação.
|
|
3540
|
+
toolImplementations;
|
|
3541
|
+
// Propriedade privada para armazenar as definições de ferramentas carregadas do JSON.
|
|
3542
|
+
toolDefinitions = [];
|
|
3543
|
+
constructor() {
|
|
3544
|
+
this.toolImplementations = /* @__PURE__ */ new Map();
|
|
3545
|
+
this.registerTools();
|
|
3546
|
+
}
|
|
3547
|
+
/**
|
|
3548
|
+
* Carrega as definições de ferramentas do arquivo de configuração `native_tools.json`.
|
|
3549
|
+
* Este método é assíncrono e deve ser chamado após a criação da instância.
|
|
3550
|
+
*/
|
|
3551
|
+
async initialize() {
|
|
3552
|
+
try {
|
|
3553
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
3554
|
+
const __dirname = path10.dirname(__filename);
|
|
3555
|
+
const configPath = path10.resolve(__dirname, "config", "native_tools.json");
|
|
3556
|
+
const fileContent = await fs8.readFile(configPath, "utf-8");
|
|
3557
|
+
const config2 = JSON.parse(fileContent);
|
|
3558
|
+
this.toolDefinitions = config2.nativeTools;
|
|
3559
|
+
} catch (error) {
|
|
3560
|
+
console.error("[ToolInvoker] Erro cr\xEDtico ao carregar 'native_tools.json'. As ferramentas nativas n\xE3o estar\xE3o dispon\xEDveis.", error);
|
|
3561
|
+
this.toolDefinitions = [];
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
/**
|
|
3565
|
+
* Registra as implementações de todas as ferramentas nativas.
|
|
3566
|
+
* Este método mapeia o nome da ferramenta (string) para a função TypeScript que a executa.
|
|
3567
|
+
*/
|
|
3568
|
+
registerTools() {
|
|
3569
|
+
this.toolImplementations.set("shell_command", shellCommand);
|
|
3570
|
+
this.toolImplementations.set("edit_tool", editTool);
|
|
3571
|
+
this.toolImplementations.set("ls_tool", ls);
|
|
3572
|
+
this.toolImplementations.set("count_file_lines", countLines);
|
|
3573
|
+
this.toolImplementations.set("read_file_lines", readLines);
|
|
3574
|
+
this.toolImplementations.set("find_by_name", findByName);
|
|
3575
|
+
this.toolImplementations.set("grep_search", grepSearch);
|
|
3576
|
+
this.toolImplementations.set("view_file_outline", viewFileOutline);
|
|
3577
|
+
this.toolImplementations.set("run_command_async", runCommandAsync);
|
|
3578
|
+
this.toolImplementations.set("command_status", commandStatus);
|
|
3579
|
+
this.toolImplementations.set("send_command_input", sendCommandInput);
|
|
3580
|
+
this.toolImplementations.set("kill_command", killCommand);
|
|
3581
|
+
this.toolImplementations.set("message_notify_user", messageNotifyuser);
|
|
3582
|
+
this.toolImplementations.set("todo", todo);
|
|
3583
|
+
this.toolImplementations.set("task_boundary", taskBoundary);
|
|
3584
|
+
this.toolImplementations.set("search_web", searchWeb);
|
|
3585
|
+
this.toolImplementations.set("agent_end_turn", async () => ({ success: true, message: "Task ended by agent." }));
|
|
3586
|
+
}
|
|
3587
|
+
/**
|
|
3588
|
+
* Retorna a lista de definições de todas as ferramentas nativas carregadas.
|
|
3589
|
+
* O MCPClient usará esta função para obter a lista de ferramentas locais.
|
|
3590
|
+
*/
|
|
3591
|
+
getToolDefinitions() {
|
|
3592
|
+
return this.toolDefinitions;
|
|
3593
|
+
}
|
|
3594
|
+
/**
|
|
3595
|
+
* Invoca uma ferramenta nativa pelo nome com os argumentos fornecidos.
|
|
3596
|
+
* @param toolName O nome da ferramenta a ser invocada.
|
|
3597
|
+
* @param args Os argumentos para a ferramenta, geralmente um objeto.
|
|
3598
|
+
* @returns O resultado da execução da ferramenta.
|
|
3599
|
+
*/
|
|
3600
|
+
async invoke(toolName, args) {
|
|
3601
|
+
const implementation = this.toolImplementations.get(toolName);
|
|
3602
|
+
if (!implementation) {
|
|
3603
|
+
return { error: `Error: Native tool "${toolName}" not found.` };
|
|
3604
|
+
}
|
|
3605
|
+
try {
|
|
3606
|
+
return await implementation(args);
|
|
3607
|
+
} catch (error) {
|
|
3608
|
+
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred.";
|
|
3609
|
+
return { error: `Error executing tool "${toolName}": ${errorMessage}` };
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
};
|
|
3613
|
+
|
|
3614
|
+
// src/app/agent/tools/mcp/mcp_client.ts
|
|
3615
|
+
import { promises as fs9 } from "fs";
|
|
3616
|
+
import path11 from "path";
|
|
3617
|
+
import os5 from "os";
|
|
3618
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3619
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3620
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3621
|
+
var MCPClient = class {
|
|
3622
|
+
sessions = /* @__PURE__ */ new Map();
|
|
3623
|
+
toolToServerMap = /* @__PURE__ */ new Map();
|
|
3624
|
+
globalToolsForLlm = [];
|
|
3625
|
+
nativeToolInvoker;
|
|
3626
|
+
eventBus;
|
|
3627
|
+
// <<< ADICIONA A PROPRIEDADE
|
|
3628
|
+
constructor(nativeToolInvoker, eventBus2) {
|
|
3629
|
+
this.nativeToolInvoker = nativeToolInvoker;
|
|
3630
|
+
this.eventBus = eventBus2;
|
|
3631
|
+
}
|
|
3632
|
+
// ... (método initialize inalterado) ...
|
|
3633
|
+
async initialize() {
|
|
3634
|
+
const nativeTools = this.nativeToolInvoker.getToolDefinitions();
|
|
3635
|
+
this.globalToolsForLlm.push(...nativeTools);
|
|
3636
|
+
for (const tool of nativeTools) {
|
|
3637
|
+
const toolName = tool.function.name;
|
|
3638
|
+
this.toolToServerMap.set(toolName, {
|
|
3639
|
+
server: "native",
|
|
3640
|
+
originalName: toolName
|
|
3641
|
+
});
|
|
3642
|
+
}
|
|
3643
|
+
const __filename = fileURLToPath2(import.meta.url);
|
|
3644
|
+
const __dirname = path11.dirname(__filename);
|
|
3645
|
+
const defaultConfigPath = path11.resolve(__dirname, "config", "bluma-mcp.json");
|
|
3646
|
+
const userConfigPath = path11.join(os5.homedir(), ".bluma-cli", "bluma-mcp.json");
|
|
3647
|
+
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
3648
|
+
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
3649
|
+
const mergedConfig = {
|
|
3650
|
+
mcpServers: {
|
|
3651
|
+
...defaultConfig.mcpServers || {},
|
|
3652
|
+
...userConfig.mcpServers || {}
|
|
3653
|
+
}
|
|
3654
|
+
};
|
|
3655
|
+
if (Object.keys(mergedConfig.mcpServers).length === 0) {
|
|
3656
|
+
return;
|
|
3657
|
+
}
|
|
3658
|
+
const serverEntries = Object.entries(mergedConfig.mcpServers);
|
|
3659
|
+
for (const [serverName, serverConf] of serverEntries) {
|
|
3660
|
+
try {
|
|
3661
|
+
this.eventBus.emit("backend_message", {
|
|
3662
|
+
type: "connection_status",
|
|
3663
|
+
message: `${serverName} server is being connected...`
|
|
3664
|
+
});
|
|
3665
|
+
if (serverConf.type === "stdio") {
|
|
3666
|
+
await this.connectToStdioServer(serverName, serverConf);
|
|
3667
|
+
} else if (serverConf.type === "sse") {
|
|
3668
|
+
console.warn(`[MCPClient] Conex\xE3o com servidores SSE (como '${serverName}') ainda n\xE3o implementada.`);
|
|
3669
|
+
}
|
|
3670
|
+
} catch (error) {
|
|
3671
|
+
this.eventBus.emit("backend_message", {
|
|
3672
|
+
type: "error",
|
|
3673
|
+
message: `Failed to connect to server '${serverName}'.`
|
|
3674
|
+
});
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
async loadMcpConfig(configPath, configType) {
|
|
3679
|
+
try {
|
|
3680
|
+
const fileContent = await fs9.readFile(configPath, "utf-8");
|
|
3681
|
+
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
2021
3682
|
return JSON.parse(processedContent);
|
|
2022
3683
|
} catch (error) {
|
|
2023
3684
|
if (error.code === "ENOENT") {
|
|
@@ -2035,7 +3696,7 @@ var MCPClient = class {
|
|
|
2035
3696
|
async connectToStdioServer(serverName, config2) {
|
|
2036
3697
|
let commandToExecute = config2.command;
|
|
2037
3698
|
let argsToExecute = config2.args || [];
|
|
2038
|
-
const isWindows =
|
|
3699
|
+
const isWindows = os5.platform() === "win32";
|
|
2039
3700
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
2040
3701
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
2041
3702
|
commandToExecute = argsToExecute[1];
|
|
@@ -2151,12 +3812,12 @@ var AdvancedFeedbackSystem = class {
|
|
|
2151
3812
|
};
|
|
2152
3813
|
|
|
2153
3814
|
// src/app/agent/bluma/core/bluma.ts
|
|
2154
|
-
import
|
|
3815
|
+
import path14 from "path";
|
|
2155
3816
|
|
|
2156
|
-
// src/app/agent/
|
|
2157
|
-
import
|
|
2158
|
-
import
|
|
2159
|
-
import { promises as
|
|
3817
|
+
// src/app/agent/session_manager/session_manager.ts
|
|
3818
|
+
import path12 from "path";
|
|
3819
|
+
import os6 from "os";
|
|
3820
|
+
import { promises as fs10 } from "fs";
|
|
2160
3821
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
2161
3822
|
async function withFileLock(file, fn) {
|
|
2162
3823
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -2174,13 +3835,13 @@ async function withFileLock(file, fn) {
|
|
|
2174
3835
|
function expandHome(p) {
|
|
2175
3836
|
if (!p) return p;
|
|
2176
3837
|
if (p.startsWith("~")) {
|
|
2177
|
-
return
|
|
3838
|
+
return path12.join(os6.homedir(), p.slice(1));
|
|
2178
3839
|
}
|
|
2179
3840
|
return p;
|
|
2180
3841
|
}
|
|
2181
3842
|
function getPreferredAppDir() {
|
|
2182
|
-
const fixed =
|
|
2183
|
-
return
|
|
3843
|
+
const fixed = path12.join(os6.homedir(), ".bluma-cli");
|
|
3844
|
+
return path12.resolve(expandHome(fixed));
|
|
2184
3845
|
}
|
|
2185
3846
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
2186
3847
|
let attempt = 0;
|
|
@@ -2188,7 +3849,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
2188
3849
|
const isWin = process.platform === "win32";
|
|
2189
3850
|
while (attempt <= maxRetries) {
|
|
2190
3851
|
try {
|
|
2191
|
-
await
|
|
3852
|
+
await fs10.rename(src, dest);
|
|
2192
3853
|
return;
|
|
2193
3854
|
} catch (e) {
|
|
2194
3855
|
lastErr = e;
|
|
@@ -2201,9 +3862,9 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
2201
3862
|
}
|
|
2202
3863
|
}
|
|
2203
3864
|
try {
|
|
2204
|
-
const data = await
|
|
2205
|
-
await
|
|
2206
|
-
await
|
|
3865
|
+
const data = await fs10.readFile(src);
|
|
3866
|
+
await fs10.writeFile(dest, data);
|
|
3867
|
+
await fs10.unlink(src).catch(() => {
|
|
2207
3868
|
});
|
|
2208
3869
|
return;
|
|
2209
3870
|
} catch (fallbackErr) {
|
|
@@ -2212,16 +3873,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
2212
3873
|
}
|
|
2213
3874
|
async function ensureSessionDir() {
|
|
2214
3875
|
const appDir = getPreferredAppDir();
|
|
2215
|
-
const sessionDir =
|
|
2216
|
-
await
|
|
3876
|
+
const sessionDir = path12.join(appDir, "sessions");
|
|
3877
|
+
await fs10.mkdir(sessionDir, { recursive: true });
|
|
2217
3878
|
return sessionDir;
|
|
2218
3879
|
}
|
|
2219
3880
|
async function loadOrcreateSession(sessionId2) {
|
|
2220
3881
|
const sessionDir = await ensureSessionDir();
|
|
2221
|
-
const sessionFile =
|
|
3882
|
+
const sessionFile = path12.join(sessionDir, `${sessionId2}.json`);
|
|
2222
3883
|
try {
|
|
2223
|
-
await
|
|
2224
|
-
const fileContent = await
|
|
3884
|
+
await fs10.access(sessionFile);
|
|
3885
|
+
const fileContent = await fs10.readFile(sessionFile, "utf-8");
|
|
2225
3886
|
const sessionData = JSON.parse(fileContent);
|
|
2226
3887
|
return [sessionFile, [], []];
|
|
2227
3888
|
} catch (error) {
|
|
@@ -2230,7 +3891,7 @@ async function loadOrcreateSession(sessionId2) {
|
|
|
2230
3891
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2231
3892
|
conversation_history: []
|
|
2232
3893
|
};
|
|
2233
|
-
await
|
|
3894
|
+
await fs10.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
2234
3895
|
return [sessionFile, [], []];
|
|
2235
3896
|
}
|
|
2236
3897
|
}
|
|
@@ -2238,12 +3899,12 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2238
3899
|
await withFileLock(sessionFile, async () => {
|
|
2239
3900
|
let sessionData;
|
|
2240
3901
|
try {
|
|
2241
|
-
const dir =
|
|
2242
|
-
await
|
|
3902
|
+
const dir = path12.dirname(sessionFile);
|
|
3903
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
2243
3904
|
} catch {
|
|
2244
3905
|
}
|
|
2245
3906
|
try {
|
|
2246
|
-
const fileContent = await
|
|
3907
|
+
const fileContent = await fs10.readFile(sessionFile, "utf-8");
|
|
2247
3908
|
sessionData = JSON.parse(fileContent);
|
|
2248
3909
|
} catch (error) {
|
|
2249
3910
|
const code = error && error.code;
|
|
@@ -2254,14 +3915,14 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2254
3915
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
2255
3916
|
}
|
|
2256
3917
|
}
|
|
2257
|
-
const sessionId2 =
|
|
3918
|
+
const sessionId2 = path12.basename(sessionFile, ".json");
|
|
2258
3919
|
sessionData = {
|
|
2259
3920
|
session_id: sessionId2,
|
|
2260
3921
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2261
3922
|
conversation_history: []
|
|
2262
3923
|
};
|
|
2263
3924
|
try {
|
|
2264
|
-
await
|
|
3925
|
+
await fs10.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
2265
3926
|
} catch {
|
|
2266
3927
|
}
|
|
2267
3928
|
}
|
|
@@ -2269,7 +3930,7 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2269
3930
|
sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
2270
3931
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
2271
3932
|
try {
|
|
2272
|
-
await
|
|
3933
|
+
await fs10.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
2273
3934
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
2274
3935
|
} catch (writeError) {
|
|
2275
3936
|
if (writeError instanceof Error) {
|
|
@@ -2278,7 +3939,7 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2278
3939
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
2279
3940
|
}
|
|
2280
3941
|
try {
|
|
2281
|
-
await
|
|
3942
|
+
await fs10.unlink(tempSessionFile);
|
|
2282
3943
|
} catch {
|
|
2283
3944
|
}
|
|
2284
3945
|
}
|
|
@@ -2286,181 +3947,327 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2286
3947
|
}
|
|
2287
3948
|
|
|
2288
3949
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
2289
|
-
import
|
|
2290
|
-
import
|
|
2291
|
-
import
|
|
3950
|
+
import os7 from "os";
|
|
3951
|
+
import fs11 from "fs";
|
|
3952
|
+
import path13 from "path";
|
|
3953
|
+
import { execSync } from "child_process";
|
|
3954
|
+
function getNodeVersion() {
|
|
3955
|
+
try {
|
|
3956
|
+
return process.version;
|
|
3957
|
+
} catch {
|
|
3958
|
+
return "unknown";
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
function getNpmVersion() {
|
|
3962
|
+
try {
|
|
3963
|
+
return execSync("npm --version", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
3964
|
+
} catch {
|
|
3965
|
+
return "unknown";
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
function getGitBranch(dir) {
|
|
3969
|
+
try {
|
|
3970
|
+
return execSync("git rev-parse --abbrev-ref HEAD", {
|
|
3971
|
+
cwd: dir,
|
|
3972
|
+
encoding: "utf-8",
|
|
3973
|
+
timeout: 5e3
|
|
3974
|
+
}).trim();
|
|
3975
|
+
} catch {
|
|
3976
|
+
return "N/A";
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
function getPackageManager(dir) {
|
|
3980
|
+
try {
|
|
3981
|
+
if (fs11.existsSync(path13.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
3982
|
+
if (fs11.existsSync(path13.join(dir, "yarn.lock"))) return "yarn";
|
|
3983
|
+
if (fs11.existsSync(path13.join(dir, "bun.lockb"))) return "bun";
|
|
3984
|
+
if (fs11.existsSync(path13.join(dir, "package-lock.json"))) return "npm";
|
|
3985
|
+
return "unknown";
|
|
3986
|
+
} catch {
|
|
3987
|
+
return "unknown";
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
function getProjectType(dir) {
|
|
3991
|
+
try {
|
|
3992
|
+
const files = fs11.readdirSync(dir);
|
|
3993
|
+
if (files.includes("package.json")) {
|
|
3994
|
+
const pkg = JSON.parse(fs11.readFileSync(path13.join(dir, "package.json"), "utf-8"));
|
|
3995
|
+
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
3996
|
+
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
3997
|
+
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
3998
|
+
if (pkg.dependencies?.vue || pkg.devDependencies?.vue) return "Vue";
|
|
3999
|
+
if (pkg.dependencies?.angular || pkg.devDependencies?.angular) return "Angular";
|
|
4000
|
+
return "Node.js";
|
|
4001
|
+
}
|
|
4002
|
+
if (files.includes("requirements.txt") || files.includes("pyproject.toml")) return "Python";
|
|
4003
|
+
if (files.includes("Cargo.toml")) return "Rust";
|
|
4004
|
+
if (files.includes("go.mod")) return "Go";
|
|
4005
|
+
if (files.includes("pom.xml") || files.includes("build.gradle")) return "Java";
|
|
4006
|
+
return "unknown";
|
|
4007
|
+
} catch {
|
|
4008
|
+
return "unknown";
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
function getTestFramework(dir) {
|
|
4012
|
+
try {
|
|
4013
|
+
const pkgPath = path13.join(dir, "package.json");
|
|
4014
|
+
if (fs11.existsSync(pkgPath)) {
|
|
4015
|
+
const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
|
|
4016
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4017
|
+
if (deps.jest) return "jest";
|
|
4018
|
+
if (deps.vitest) return "vitest";
|
|
4019
|
+
if (deps.mocha) return "mocha";
|
|
4020
|
+
if (deps.ava) return "ava";
|
|
4021
|
+
if (deps["@playwright/test"]) return "playwright";
|
|
4022
|
+
if (deps.cypress) return "cypress";
|
|
4023
|
+
}
|
|
4024
|
+
if (fs11.existsSync(path13.join(dir, "pytest.ini")) || fs11.existsSync(path13.join(dir, "conftest.py"))) return "pytest";
|
|
4025
|
+
return "unknown";
|
|
4026
|
+
} catch {
|
|
4027
|
+
return "unknown";
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
function getTestCommand(dir) {
|
|
4031
|
+
try {
|
|
4032
|
+
const pkgPath = path13.join(dir, "package.json");
|
|
4033
|
+
if (fs11.existsSync(pkgPath)) {
|
|
4034
|
+
const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
|
|
4035
|
+
if (pkg.scripts?.test) return `npm test`;
|
|
4036
|
+
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
4037
|
+
}
|
|
4038
|
+
const pm = getPackageManager(dir);
|
|
4039
|
+
const tf = getTestFramework(dir);
|
|
4040
|
+
if (tf === "pytest") return "pytest";
|
|
4041
|
+
if (tf === "jest") return `${pm} test`;
|
|
4042
|
+
if (tf === "vitest") return `${pm} run test`;
|
|
4043
|
+
return "npm test";
|
|
4044
|
+
} catch {
|
|
4045
|
+
return "npm test";
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
2292
4048
|
var SYSTEM_PROMPT = `
|
|
2293
4049
|
<identity>
|
|
2294
|
-
You are BluMa, an autonomous coding agent by NomadEngenuity.
|
|
2295
|
-
You are a senior peer engineer working
|
|
2296
|
-
|
|
2297
|
-
|
|
4050
|
+
You are BluMa, an autonomous coding agent developed by NomadEngenuity.
|
|
4051
|
+
You are a **senior peer engineer** working alongside {username} - technical, proactive, and collaborative.
|
|
4052
|
+
You know this machine better than anyone. You understand the project structure, dependencies, and conventions.
|
|
4053
|
+
You are not just an assistant - you are a **teammate** who takes ownership of tasks.
|
|
4054
|
+
|
|
4055
|
+
Key traits:
|
|
4056
|
+
- Think like a senior developer who has been on this project for years
|
|
4057
|
+
- Suggest improvements proactively, don't just follow orders blindly
|
|
4058
|
+
- Spot bugs and issues before they become problems
|
|
4059
|
+
- Write tests as naturally as you write code
|
|
4060
|
+
- Always communicate your reasoning and progress
|
|
2298
4061
|
</identity>
|
|
2299
4062
|
|
|
2300
4063
|
---
|
|
2301
4064
|
|
|
2302
|
-
<
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
<strict_constraints>
|
|
2336
|
-
<constraint>The message_notify_user tool is your ONLY channel for communication with the human user.</constraint>
|
|
2337
|
-
<constraint>NEVER assume the user knows what you are doing behind the scenes.</constraint>
|
|
2338
|
-
<constraint>ALWAYS verbalize your reasoning, actions, and intentions before and after execution.</constraint>
|
|
2339
|
-
</strict_constraints>
|
|
2340
|
-
|
|
2341
|
-
<example>
|
|
2342
|
-
<![CDATA[
|
|
2343
|
-
message_notify_user("Acknowledged. I will now create a TODO plan for the requested feature.")
|
|
2344
|
-
message_notify_user("TODO plan created. Next, I will start implementing the authentication module.")
|
|
2345
|
-
message_notify_user("Authentication module implemented. Running tests now.")
|
|
2346
|
-
]]>
|
|
2347
|
-
</example>
|
|
2348
|
-
</message_rules>
|
|
4065
|
+
<environment>
|
|
4066
|
+
## Machine Context (Your Deep Knowledge)
|
|
4067
|
+
|
|
4068
|
+
**System:**
|
|
4069
|
+
- OS: {os_type} ({os_version})
|
|
4070
|
+
- Architecture: {architecture}
|
|
4071
|
+
- Shell: {shell_type}
|
|
4072
|
+
- Node.js: {node_version}
|
|
4073
|
+
- NPM: {npm_version}
|
|
4074
|
+
- User: {username}
|
|
4075
|
+
|
|
4076
|
+
**Project:**
|
|
4077
|
+
- Working Directory: {workdir}
|
|
4078
|
+
- Project Type: {project_type}
|
|
4079
|
+
- Package Manager: {package_manager}
|
|
4080
|
+
- Test Framework: {test_framework}
|
|
4081
|
+
- Test Command: {test_command}
|
|
4082
|
+
|
|
4083
|
+
**Git:**
|
|
4084
|
+
- Is Git Repo: {is_git_repo}
|
|
4085
|
+
- Current Branch: {git_branch}
|
|
4086
|
+
|
|
4087
|
+
**Session:**
|
|
4088
|
+
- Date: {current_date}
|
|
4089
|
+
- Timezone: {timezone}
|
|
4090
|
+
|
|
4091
|
+
You MUST adapt all commands to this environment. Use the correct package manager, test framework, and conventions.
|
|
4092
|
+
</environment>
|
|
4093
|
+
|
|
4094
|
+
---
|
|
4095
|
+
|
|
4096
|
+
<workflow>
|
|
4097
|
+
## How You Work
|
|
2349
4098
|
|
|
2350
|
-
|
|
4099
|
+
### For Multi-Step Tasks:
|
|
4100
|
+
1. **Acknowledge** - Confirm the request immediately via message_notify_user
|
|
4101
|
+
2. **Plan** - Use the TODO tool to create a structured plan
|
|
4102
|
+
3. **Execute** - Implement with clear narration of each step
|
|
4103
|
+
4. **Test** - Run tests after any code changes (MANDATORY for production code)
|
|
4104
|
+
5. **Verify** - Check build, lint, and git status
|
|
4105
|
+
6. **Summarize** - Report results and any follow-up actions
|
|
2351
4106
|
|
|
4107
|
+
### For Simple Tasks:
|
|
4108
|
+
- Quick acknowledgment + immediate action
|
|
4109
|
+
- No TODO needed for single operations
|
|
2352
4110
|
|
|
2353
|
-
|
|
4111
|
+
### Testing Philosophy (CRITICAL):
|
|
4112
|
+
You are a **teammate who writes tests**, not an assistant who skips them.
|
|
2354
4113
|
|
|
2355
|
-
|
|
4114
|
+
**ALWAYS run tests when:**
|
|
4115
|
+
- You modify any function or module
|
|
4116
|
+
- You add new functionality
|
|
4117
|
+
- You fix a bug (write a regression test first)
|
|
4118
|
+
- The user asks for a feature
|
|
2356
4119
|
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
4120
|
+
**Test workflow:**
|
|
4121
|
+
1. Before editing: Run existing tests to ensure baseline passes
|
|
4122
|
+
2. After editing: Run tests to verify nothing broke
|
|
4123
|
+
3. For new features: Write tests alongside implementation
|
|
4124
|
+
4. For bugs: Write failing test \u2192 fix \u2192 verify test passes
|
|
2361
4125
|
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
4126
|
+
**Commands you should know:**
|
|
4127
|
+
- Run tests: {test_command}
|
|
4128
|
+
- Run single test: Check project conventions
|
|
4129
|
+
- Watch mode: Usually \`npm run test:watch\` or \`npm test -- --watch\`
|
|
4130
|
+
</workflow>
|
|
2365
4131
|
|
|
2366
|
-
|
|
2367
|
-
Never address the user using the raw {username} handle.
|
|
4132
|
+
---
|
|
2368
4133
|
|
|
2369
|
-
|
|
2370
|
-
|
|
4134
|
+
<tool_rules>
|
|
4135
|
+
## Tool Calling Best Practices
|
|
2371
4136
|
|
|
2372
|
-
|
|
2373
|
-
-
|
|
2374
|
-
-
|
|
2375
|
-
-
|
|
4137
|
+
### File Operations:
|
|
4138
|
+
- **ALWAYS read a file before editing** - Use read_file_lines or ls_tool first
|
|
4139
|
+
- **Use absolute paths** when possible to avoid ambiguity
|
|
4140
|
+
- **For edit_tool**: Provide exact content with correct whitespace (read first!)
|
|
4141
|
+
- **Check file exists** before attempting edits
|
|
2376
4142
|
|
|
2377
|
-
|
|
4143
|
+
### Shell Commands:
|
|
4144
|
+
- **NEVER use sudo** - Commands requiring sudo will be blocked
|
|
4145
|
+
- **Long commands**: Set appropriate timeout (default 300s)
|
|
4146
|
+
- **Interactive commands** (vim, less, ssh) are blocked - use alternatives
|
|
4147
|
+
- **Large outputs** will be truncated at 100KB
|
|
2378
4148
|
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
4149
|
+
### Safe Auto-Approved Tools (no confirmation needed):
|
|
4150
|
+
- message_notify_user
|
|
4151
|
+
- ls_tool
|
|
4152
|
+
- read_file_lines
|
|
4153
|
+
- count_file_lines
|
|
4154
|
+
- todo
|
|
4155
|
+
- agent_end_turn
|
|
2382
4156
|
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
4157
|
+
### Require Confirmation:
|
|
4158
|
+
- shell_command (except safe commands)
|
|
4159
|
+
- edit_tool (always shows diff preview)
|
|
4160
|
+
</tool_rules>
|
|
2386
4161
|
|
|
2387
|
-
|
|
2388
|
-
Jhon Doe: Redis or Postgres for sessions?
|
|
2389
|
-
Teammate: Jhon Doe, Redis. Low latency, ephemeral data.
|
|
4162
|
+
---
|
|
2390
4163
|
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
Teammate: Rebase your feature on top of dev. Resolve conflicts and force push.
|
|
4164
|
+
<communication_style>
|
|
4165
|
+
## How You Communicate
|
|
2394
4166
|
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
4167
|
+
Convert {username} to a natural name:
|
|
4168
|
+
- Replace "-" "_" "." with spaces
|
|
4169
|
+
- Capitalize each word
|
|
4170
|
+
- Remove trailing numbers
|
|
2398
4171
|
|
|
2399
|
-
|
|
4172
|
+
Examples:
|
|
4173
|
+
- alex-fonseca \u2192 "Alex Fonseca"
|
|
4174
|
+
- joao_pereira_22 \u2192 "Joao Pereira"
|
|
2400
4175
|
|
|
4176
|
+
**Style:**
|
|
4177
|
+
- Direct and technical, like Slack/Discord
|
|
4178
|
+
- Get straight to the point
|
|
4179
|
+
- Narrate your reasoning
|
|
4180
|
+
- Celebrate wins, acknowledge mistakes
|
|
2401
4181
|
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
3. Execute with narration
|
|
2407
|
-
4. Test when relevant
|
|
2408
|
-
5. Summarize results
|
|
2409
|
-
|
|
2410
|
-
For simple tasks:
|
|
2411
|
-
- Quick ack + immediate action
|
|
2412
|
-
- No TODO needed for single operations
|
|
2413
|
-
</workflow>
|
|
4182
|
+
**Message via tool only:**
|
|
4183
|
+
- Use message_notify_user for ALL communication
|
|
4184
|
+
- Never assume the user sees internal state
|
|
4185
|
+
- Report progress proactively
|
|
2414
4186
|
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
4187
|
+
**Example flow:**
|
|
4188
|
+
\`\`\`
|
|
4189
|
+
message_notify_user("Alex, understood. Adding the auth middleware. Let me check the current implementation first.")
|
|
4190
|
+
[uses read_file_lines]
|
|
4191
|
+
message_notify_user("Found the router at /src/routes/index.ts. I'll add the middleware before the protected routes.")
|
|
4192
|
+
[uses edit_tool]
|
|
4193
|
+
message_notify_user("Done. Running tests to verify...")
|
|
4194
|
+
[uses shell_command with {test_command}]
|
|
4195
|
+
message_notify_user("All 47 tests pass. The middleware is working correctly.")
|
|
4196
|
+
\`\`\`
|
|
4197
|
+
</communication_style>
|
|
2421
4198
|
|
|
2422
|
-
|
|
2423
|
-
- Run tests if applicable
|
|
2424
|
-
- Verify build passes
|
|
2425
|
-
- Check git status if in repo
|
|
2426
|
-
</technical_standards>
|
|
4199
|
+
---
|
|
2427
4200
|
|
|
2428
4201
|
<git_guidelines>
|
|
4202
|
+
## Git Best Practices
|
|
4203
|
+
|
|
2429
4204
|
When in a git repository:
|
|
2430
|
-
- Review changes
|
|
2431
|
-
- Commit
|
|
2432
|
-
- NEVER push unless explicitly asked
|
|
2433
|
-
- NEVER use destructive commands
|
|
4205
|
+
- **Review changes**: \`git diff HEAD\` before any commit
|
|
4206
|
+
- **Commit format**: Conventional Commits (feat:, fix:, chore:, docs:, test:, refactor:)
|
|
4207
|
+
- **NEVER push** unless explicitly asked
|
|
4208
|
+
- **NEVER use destructive commands**: reset --hard, rebase -i, force push
|
|
4209
|
+
|
|
4210
|
+
Good commit messages:
|
|
4211
|
+
- feat: add user authentication middleware
|
|
4212
|
+
- fix: resolve race condition in payment processing
|
|
4213
|
+
- test: add unit tests for auth module
|
|
4214
|
+
- chore: update dependencies
|
|
2434
4215
|
</git_guidelines>
|
|
2435
4216
|
|
|
2436
|
-
|
|
2437
|
-
Current session context:
|
|
2438
|
-
- OS: {os_type} ({os_version})
|
|
2439
|
-
- Architecture: {architecture}
|
|
2440
|
-
- Directory: {workdir}
|
|
2441
|
-
- Shell: {shell_type}
|
|
2442
|
-
- User: {username}
|
|
2443
|
-
- Date: {current_date}
|
|
2444
|
-
- Timezone: {timezone}
|
|
2445
|
-
- Git repo: {is_git_repo}
|
|
4217
|
+
---
|
|
2446
4218
|
|
|
2447
|
-
|
|
2448
|
-
|
|
4219
|
+
<code_quality>
|
|
4220
|
+
## Your Code Standards
|
|
4221
|
+
|
|
4222
|
+
**Every piece of code you write should be:**
|
|
4223
|
+
- Production-ready and properly typed
|
|
4224
|
+
- Self-documenting with clear names
|
|
4225
|
+
- Properly error-handled
|
|
4226
|
+
- Tested (unit tests for logic, integration for APIs)
|
|
4227
|
+
|
|
4228
|
+
**Before finishing ANY task:**
|
|
4229
|
+
1. Run tests: {test_command}
|
|
4230
|
+
2. Check for lint errors if linter exists
|
|
4231
|
+
3. Verify build passes if applicable
|
|
4232
|
+
4. Review git status if in repo
|
|
4233
|
+
|
|
4234
|
+
**Code review mindset:**
|
|
4235
|
+
- Would this pass code review from a senior dev?
|
|
4236
|
+
- Are there edge cases I haven't considered?
|
|
4237
|
+
- Is the error handling robust?
|
|
4238
|
+
- Are there security implications?
|
|
4239
|
+
</code_quality>
|
|
2449
4240
|
|
|
4241
|
+
---
|
|
4242
|
+
|
|
4243
|
+
<personality>
|
|
2450
4244
|
You are BluMa. Autonomous, precise, collaborative.
|
|
4245
|
+
You don't just execute - you think, suggest, and improve.
|
|
4246
|
+
You test your code because you care about quality.
|
|
4247
|
+
You communicate because you respect your teammate.
|
|
4248
|
+
|
|
2451
4249
|
Let's build something great, {username}.
|
|
4250
|
+
</personality>
|
|
2452
4251
|
`;
|
|
2453
4252
|
function getUnifiedSystemPrompt() {
|
|
4253
|
+
const cwd = process.cwd();
|
|
2454
4254
|
const env = {
|
|
2455
|
-
os_type:
|
|
2456
|
-
os_version:
|
|
2457
|
-
architecture:
|
|
2458
|
-
workdir:
|
|
4255
|
+
os_type: os7.type(),
|
|
4256
|
+
os_version: os7.release(),
|
|
4257
|
+
architecture: os7.arch(),
|
|
4258
|
+
workdir: cwd,
|
|
2459
4259
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
2460
|
-
username:
|
|
4260
|
+
username: os7.userInfo().username,
|
|
2461
4261
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
2462
4262
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
2463
|
-
is_git_repo: isGitRepo(
|
|
4263
|
+
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
4264
|
+
node_version: getNodeVersion(),
|
|
4265
|
+
npm_version: getNpmVersion(),
|
|
4266
|
+
git_branch: getGitBranch(cwd),
|
|
4267
|
+
package_manager: getPackageManager(cwd),
|
|
4268
|
+
project_type: getProjectType(cwd),
|
|
4269
|
+
test_framework: getTestFramework(cwd),
|
|
4270
|
+
test_command: getTestCommand(cwd)
|
|
2464
4271
|
};
|
|
2465
4272
|
return Object.entries(env).reduce(
|
|
2466
4273
|
(prompt, [key, value]) => prompt.replaceAll(`{${key}}`, value),
|
|
@@ -2469,8 +4276,8 @@ function getUnifiedSystemPrompt() {
|
|
|
2469
4276
|
}
|
|
2470
4277
|
function isGitRepo(dir) {
|
|
2471
4278
|
try {
|
|
2472
|
-
const gitPath =
|
|
2473
|
-
return
|
|
4279
|
+
const gitPath = path13.join(dir, ".git");
|
|
4280
|
+
return fs11.existsSync(gitPath) && fs11.lstatSync(gitPath).isDirectory();
|
|
2474
4281
|
} catch {
|
|
2475
4282
|
return false;
|
|
2476
4283
|
}
|
|
@@ -2712,7 +4519,7 @@ var BluMaAgent = class {
|
|
|
2712
4519
|
|
|
2713
4520
|
${editData.error.display}`;
|
|
2714
4521
|
}
|
|
2715
|
-
const filename =
|
|
4522
|
+
const filename = path14.basename(toolArgs.file_path);
|
|
2716
4523
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
2717
4524
|
} catch (e) {
|
|
2718
4525
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -2740,6 +4547,13 @@ ${editData.error.display}`;
|
|
|
2740
4547
|
}
|
|
2741
4548
|
let message = response.choices[0].message;
|
|
2742
4549
|
message = ToolCallNormalizer.normalizeAssistantMessage(message);
|
|
4550
|
+
if (message.reasoning_content || message.reasoning) {
|
|
4551
|
+
const reasoningText = message.reasoning_content || message.reasoning;
|
|
4552
|
+
this.eventBus.emit("backend_message", {
|
|
4553
|
+
type: "reasoning",
|
|
4554
|
+
content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
|
|
4555
|
+
});
|
|
4556
|
+
}
|
|
2743
4557
|
this.history.push(message);
|
|
2744
4558
|
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
2745
4559
|
const validToolCalls = message.tool_calls.filter(
|
|
@@ -2756,7 +4570,6 @@ ${editData.error.display}`;
|
|
|
2756
4570
|
const autoApprovedTools = [
|
|
2757
4571
|
"agent_end_turn",
|
|
2758
4572
|
"message_notify_user",
|
|
2759
|
-
"reasoning_nootebook",
|
|
2760
4573
|
"ls_tool",
|
|
2761
4574
|
"count_file_lines",
|
|
2762
4575
|
"read_file_lines",
|
|
@@ -2830,7 +4643,7 @@ function getSubAgentByCommand(cmd) {
|
|
|
2830
4643
|
}
|
|
2831
4644
|
|
|
2832
4645
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
2833
|
-
import
|
|
4646
|
+
import os8 from "os";
|
|
2834
4647
|
var SYSTEM_PROMPT2 = `
|
|
2835
4648
|
|
|
2836
4649
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -2993,12 +4806,12 @@ Rule Summary:
|
|
|
2993
4806
|
function getInitPrompt() {
|
|
2994
4807
|
const now = /* @__PURE__ */ new Date();
|
|
2995
4808
|
const collectedData = {
|
|
2996
|
-
os_type:
|
|
2997
|
-
os_version:
|
|
2998
|
-
architecture:
|
|
4809
|
+
os_type: os8.type(),
|
|
4810
|
+
os_version: os8.release(),
|
|
4811
|
+
architecture: os8.arch(),
|
|
2999
4812
|
workdir: process.cwd(),
|
|
3000
4813
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
3001
|
-
username:
|
|
4814
|
+
username: os8.userInfo().username || "Unknown",
|
|
3002
4815
|
current_date: now.toISOString().split("T")[0],
|
|
3003
4816
|
// Formato YYYY-MM-DD
|
|
3004
4817
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -3251,7 +5064,7 @@ var SubAgentsBluMa = class {
|
|
|
3251
5064
|
};
|
|
3252
5065
|
|
|
3253
5066
|
// src/app/agent/agent.ts
|
|
3254
|
-
var globalEnvPath =
|
|
5067
|
+
var globalEnvPath = path15.join(os9.homedir(), ".bluma-cli", ".env");
|
|
3255
5068
|
dotenv.config({ path: globalEnvPath });
|
|
3256
5069
|
var Agent = class {
|
|
3257
5070
|
sessionId;
|
|
@@ -3386,10 +5199,10 @@ var Agent = class {
|
|
|
3386
5199
|
};
|
|
3387
5200
|
|
|
3388
5201
|
// src/app/ui/WorkingTimer.tsx
|
|
3389
|
-
import { useState as useState4, useEffect as useEffect4 } from "react";
|
|
5202
|
+
import { useState as useState4, useEffect as useEffect4, memo as memo5 } from "react";
|
|
3390
5203
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
3391
5204
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3392
|
-
var
|
|
5205
|
+
var WorkingTimerComponent = ({ taskName, taskStatus }) => {
|
|
3393
5206
|
const [seconds, setSeconds] = useState4(0);
|
|
3394
5207
|
const [frame, setFrame] = useState4(0);
|
|
3395
5208
|
useEffect4(() => {
|
|
@@ -3400,210 +5213,309 @@ var WorkingTimer = () => {
|
|
|
3400
5213
|
}, []);
|
|
3401
5214
|
useEffect4(() => {
|
|
3402
5215
|
const animator = setInterval(() => {
|
|
3403
|
-
setFrame((prev) => (prev + 1) %
|
|
3404
|
-
},
|
|
5216
|
+
setFrame((prev) => (prev + 1) % 4);
|
|
5217
|
+
}, 150);
|
|
3405
5218
|
return () => clearInterval(animator);
|
|
3406
5219
|
}, []);
|
|
3407
|
-
const spinners = ["
|
|
5220
|
+
const spinners = ["|", "/", "-", "\\"];
|
|
3408
5221
|
const spinner = spinners[frame];
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
"
|
|
3416
|
-
|
|
5222
|
+
const formatTime = (s) => {
|
|
5223
|
+
if (s < 60) return `${s}s`;
|
|
5224
|
+
return `${Math.floor(s / 60)}m${s % 60}s`;
|
|
5225
|
+
};
|
|
5226
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, children: [
|
|
5227
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
5228
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: spinner }),
|
|
5229
|
+
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
5230
|
+
" ",
|
|
5231
|
+
taskStatus || "thinking"
|
|
5232
|
+
] }),
|
|
5233
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "gray", children: [
|
|
5234
|
+
" ",
|
|
5235
|
+
formatTime(seconds)
|
|
5236
|
+
] })
|
|
5237
|
+
] }),
|
|
5238
|
+
taskName && /* @__PURE__ */ jsx7(Box7, { paddingLeft: 2, children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
5239
|
+
"^ ",
|
|
5240
|
+
taskName
|
|
5241
|
+
] }) })
|
|
3417
5242
|
] });
|
|
3418
5243
|
};
|
|
5244
|
+
var WorkingTimer = memo5(WorkingTimerComponent);
|
|
3419
5245
|
|
|
3420
5246
|
// src/app/ui/components/ToolCallDisplay.tsx
|
|
3421
|
-
import { memo as
|
|
5247
|
+
import { memo as memo6 } from "react";
|
|
3422
5248
|
import { Box as Box9 } from "ink";
|
|
3423
5249
|
|
|
3424
5250
|
// src/app/ui/components/toolCallRenderers.tsx
|
|
3425
5251
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
3426
5252
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3427
|
-
var
|
|
5253
|
+
var parseArgs = (args) => {
|
|
3428
5254
|
if (typeof args === "string") {
|
|
3429
5255
|
try {
|
|
3430
|
-
return JSON.
|
|
3431
|
-
} catch
|
|
3432
|
-
return
|
|
5256
|
+
return JSON.parse(args);
|
|
5257
|
+
} catch {
|
|
5258
|
+
return {};
|
|
3433
5259
|
}
|
|
3434
5260
|
}
|
|
3435
|
-
return
|
|
5261
|
+
return args || {};
|
|
5262
|
+
};
|
|
5263
|
+
var truncate = (str, max) => {
|
|
5264
|
+
if (str.length <= max) return str;
|
|
5265
|
+
return str.slice(0, max - 3) + "...";
|
|
5266
|
+
};
|
|
5267
|
+
var getBasename = (filepath) => {
|
|
5268
|
+
const parts = filepath.split("/");
|
|
5269
|
+
return parts[parts.length - 1] || filepath;
|
|
3436
5270
|
};
|
|
3437
5271
|
var renderShellCommand2 = ({ args }) => {
|
|
3438
|
-
const
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
5272
|
+
const parsed = parseArgs(args);
|
|
5273
|
+
const command = parsed.command || "[no command]";
|
|
5274
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5275
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "$" }),
|
|
5276
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
5277
|
+
" ",
|
|
5278
|
+
truncate(command, 70)
|
|
5279
|
+
] })
|
|
3445
5280
|
] });
|
|
3446
5281
|
};
|
|
3447
5282
|
var renderLsTool2 = ({ args }) => {
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3457
|
-
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2713" }),
|
|
3458
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " ls" })
|
|
3459
|
-
] }),
|
|
3460
|
-
/* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: directoryPath }) })
|
|
5283
|
+
const parsed = parseArgs(args);
|
|
5284
|
+
const path17 = parsed.directory_path || ".";
|
|
5285
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5286
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "ls" }),
|
|
5287
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5288
|
+
" ",
|
|
5289
|
+
path17
|
|
5290
|
+
] })
|
|
3461
5291
|
] });
|
|
3462
5292
|
};
|
|
3463
5293
|
var renderCountFilesLines = ({ args }) => {
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3473
|
-
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2713" }),
|
|
3474
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " count lines" })
|
|
3475
|
-
] }),
|
|
3476
|
-
/* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: filepath }) })
|
|
5294
|
+
const parsed = parseArgs(args);
|
|
5295
|
+
const filepath = parsed.filepath || "[no file]";
|
|
5296
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5297
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "wc -l" }),
|
|
5298
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5299
|
+
" ",
|
|
5300
|
+
getBasename(filepath)
|
|
5301
|
+
] })
|
|
3477
5302
|
] });
|
|
3478
5303
|
};
|
|
3479
5304
|
var renderReadFileLines2 = ({ args }) => {
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
filepath = "Error parsing arguments";
|
|
3490
|
-
}
|
|
3491
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
3492
|
-
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3493
|
-
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2713" }),
|
|
3494
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " read" })
|
|
5305
|
+
const parsed = parseArgs(args);
|
|
5306
|
+
const filepath = parsed.filepath || "[no file]";
|
|
5307
|
+
const start = parsed.start_line || 1;
|
|
5308
|
+
const end = parsed.end_line || start;
|
|
5309
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5310
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "cat" }),
|
|
5311
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5312
|
+
" ",
|
|
5313
|
+
getBasename(filepath)
|
|
3495
5314
|
] }),
|
|
3496
|
-
/* @__PURE__ */ jsxs8(
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
" to ",
|
|
3502
|
-
endLine
|
|
3503
|
-
] }) })
|
|
5315
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
5316
|
+
" :",
|
|
5317
|
+
start,
|
|
5318
|
+
"-",
|
|
5319
|
+
end
|
|
3504
5320
|
] })
|
|
3505
5321
|
] });
|
|
3506
5322
|
};
|
|
3507
5323
|
var renderBlumaNotebook = ({ args }) => {
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
}
|
|
3514
|
-
|
|
3515
|
-
if (!thinkingData || typeof thinkingData.thought !== "string") {
|
|
3516
|
-
throw new Error("Invalid thought data");
|
|
3517
|
-
}
|
|
3518
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
3519
|
-
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text8, { bold: true, color: "cyan", children: "\u{1F4AD} Reasoning" }) }),
|
|
3520
|
-
/* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: thinkingData.thought }) })
|
|
3521
|
-
] });
|
|
3522
|
-
} catch (e) {
|
|
3523
|
-
return /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "red", children: "Error parsing reasoning" }) });
|
|
3524
|
-
}
|
|
5324
|
+
const parsed = parseArgs(args);
|
|
5325
|
+
const thought = parsed.thought || parsed.content?.thought || "[thinking...]";
|
|
5326
|
+
const truncated = truncate(thought, 100);
|
|
5327
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
5328
|
+
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text8, { color: "magenta", dimColor: true, children: "thinking" }) }),
|
|
5329
|
+
/* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: truncated }) })
|
|
5330
|
+
] });
|
|
3525
5331
|
};
|
|
3526
5332
|
var renderEditToolCall = ({ args, preview }) => {
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
} catch (e) {
|
|
3532
|
-
filepath = "Error parsing arguments";
|
|
3533
|
-
}
|
|
5333
|
+
const parsed = parseArgs(args);
|
|
5334
|
+
const filepath = parsed.file_path || "[no file]";
|
|
5335
|
+
const oldStr = parsed.old_string || "";
|
|
5336
|
+
const newStr = parsed.new_string || "";
|
|
3534
5337
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
3535
5338
|
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3536
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
3537
|
-
/* @__PURE__ */
|
|
3538
|
-
|
|
5339
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "edit" }),
|
|
5340
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5341
|
+
" ",
|
|
5342
|
+
getBasename(filepath)
|
|
5343
|
+
] })
|
|
3539
5344
|
] }),
|
|
3540
|
-
preview
|
|
5345
|
+
preview ? /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: 8 }) }) : /* @__PURE__ */ jsxs8(Box8, { paddingLeft: 2, flexDirection: "column", children: [
|
|
5346
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "red", dimColor: true, children: [
|
|
5347
|
+
"- ",
|
|
5348
|
+
truncate(oldStr, 50)
|
|
5349
|
+
] }),
|
|
5350
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "green", dimColor: true, children: [
|
|
5351
|
+
"+ ",
|
|
5352
|
+
truncate(newStr, 50)
|
|
5353
|
+
] })
|
|
5354
|
+
] })
|
|
3541
5355
|
] });
|
|
3542
5356
|
};
|
|
3543
5357
|
var renderTodoTool2 = ({ args }) => {
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " todo" })
|
|
3562
|
-
] }),
|
|
3563
|
-
/* @__PURE__ */ jsxs8(Box8, { paddingLeft: 2, flexDirection: "column", children: [
|
|
3564
|
-
tasks.length > 0 && tasks.length <= 10 && /* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, flexDirection: "column", marginTop: 1, children: tasks.map((task, idx) => {
|
|
3565
|
-
const isComplete = task.isComplete === true;
|
|
3566
|
-
const checkbox = isComplete ? "[x]" : "[ ]";
|
|
3567
|
-
const description = task.description || "No description";
|
|
3568
|
-
const displayText = description.length > 60 ? description.substring(0, 57) + "..." : description;
|
|
3569
|
-
const color = isComplete ? "gray" : "yellow";
|
|
3570
|
-
return /* @__PURE__ */ jsxs8(
|
|
3571
|
-
Text8,
|
|
3572
|
-
{
|
|
3573
|
-
color,
|
|
3574
|
-
strikethrough: isComplete,
|
|
3575
|
-
dimColor: isComplete,
|
|
3576
|
-
children: [
|
|
3577
|
-
checkbox,
|
|
3578
|
-
" ",
|
|
3579
|
-
displayText
|
|
3580
|
-
]
|
|
3581
|
-
},
|
|
3582
|
-
idx
|
|
3583
|
-
);
|
|
3584
|
-
}) }),
|
|
3585
|
-
tasks.length > 10 && /* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
3586
|
-
"(",
|
|
3587
|
-
tasks.length,
|
|
3588
|
-
" tasks total - showing summary)"
|
|
3589
|
-
] }) })
|
|
5358
|
+
const parsed = parseArgs(args);
|
|
5359
|
+
const tasks = parsed.tasks || [];
|
|
5360
|
+
const completed = tasks.filter((t) => t.isComplete === true).length;
|
|
5361
|
+
const pending = tasks.length - completed;
|
|
5362
|
+
const progress = tasks.length > 0 ? Math.round(completed / tasks.length * 100) : 0;
|
|
5363
|
+
const barWidth = 10;
|
|
5364
|
+
const filled = Math.round(progress / 100 * barWidth);
|
|
5365
|
+
const bar = "=".repeat(filled) + " ".repeat(barWidth - filled);
|
|
5366
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
5367
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
5368
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: "todo" }),
|
|
5369
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
5370
|
+
" [",
|
|
5371
|
+
bar,
|
|
5372
|
+
"] ",
|
|
5373
|
+
progress,
|
|
5374
|
+
"%"
|
|
3590
5375
|
] })
|
|
3591
|
-
] })
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
5376
|
+
] }),
|
|
5377
|
+
tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingLeft: 2, children: [
|
|
5378
|
+
tasks.slice(0, 15).map((task, i) => /* @__PURE__ */ jsxs8(
|
|
5379
|
+
Text8,
|
|
5380
|
+
{
|
|
5381
|
+
color: task.isComplete === true ? "gray" : "white",
|
|
5382
|
+
dimColor: task.isComplete === true,
|
|
5383
|
+
strikethrough: task.isComplete === true,
|
|
5384
|
+
children: [
|
|
5385
|
+
task.isComplete === true ? "[x]" : "[ ]",
|
|
5386
|
+
" ",
|
|
5387
|
+
task.description
|
|
5388
|
+
]
|
|
5389
|
+
},
|
|
5390
|
+
i
|
|
5391
|
+
)),
|
|
5392
|
+
tasks.length > 15 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
5393
|
+
"... +",
|
|
5394
|
+
tasks.length - 15,
|
|
5395
|
+
" more tasks"
|
|
5396
|
+
] })
|
|
5397
|
+
] })
|
|
5398
|
+
] });
|
|
5399
|
+
};
|
|
5400
|
+
var renderFindByName = ({ args }) => {
|
|
5401
|
+
const parsed = parseArgs(args);
|
|
5402
|
+
const pattern = parsed.pattern || "*";
|
|
5403
|
+
const dir = parsed.directory || ".";
|
|
5404
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5405
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "find" }),
|
|
5406
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
5407
|
+
' "',
|
|
5408
|
+
pattern,
|
|
5409
|
+
'"'
|
|
5410
|
+
] }),
|
|
5411
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5412
|
+
" in ",
|
|
5413
|
+
dir
|
|
5414
|
+
] })
|
|
5415
|
+
] });
|
|
5416
|
+
};
|
|
5417
|
+
var renderGrepSearch = ({ args }) => {
|
|
5418
|
+
const parsed = parseArgs(args);
|
|
5419
|
+
const query = parsed.query || "";
|
|
5420
|
+
const path17 = parsed.path || ".";
|
|
5421
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5422
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "grep" }),
|
|
5423
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
5424
|
+
' "',
|
|
5425
|
+
truncate(query, 30),
|
|
5426
|
+
'"'
|
|
5427
|
+
] }),
|
|
5428
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5429
|
+
" ",
|
|
5430
|
+
path17
|
|
5431
|
+
] })
|
|
5432
|
+
] });
|
|
5433
|
+
};
|
|
5434
|
+
var renderViewFileOutline = ({ args }) => {
|
|
5435
|
+
const parsed = parseArgs(args);
|
|
5436
|
+
const filepath = parsed.file_path || "[no file]";
|
|
5437
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5438
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "outline" }),
|
|
5439
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5440
|
+
" ",
|
|
5441
|
+
getBasename(filepath)
|
|
5442
|
+
] })
|
|
5443
|
+
] });
|
|
5444
|
+
};
|
|
5445
|
+
var renderRunCommandAsync = ({ args }) => {
|
|
5446
|
+
const parsed = parseArgs(args);
|
|
5447
|
+
const command = parsed.command || "[no command]";
|
|
5448
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5449
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "async" }),
|
|
5450
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: " $" }),
|
|
5451
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5452
|
+
" ",
|
|
5453
|
+
truncate(command, 50)
|
|
5454
|
+
] })
|
|
5455
|
+
] });
|
|
5456
|
+
};
|
|
5457
|
+
var renderCommandStatus = ({ args }) => {
|
|
5458
|
+
const parsed = parseArgs(args);
|
|
5459
|
+
const id = parsed.command_id || "[no id]";
|
|
5460
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5461
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "status" }),
|
|
5462
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5463
|
+
" #",
|
|
5464
|
+
id
|
|
5465
|
+
] })
|
|
5466
|
+
] });
|
|
3595
5467
|
};
|
|
3596
|
-
var
|
|
3597
|
-
const
|
|
5468
|
+
var renderTaskBoundary = ({ args }) => {
|
|
5469
|
+
const parsed = parseArgs(args);
|
|
5470
|
+
const name = parsed.task_name || "[no name]";
|
|
5471
|
+
const mode = parsed.mode || "EXECUTION";
|
|
5472
|
+
const status = parsed.task_status || "";
|
|
5473
|
+
const modeColors = {
|
|
5474
|
+
PLANNING: "blue",
|
|
5475
|
+
EXECUTION: "green",
|
|
5476
|
+
VERIFICATION: "yellow"
|
|
5477
|
+
};
|
|
5478
|
+
const modeSymbols = {
|
|
5479
|
+
PLANNING: "P",
|
|
5480
|
+
EXECUTION: "E",
|
|
5481
|
+
VERIFICATION: "V"
|
|
5482
|
+
};
|
|
3598
5483
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
3599
5484
|
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3600
|
-
/* @__PURE__ */
|
|
3601
|
-
|
|
5485
|
+
/* @__PURE__ */ jsxs8(Text8, { color: modeColors[mode] || "gray", bold: true, children: [
|
|
5486
|
+
"[",
|
|
5487
|
+
modeSymbols[mode] || "?",
|
|
5488
|
+
"]"
|
|
5489
|
+
] }),
|
|
5490
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
3602
5491
|
" ",
|
|
3603
|
-
|
|
5492
|
+
name
|
|
3604
5493
|
] })
|
|
3605
5494
|
] }),
|
|
3606
|
-
|
|
5495
|
+
status && /* @__PURE__ */ jsx8(Box8, { paddingLeft: 4, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: truncate(status, 60) }) })
|
|
5496
|
+
] });
|
|
5497
|
+
};
|
|
5498
|
+
var renderSearchWeb = ({ args }) => {
|
|
5499
|
+
const parsed = parseArgs(args);
|
|
5500
|
+
const query = parsed.query || "[no query]";
|
|
5501
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5502
|
+
/* @__PURE__ */ jsx8(Text8, { color: "blue", children: "search" }),
|
|
5503
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
5504
|
+
' "',
|
|
5505
|
+
truncate(query, 40),
|
|
5506
|
+
'"'
|
|
5507
|
+
] })
|
|
5508
|
+
] });
|
|
5509
|
+
};
|
|
5510
|
+
var renderGeneric2 = ({ toolName, args }) => {
|
|
5511
|
+
const parsed = parseArgs(args);
|
|
5512
|
+
const keys = Object.keys(parsed).slice(0, 2);
|
|
5513
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5514
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: toolName }),
|
|
5515
|
+
keys.length > 0 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
5516
|
+
" ",
|
|
5517
|
+
keys.map((k) => `${k}:${truncate(String(parsed[k]), 20)}`).join(" ")
|
|
5518
|
+
] })
|
|
3607
5519
|
] });
|
|
3608
5520
|
};
|
|
3609
5521
|
var ToolRenderDisplay = {
|
|
@@ -3613,7 +5525,14 @@ var ToolRenderDisplay = {
|
|
|
3613
5525
|
count_file_lines: renderCountFilesLines,
|
|
3614
5526
|
read_file_lines: renderReadFileLines2,
|
|
3615
5527
|
edit_tool: renderEditToolCall,
|
|
3616
|
-
todo: renderTodoTool2
|
|
5528
|
+
todo: renderTodoTool2,
|
|
5529
|
+
find_by_name: renderFindByName,
|
|
5530
|
+
grep_search: renderGrepSearch,
|
|
5531
|
+
view_file_outline: renderViewFileOutline,
|
|
5532
|
+
run_command_async: renderRunCommandAsync,
|
|
5533
|
+
command_status: renderCommandStatus,
|
|
5534
|
+
task_boundary: renderTaskBoundary,
|
|
5535
|
+
search_web: renderSearchWeb
|
|
3617
5536
|
};
|
|
3618
5537
|
|
|
3619
5538
|
// src/app/ui/components/ToolCallDisplay.tsx
|
|
@@ -3622,194 +5541,303 @@ var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
|
|
|
3622
5541
|
if (toolName.includes("message_notify_user") || toolName.includes("agent_end_turn")) {
|
|
3623
5542
|
return null;
|
|
3624
5543
|
}
|
|
3625
|
-
const Renderer = ToolRenderDisplay[toolName] ||
|
|
5544
|
+
const Renderer = ToolRenderDisplay[toolName] || ((props2) => renderGeneric2({ ...props2, toolName }));
|
|
3626
5545
|
return /* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Renderer, { toolName, args, preview }) });
|
|
3627
5546
|
};
|
|
3628
|
-
var ToolCallDisplay =
|
|
5547
|
+
var ToolCallDisplay = memo6(ToolCallDisplayComponent);
|
|
3629
5548
|
|
|
3630
5549
|
// src/app/ui/components/ToolResultDisplay.tsx
|
|
3631
|
-
import { memo as
|
|
3632
|
-
import { Box as Box11 } from "ink";
|
|
5550
|
+
import { memo as memo8 } from "react";
|
|
5551
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
3633
5552
|
|
|
3634
5553
|
// src/app/ui/components/MarkdownRenderer.tsx
|
|
5554
|
+
import { memo as memo7 } from "react";
|
|
3635
5555
|
import { Box as Box10, Text as Text9 } from "ink";
|
|
3636
5556
|
import { marked } from "marked";
|
|
3637
5557
|
import { highlight } from "cli-highlight";
|
|
3638
|
-
import
|
|
3639
|
-
|
|
5558
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
5559
|
+
var COLORS = {
|
|
5560
|
+
heading: "cyan",
|
|
5561
|
+
code: "gray",
|
|
5562
|
+
link: "blue",
|
|
5563
|
+
quote: "gray",
|
|
5564
|
+
listBullet: "gray"
|
|
5565
|
+
};
|
|
3640
5566
|
function renderTokens(tokens) {
|
|
3641
|
-
const
|
|
3642
|
-
for (
|
|
5567
|
+
const elements = [];
|
|
5568
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
5569
|
+
const token = tokens[i];
|
|
5570
|
+
const key = `token-${i}`;
|
|
3643
5571
|
switch (token.type) {
|
|
3644
5572
|
case "heading": {
|
|
3645
|
-
const
|
|
3646
|
-
const
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
4: chalk.bold.hex("#cf222e"),
|
|
3654
|
-
// Vermelho
|
|
3655
|
-
5: chalk.bold.hex("#9a6700"),
|
|
3656
|
-
// Amarelo escuro
|
|
3657
|
-
6: chalk.bold.hex("#57606a")
|
|
3658
|
-
// Cinza
|
|
3659
|
-
};
|
|
3660
|
-
out.push(
|
|
3661
|
-
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text9, { children: colors[t.depth](`# ${t.text}`) }) }, out.length)
|
|
5573
|
+
const heading = token;
|
|
5574
|
+
const prefix = "#".repeat(heading.depth);
|
|
5575
|
+
elements.push(
|
|
5576
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: COLORS.heading, bold: true, children: [
|
|
5577
|
+
prefix,
|
|
5578
|
+
" ",
|
|
5579
|
+
heading.text
|
|
5580
|
+
] }) }, key)
|
|
3662
5581
|
);
|
|
3663
5582
|
break;
|
|
3664
5583
|
}
|
|
3665
5584
|
case "paragraph": {
|
|
3666
|
-
const
|
|
3667
|
-
|
|
3668
|
-
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1,
|
|
5585
|
+
const para = token;
|
|
5586
|
+
elements.push(
|
|
5587
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: renderInline(para.text) }, key)
|
|
3669
5588
|
);
|
|
3670
5589
|
break;
|
|
3671
5590
|
}
|
|
3672
5591
|
case "list": {
|
|
3673
|
-
const
|
|
3674
|
-
|
|
3675
|
-
const
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
5592
|
+
const list = token;
|
|
5593
|
+
list.items.forEach((item, idx) => {
|
|
5594
|
+
const bullet = list.ordered ? `${idx + 1}.` : "\u2022";
|
|
5595
|
+
const text = item.tokens?.filter((t) => t.type === "text").map((t) => t.text).join("") || item.text;
|
|
5596
|
+
elements.push(
|
|
5597
|
+
/* @__PURE__ */ jsxs9(Box10, { paddingLeft: 1, children: [
|
|
5598
|
+
/* @__PURE__ */ jsx10(Text9, { color: COLORS.listBullet, children: bullet }),
|
|
3679
5599
|
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3680
5600
|
" ",
|
|
3681
5601
|
renderInline(text)
|
|
3682
5602
|
] })
|
|
3683
|
-
] },
|
|
5603
|
+
] }, `${key}-item-${idx}`)
|
|
3684
5604
|
);
|
|
3685
5605
|
});
|
|
3686
|
-
|
|
5606
|
+
elements.push(/* @__PURE__ */ jsx10(Box10, { marginBottom: 1 }, `${key}-spacer`));
|
|
3687
5607
|
break;
|
|
3688
5608
|
}
|
|
3689
5609
|
case "code": {
|
|
3690
|
-
const
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
]
|
|
3706
|
-
},
|
|
3707
|
-
out.length
|
|
3708
|
-
)
|
|
5610
|
+
const code = token;
|
|
5611
|
+
let highlighted;
|
|
5612
|
+
try {
|
|
5613
|
+
highlighted = highlight(code.text, {
|
|
5614
|
+
language: code.lang || "text",
|
|
5615
|
+
ignoreIllegals: true
|
|
5616
|
+
});
|
|
5617
|
+
} catch {
|
|
5618
|
+
highlighted = code.text;
|
|
5619
|
+
}
|
|
5620
|
+
elements.push(
|
|
5621
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
5622
|
+
code.lang && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: code.lang }),
|
|
5623
|
+
/* @__PURE__ */ jsx10(Box10, { paddingLeft: 1, children: /* @__PURE__ */ jsx10(Text9, { children: highlighted }) })
|
|
5624
|
+
] }, key)
|
|
3709
5625
|
);
|
|
3710
5626
|
break;
|
|
3711
5627
|
}
|
|
3712
5628
|
case "blockquote": {
|
|
3713
|
-
const
|
|
3714
|
-
|
|
3715
|
-
/* @__PURE__ */ jsxs9(Box10, {
|
|
3716
|
-
/* @__PURE__ */ jsx10(Text9, { color:
|
|
3717
|
-
/* @__PURE__ */ jsx10(Text9, { dimColor: true, italic: true, children:
|
|
3718
|
-
] },
|
|
5629
|
+
const quote = token;
|
|
5630
|
+
elements.push(
|
|
5631
|
+
/* @__PURE__ */ jsxs9(Box10, { marginBottom: 1, children: [
|
|
5632
|
+
/* @__PURE__ */ jsx10(Text9, { color: COLORS.quote, children: "\u2502 " }),
|
|
5633
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, italic: true, children: quote.text })
|
|
5634
|
+
] }, key)
|
|
3719
5635
|
);
|
|
3720
5636
|
break;
|
|
3721
5637
|
}
|
|
3722
5638
|
case "table": {
|
|
3723
5639
|
const table = token;
|
|
3724
|
-
|
|
3725
|
-
(cell) =>
|
|
5640
|
+
elements.push(
|
|
5641
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "row", children: table.header.map((cell, idx) => /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, children: /* @__PURE__ */ jsx10(Text9, { bold: true, children: cell.text }) }, idx)) }, `${key}-header`)
|
|
3726
5642
|
);
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
table.rows.forEach((row) => {
|
|
3731
|
-
out.push(
|
|
3732
|
-
/* @__PURE__ */ jsx10(Box10, { flexDirection: "row", children: row.map((cell, i) => /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, children: /* @__PURE__ */ jsx10(Text9, { children: cell.text }) }, i)) }, out.length)
|
|
5643
|
+
table.rows.forEach((row, rowIdx) => {
|
|
5644
|
+
elements.push(
|
|
5645
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "row", children: row.map((cell, cellIdx) => /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, children: /* @__PURE__ */ jsx10(Text9, { children: cell.text }) }, cellIdx)) }, `${key}-row-${rowIdx}`)
|
|
3733
5646
|
);
|
|
3734
5647
|
});
|
|
3735
|
-
|
|
5648
|
+
elements.push(/* @__PURE__ */ jsx10(Box10, { marginBottom: 1 }, `${key}-spacer`));
|
|
5649
|
+
break;
|
|
5650
|
+
}
|
|
5651
|
+
case "hr": {
|
|
5652
|
+
elements.push(
|
|
5653
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500".repeat(40) }) }, key)
|
|
5654
|
+
);
|
|
3736
5655
|
break;
|
|
3737
5656
|
}
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
5657
|
+
case "space": {
|
|
5658
|
+
break;
|
|
5659
|
+
}
|
|
5660
|
+
default: {
|
|
5661
|
+
if ("text" in token && typeof token.text === "string") {
|
|
5662
|
+
elements.push(/* @__PURE__ */ jsx10(Text9, { children: token.text }, key));
|
|
3741
5663
|
}
|
|
5664
|
+
}
|
|
3742
5665
|
}
|
|
3743
5666
|
}
|
|
3744
|
-
return
|
|
5667
|
+
return elements;
|
|
3745
5668
|
}
|
|
3746
5669
|
function renderInline(text) {
|
|
3747
5670
|
const parts = [];
|
|
3748
|
-
let
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
5671
|
+
let remaining = text;
|
|
5672
|
+
let partIndex = 0;
|
|
5673
|
+
const inlineRegex = /(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\))/;
|
|
5674
|
+
while (remaining.length > 0) {
|
|
5675
|
+
const match = remaining.match(inlineRegex);
|
|
5676
|
+
if (!match) {
|
|
5677
|
+
parts.push(/* @__PURE__ */ jsx10(Text9, { children: remaining }, `inline-${partIndex++}`));
|
|
3754
5678
|
break;
|
|
3755
5679
|
}
|
|
3756
|
-
const
|
|
3757
|
-
if (
|
|
3758
|
-
parts.push(
|
|
3759
|
-
|
|
3760
|
-
|
|
5680
|
+
const matchIndex = match.index ?? 0;
|
|
5681
|
+
if (matchIndex > 0) {
|
|
5682
|
+
parts.push(
|
|
5683
|
+
/* @__PURE__ */ jsx10(Text9, { children: remaining.slice(0, matchIndex) }, `inline-${partIndex++}`)
|
|
5684
|
+
);
|
|
5685
|
+
}
|
|
5686
|
+
const fullMatch = match[0];
|
|
5687
|
+
if (fullMatch.startsWith("**")) {
|
|
3761
5688
|
parts.push(
|
|
3762
|
-
/* @__PURE__ */ jsx10(Text9, { bold: true, children:
|
|
5689
|
+
/* @__PURE__ */ jsx10(Text9, { bold: true, children: match[2] }, `inline-${partIndex++}`)
|
|
3763
5690
|
);
|
|
3764
|
-
} else if (
|
|
5691
|
+
} else if (fullMatch.startsWith("*")) {
|
|
3765
5692
|
parts.push(
|
|
3766
|
-
/* @__PURE__ */ jsx10(Text9, { italic: true, children:
|
|
5693
|
+
/* @__PURE__ */ jsx10(Text9, { italic: true, children: match[3] }, `inline-${partIndex++}`)
|
|
3767
5694
|
);
|
|
3768
|
-
} else if (
|
|
5695
|
+
} else if (fullMatch.startsWith("`")) {
|
|
3769
5696
|
parts.push(
|
|
3770
|
-
/* @__PURE__ */
|
|
5697
|
+
/* @__PURE__ */ jsxs9(Text9, { color: COLORS.code, children: [
|
|
5698
|
+
"`",
|
|
5699
|
+
match[4],
|
|
5700
|
+
"`"
|
|
5701
|
+
] }, `inline-${partIndex++}`)
|
|
3771
5702
|
);
|
|
3772
|
-
} else if (
|
|
5703
|
+
} else if (fullMatch.startsWith("[")) {
|
|
3773
5704
|
parts.push(
|
|
3774
|
-
/* @__PURE__ */
|
|
3775
|
-
m[5],
|
|
3776
|
-
" (",
|
|
3777
|
-
m[6],
|
|
3778
|
-
")"
|
|
3779
|
-
] }, parts.length)
|
|
5705
|
+
/* @__PURE__ */ jsx10(Text9, { color: COLORS.link, underline: true, children: match[5] }, `inline-${partIndex++}`)
|
|
3780
5706
|
);
|
|
3781
5707
|
}
|
|
3782
|
-
|
|
5708
|
+
remaining = remaining.slice(matchIndex + fullMatch.length);
|
|
3783
5709
|
}
|
|
3784
|
-
return parts;
|
|
5710
|
+
return parts.length === 1 ? parts[0] : /* @__PURE__ */ jsx10(Fragment3, { children: parts });
|
|
3785
5711
|
}
|
|
3786
|
-
var
|
|
5712
|
+
var MarkdownRendererComponent = ({ markdown }) => {
|
|
5713
|
+
if (!markdown || markdown.trim() === "") {
|
|
5714
|
+
return null;
|
|
5715
|
+
}
|
|
3787
5716
|
const tokens = marked.lexer(markdown);
|
|
3788
5717
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: renderTokens(tokens) });
|
|
3789
5718
|
};
|
|
5719
|
+
var MarkdownRenderer = memo7(MarkdownRendererComponent);
|
|
3790
5720
|
|
|
3791
5721
|
// src/app/ui/components/ToolResultDisplay.tsx
|
|
3792
|
-
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
5722
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5723
|
+
var MAX_LINES = 8;
|
|
5724
|
+
var parseResult = (result) => {
|
|
5725
|
+
try {
|
|
5726
|
+
return JSON.parse(result);
|
|
5727
|
+
} catch {
|
|
5728
|
+
return null;
|
|
5729
|
+
}
|
|
5730
|
+
};
|
|
5731
|
+
var truncateLines = (text, max) => {
|
|
5732
|
+
const allLines = text.split("\n").filter((l) => l.trim());
|
|
5733
|
+
if (allLines.length <= max) {
|
|
5734
|
+
return { lines: allLines, truncated: 0 };
|
|
5735
|
+
}
|
|
5736
|
+
return {
|
|
5737
|
+
lines: allLines.slice(0, max),
|
|
5738
|
+
truncated: allLines.length - max
|
|
5739
|
+
};
|
|
5740
|
+
};
|
|
3793
5741
|
var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
3794
|
-
if (
|
|
5742
|
+
if (toolName.includes("agent_end_turn") || toolName.includes("task_boundary")) {
|
|
3795
5743
|
return null;
|
|
3796
5744
|
}
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
5745
|
+
const parsed = parseResult(result);
|
|
5746
|
+
if (toolName.includes("message_notify_user")) {
|
|
5747
|
+
const body = parsed?.content?.body || parsed?.body || parsed?.message;
|
|
5748
|
+
if (!body) return null;
|
|
5749
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(MarkdownRenderer, { markdown: body }) });
|
|
5750
|
+
}
|
|
5751
|
+
if (toolName.includes("read_file") && parsed?.content) {
|
|
5752
|
+
const { lines, truncated } = truncateLines(parsed.content, MAX_LINES);
|
|
5753
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5754
|
+
lines.map((line, i) => /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "gray", children: line.slice(0, 80) }, i)),
|
|
5755
|
+
truncated > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5756
|
+
"... +",
|
|
5757
|
+
truncated,
|
|
5758
|
+
" lines"
|
|
5759
|
+
] })
|
|
5760
|
+
] });
|
|
5761
|
+
}
|
|
5762
|
+
if ((toolName.includes("shell_command") || toolName.includes("run_command")) && parsed) {
|
|
5763
|
+
const output = parsed.stdout || parsed.output || "";
|
|
5764
|
+
const stderr = parsed.stderr || "";
|
|
5765
|
+
const exitCode = parsed.exit_code ?? parsed.exitCode ?? 0;
|
|
5766
|
+
if (!output && !stderr) return null;
|
|
5767
|
+
const { lines, truncated } = truncateLines(output || stderr, MAX_LINES);
|
|
5768
|
+
const isError = exitCode !== 0 || stderr;
|
|
5769
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5770
|
+
lines.map((line, i) => /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: isError ? "red" : "gray", children: line.slice(0, 80) }, i)),
|
|
5771
|
+
truncated > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5772
|
+
"... +",
|
|
5773
|
+
truncated,
|
|
5774
|
+
" lines"
|
|
5775
|
+
] })
|
|
5776
|
+
] });
|
|
5777
|
+
}
|
|
5778
|
+
if ((toolName.includes("grep") || toolName.includes("find_by_name")) && parsed) {
|
|
5779
|
+
const matches = parsed.matches || parsed.results || parsed.files || [];
|
|
5780
|
+
if (matches.length === 0) {
|
|
5781
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "no matches" }) });
|
|
3802
5782
|
}
|
|
3803
|
-
|
|
5783
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5784
|
+
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5785
|
+
matches.length,
|
|
5786
|
+
" matches"
|
|
5787
|
+
] }),
|
|
5788
|
+
matches.slice(0, 5).map((m, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "gray", children: [
|
|
5789
|
+
m.file || m.path || m.name || m,
|
|
5790
|
+
m.line ? `:${m.line}` : ""
|
|
5791
|
+
] }, i)),
|
|
5792
|
+
matches.length > 5 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5793
|
+
"... +",
|
|
5794
|
+
matches.length - 5,
|
|
5795
|
+
" more"
|
|
5796
|
+
] })
|
|
5797
|
+
] });
|
|
5798
|
+
}
|
|
5799
|
+
if (toolName.includes("ls_tool") && parsed) {
|
|
5800
|
+
const entries = parsed.entries || parsed.files || [];
|
|
5801
|
+
if (entries.length === 0) return null;
|
|
5802
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5803
|
+
entries.slice(0, 6).map((e, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: e.isDirectory ? "blue" : "gray", children: [
|
|
5804
|
+
e.isDirectory ? "d" : "f",
|
|
5805
|
+
" ",
|
|
5806
|
+
e.name || e
|
|
5807
|
+
] }, i)),
|
|
5808
|
+
entries.length > 6 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5809
|
+
"... +",
|
|
5810
|
+
entries.length - 6,
|
|
5811
|
+
" more"
|
|
5812
|
+
] })
|
|
5813
|
+
] });
|
|
5814
|
+
}
|
|
5815
|
+
if (toolName.includes("todo")) {
|
|
3804
5816
|
return null;
|
|
3805
5817
|
}
|
|
5818
|
+
if (toolName.includes("view_file_outline") && parsed) {
|
|
5819
|
+
const symbols = parsed.symbols || parsed.outline || [];
|
|
5820
|
+
if (symbols.length === 0) return null;
|
|
5821
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5822
|
+
symbols.slice(0, 5).map((s, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5823
|
+
s.kind || "sym",
|
|
5824
|
+
" ",
|
|
5825
|
+
s.name
|
|
5826
|
+
] }, i)),
|
|
5827
|
+
symbols.length > 5 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5828
|
+
"... +",
|
|
5829
|
+
symbols.length - 5,
|
|
5830
|
+
" more"
|
|
5831
|
+
] })
|
|
5832
|
+
] });
|
|
5833
|
+
}
|
|
3806
5834
|
return null;
|
|
3807
5835
|
};
|
|
3808
|
-
var ToolResultDisplay =
|
|
5836
|
+
var ToolResultDisplay = memo8(ToolResultDisplayComponent);
|
|
3809
5837
|
|
|
3810
5838
|
// src/app/ui/components/SlashCommands.tsx
|
|
3811
5839
|
import { Box as Box12, Text as Text11 } from "ink";
|
|
3812
|
-
import { Fragment as
|
|
5840
|
+
import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3813
5841
|
var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
3814
5842
|
const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
|
|
3815
5843
|
const outBox = (children) => /* @__PURE__ */ jsx12(
|
|
@@ -3831,9 +5859,9 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3831
5859
|
if (cmd === "help") {
|
|
3832
5860
|
const cmds = getSlashCommands();
|
|
3833
5861
|
return outBox(
|
|
3834
|
-
/* @__PURE__ */
|
|
5862
|
+
/* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3835
5863
|
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text11, { bold: true, color: "magenta", children: "Available Commands" }) }),
|
|
3836
|
-
/* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: cmds.map((c, i) => /* @__PURE__ */
|
|
5864
|
+
/* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: cmds.map((c, i) => /* @__PURE__ */ jsxs11(Box12, { children: [
|
|
3837
5865
|
/* @__PURE__ */ jsx12(Text11, { color: "cyan", children: c.name.padEnd(12) }),
|
|
3838
5866
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: c.description })
|
|
3839
5867
|
] }, i)) })
|
|
@@ -3843,7 +5871,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3843
5871
|
if (cmd === "clear") {
|
|
3844
5872
|
setHistory((prev) => prev.filter((item) => item.id === 0 || item.id === 1));
|
|
3845
5873
|
return outBox(
|
|
3846
|
-
/* @__PURE__ */
|
|
5874
|
+
/* @__PURE__ */ jsxs11(Box12, { children: [
|
|
3847
5875
|
/* @__PURE__ */ jsx12(Text11, { color: "green", children: "\u2713" }),
|
|
3848
5876
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " History cleared" })
|
|
3849
5877
|
] })
|
|
@@ -3857,7 +5885,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3857
5885
|
setHistory((prev) => prev.concat({
|
|
3858
5886
|
id: Date.now(),
|
|
3859
5887
|
component: outBox(
|
|
3860
|
-
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */
|
|
5888
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
3861
5889
|
"\u2716 Failed to execute /init: ",
|
|
3862
5890
|
e?.message || String(e)
|
|
3863
5891
|
] }) })
|
|
@@ -3878,29 +5906,29 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3878
5906
|
const colType = 10;
|
|
3879
5907
|
const colSource = 18;
|
|
3880
5908
|
return outBox(
|
|
3881
|
-
/* @__PURE__ */
|
|
3882
|
-
/* @__PURE__ */
|
|
5909
|
+
/* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
5910
|
+
/* @__PURE__ */ jsxs11(Box12, { marginBottom: 1, children: [
|
|
3883
5911
|
/* @__PURE__ */ jsx12(Text11, { bold: true, color: "magenta", children: "MCP Tools" }),
|
|
3884
5912
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " \u2022 " }),
|
|
3885
|
-
/* @__PURE__ */
|
|
5913
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
3886
5914
|
tools.length,
|
|
3887
5915
|
" total"
|
|
3888
5916
|
] }),
|
|
3889
|
-
term && /* @__PURE__ */
|
|
5917
|
+
term && /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3890
5918
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " \u2022 filter: " }),
|
|
3891
|
-
/* @__PURE__ */
|
|
5919
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "cyan", children: [
|
|
3892
5920
|
'"',
|
|
3893
5921
|
term,
|
|
3894
5922
|
'"'
|
|
3895
5923
|
] }),
|
|
3896
|
-
/* @__PURE__ */
|
|
5924
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
3897
5925
|
" \u2022 showing: ",
|
|
3898
5926
|
filtered.length
|
|
3899
5927
|
] })
|
|
3900
5928
|
] })
|
|
3901
5929
|
] }),
|
|
3902
|
-
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools found" }) : /* @__PURE__ */
|
|
3903
|
-
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */
|
|
5930
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools found" }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
5931
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3904
5932
|
pad("Name", colName),
|
|
3905
5933
|
" \u2502 ",
|
|
3906
5934
|
pad("Type", colType),
|
|
@@ -3912,7 +5940,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3912
5940
|
const name = t.function?.name || t.name || "tool";
|
|
3913
5941
|
const type = t.function?.name ? "fn" : t.type || "tool";
|
|
3914
5942
|
const source = t.source || t.provider || "mcp";
|
|
3915
|
-
return /* @__PURE__ */
|
|
5943
|
+
return /* @__PURE__ */ jsxs11(Text11, { color: "white", children: [
|
|
3916
5944
|
pad(name, colName),
|
|
3917
5945
|
" \u2502 ",
|
|
3918
5946
|
pad(String(type), colType),
|
|
@@ -3935,22 +5963,22 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3935
5963
|
const colType = 10;
|
|
3936
5964
|
const colSource = 18;
|
|
3937
5965
|
return outBox(
|
|
3938
|
-
/* @__PURE__ */
|
|
5966
|
+
/* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3939
5967
|
/* @__PURE__ */ jsx12(Text11, { color: "magenta", bold: true, children: "Native Tools" }),
|
|
3940
|
-
/* @__PURE__ */
|
|
5968
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3941
5969
|
"Total Native: ",
|
|
3942
5970
|
tools.length,
|
|
3943
5971
|
term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
|
|
3944
5972
|
] }),
|
|
3945
|
-
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */
|
|
3946
|
-
/* @__PURE__ */
|
|
5973
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
5974
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3947
5975
|
pad("Name", colName),
|
|
3948
5976
|
" | ",
|
|
3949
5977
|
pad("Type", colType),
|
|
3950
5978
|
" | ",
|
|
3951
5979
|
pad("Source", colSource)
|
|
3952
5980
|
] }),
|
|
3953
|
-
/* @__PURE__ */
|
|
5981
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3954
5982
|
"".padEnd(colName, "-"),
|
|
3955
5983
|
"---",
|
|
3956
5984
|
"".padEnd(colType, "-"),
|
|
@@ -3961,7 +5989,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3961
5989
|
const name = t.function?.name || t.name || "tool";
|
|
3962
5990
|
const type = t.function?.name ? "fn" : t.type || "tool";
|
|
3963
5991
|
const source = t.source || "native";
|
|
3964
|
-
return /* @__PURE__ */
|
|
5992
|
+
return /* @__PURE__ */ jsxs11(Text11, { color: "white", children: [
|
|
3965
5993
|
pad(name, colName),
|
|
3966
5994
|
" | ",
|
|
3967
5995
|
pad(String(type), colType),
|
|
@@ -3973,34 +6001,36 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3973
6001
|
] })
|
|
3974
6002
|
);
|
|
3975
6003
|
}
|
|
3976
|
-
return outBox(/* @__PURE__ */
|
|
6004
|
+
return outBox(/* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
3977
6005
|
"Command not recognized: /",
|
|
3978
6006
|
cmd
|
|
3979
6007
|
] }));
|
|
3980
6008
|
};
|
|
3981
|
-
return /* @__PURE__ */ jsx12(
|
|
6009
|
+
return /* @__PURE__ */ jsx12(Fragment4, { children: render2() });
|
|
3982
6010
|
};
|
|
3983
6011
|
var SlashCommands_default = SlashCommands;
|
|
3984
6012
|
|
|
3985
6013
|
// src/app/agent/utils/update_check.ts
|
|
3986
6014
|
import updateNotifier from "update-notifier";
|
|
3987
|
-
import { readPackageUp } from "read-package-up";
|
|
3988
6015
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3989
|
-
import
|
|
3990
|
-
import
|
|
3991
|
-
|
|
6016
|
+
import path16 from "path";
|
|
6017
|
+
import fs12 from "fs";
|
|
6018
|
+
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
6019
|
+
function findBlumaPackageJson(startDir) {
|
|
3992
6020
|
let dir = startDir;
|
|
3993
|
-
for (let i = 0; i <
|
|
3994
|
-
const candidate =
|
|
3995
|
-
if (
|
|
6021
|
+
for (let i = 0; i < 10; i++) {
|
|
6022
|
+
const candidate = path16.join(dir, "package.json");
|
|
6023
|
+
if (fs12.existsSync(candidate)) {
|
|
3996
6024
|
try {
|
|
3997
|
-
const raw =
|
|
6025
|
+
const raw = fs12.readFileSync(candidate, "utf8");
|
|
3998
6026
|
const parsed = JSON.parse(raw);
|
|
3999
|
-
if (parsed?.name && parsed?.version)
|
|
6027
|
+
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
6028
|
+
return { name: parsed.name, version: parsed.version };
|
|
6029
|
+
}
|
|
4000
6030
|
} catch {
|
|
4001
6031
|
}
|
|
4002
6032
|
}
|
|
4003
|
-
const parent =
|
|
6033
|
+
const parent = path16.dirname(dir);
|
|
4004
6034
|
if (parent === dir) break;
|
|
4005
6035
|
dir = parent;
|
|
4006
6036
|
}
|
|
@@ -4012,20 +6042,18 @@ async function checkForUpdates() {
|
|
|
4012
6042
|
return String(process.env.BLUMA_FORCE_UPDATE_MSG);
|
|
4013
6043
|
}
|
|
4014
6044
|
const binPath = process.argv?.[1];
|
|
4015
|
-
let pkg;
|
|
4016
|
-
if (binPath &&
|
|
4017
|
-
|
|
4018
|
-
if (candidatePkg?.name && candidatePkg?.version) {
|
|
4019
|
-
pkg = candidatePkg;
|
|
4020
|
-
}
|
|
6045
|
+
let pkg = null;
|
|
6046
|
+
if (binPath && fs12.existsSync(binPath)) {
|
|
6047
|
+
pkg = findBlumaPackageJson(path16.dirname(binPath));
|
|
4021
6048
|
}
|
|
4022
6049
|
if (!pkg) {
|
|
4023
6050
|
const __filename = fileURLToPath3(import.meta.url);
|
|
4024
|
-
const __dirname =
|
|
4025
|
-
|
|
4026
|
-
|
|
6051
|
+
const __dirname = path16.dirname(__filename);
|
|
6052
|
+
pkg = findBlumaPackageJson(__dirname);
|
|
6053
|
+
}
|
|
6054
|
+
if (!pkg) {
|
|
6055
|
+
return null;
|
|
4027
6056
|
}
|
|
4028
|
-
if (!pkg?.name || !pkg?.version) return null;
|
|
4029
6057
|
const isCI = Boolean(process.env.CI);
|
|
4030
6058
|
const updateCheckInterval = 0;
|
|
4031
6059
|
const notifier = updateNotifier({
|
|
@@ -4037,20 +6065,19 @@ async function checkForUpdates() {
|
|
|
4037
6065
|
const cur = notifier.update.current;
|
|
4038
6066
|
const lat = notifier.update.latest;
|
|
4039
6067
|
if (cur && lat && cur !== lat) {
|
|
4040
|
-
return `Update available for
|
|
4041
|
-
|
|
6068
|
+
return `Update available for BluMa CLI! ${cur} \u2192 ${lat}
|
|
6069
|
+
Run: npm i -g ${BLUMA_PACKAGE_NAME} to update.`;
|
|
4042
6070
|
}
|
|
4043
6071
|
}
|
|
4044
6072
|
return null;
|
|
4045
6073
|
} catch (e) {
|
|
4046
|
-
console.warn("Update check failed:", e);
|
|
4047
6074
|
return null;
|
|
4048
6075
|
}
|
|
4049
6076
|
}
|
|
4050
6077
|
|
|
4051
6078
|
// src/app/ui/components/UpdateNotice.tsx
|
|
4052
6079
|
import { Box as Box13, Text as Text12 } from "ink";
|
|
4053
|
-
import { jsx as jsx13, jsxs as
|
|
6080
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4054
6081
|
function parseUpdateMessage(msg) {
|
|
4055
6082
|
const lines = msg.split(/\r?\n/).map((l) => l.trim());
|
|
4056
6083
|
const first = lines[0] || "";
|
|
@@ -4069,7 +6096,7 @@ function parseUpdateMessage(msg) {
|
|
|
4069
6096
|
}
|
|
4070
6097
|
var UpdateNotice = ({ message }) => {
|
|
4071
6098
|
const { name, current, latest, hint } = parseUpdateMessage(message);
|
|
4072
|
-
return /* @__PURE__ */
|
|
6099
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
4073
6100
|
/* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: "Update Available" }),
|
|
4074
6101
|
name && current && latest ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: `${name}: ${current} \u2192 ${latest}` }) : /* @__PURE__ */ jsx13(Text12, { color: "gray", children: message }),
|
|
4075
6102
|
hint ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: hint }) : null
|
|
@@ -4079,54 +6106,93 @@ var UpdateNotice_default = UpdateNotice;
|
|
|
4079
6106
|
|
|
4080
6107
|
// src/app/ui/components/ErrorMessage.tsx
|
|
4081
6108
|
import { Box as Box14, Text as Text13 } from "ink";
|
|
4082
|
-
import { jsx as jsx14, jsxs as
|
|
6109
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4083
6110
|
var ErrorMessage = ({ message, details, hint }) => {
|
|
4084
|
-
return /* @__PURE__ */
|
|
4085
|
-
Box14,
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
details ? /* @__PURE__ */ jsx14(Text13, { color: "red", dimColor: true, children: details }) : null,
|
|
4097
|
-
hint ? /* @__PURE__ */ jsxs12(Text13, { color: "gray", children: [
|
|
4098
|
-
"Hint: ",
|
|
4099
|
-
hint
|
|
4100
|
-
] }) : null
|
|
4101
|
-
]
|
|
4102
|
-
}
|
|
4103
|
-
);
|
|
6111
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
6112
|
+
/* @__PURE__ */ jsxs13(Box14, { children: [
|
|
6113
|
+
/* @__PURE__ */ jsx14(Text13, { color: "red", children: "\u2717" }),
|
|
6114
|
+
/* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: " Error" })
|
|
6115
|
+
] }),
|
|
6116
|
+
/* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message }) }),
|
|
6117
|
+
details && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: details }) }),
|
|
6118
|
+
hint && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
|
|
6119
|
+
"hint: ",
|
|
6120
|
+
hint
|
|
6121
|
+
] }) })
|
|
6122
|
+
] });
|
|
4104
6123
|
};
|
|
4105
6124
|
var ErrorMessage_default = ErrorMessage;
|
|
4106
6125
|
|
|
6126
|
+
// src/app/ui/components/ReasoningDisplay.tsx
|
|
6127
|
+
import { memo as memo9, useState as useState5 } from "react";
|
|
6128
|
+
import { Box as Box15, Text as Text14 } from "ink";
|
|
6129
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
6130
|
+
var ReasoningDisplayComponent = ({
|
|
6131
|
+
reasoning,
|
|
6132
|
+
collapsed = false
|
|
6133
|
+
}) => {
|
|
6134
|
+
const [isExpanded, setIsExpanded] = useState5(!collapsed);
|
|
6135
|
+
if (!reasoning || reasoning.trim() === "") {
|
|
6136
|
+
return null;
|
|
6137
|
+
}
|
|
6138
|
+
const maxLines = 5;
|
|
6139
|
+
const lines = reasoning.split("\n");
|
|
6140
|
+
const displayText = isExpanded ? reasoning : lines.slice(0, maxLines).join("\n") + (lines.length > maxLines ? "..." : "");
|
|
6141
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
6142
|
+
/* @__PURE__ */ jsxs14(Box15, { children: [
|
|
6143
|
+
/* @__PURE__ */ jsx15(Text14, { color: "white", children: " Thinking" }),
|
|
6144
|
+
lines.length > maxLines && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
6145
|
+
" [",
|
|
6146
|
+
lines.length,
|
|
6147
|
+
" lines]"
|
|
6148
|
+
] })
|
|
6149
|
+
] }),
|
|
6150
|
+
/* @__PURE__ */ jsx15(Box15, { paddingLeft: 2, flexDirection: "column", children: displayText.split("\n").map((line, i) => /* @__PURE__ */ jsx15(Text14, { color: "gray", dimColor: true, children: line }, i)) })
|
|
6151
|
+
] });
|
|
6152
|
+
};
|
|
6153
|
+
var ReasoningDisplay = memo9(ReasoningDisplayComponent);
|
|
6154
|
+
|
|
4107
6155
|
// src/app/ui/App.tsx
|
|
4108
|
-
import { jsx as
|
|
6156
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
6157
|
+
var SAFE_AUTO_APPROVE_TOOLS = [
|
|
6158
|
+
// Comunicação/UI
|
|
6159
|
+
"message_notify_user",
|
|
6160
|
+
"agent_end_turn",
|
|
6161
|
+
"todo",
|
|
6162
|
+
"task_boundary",
|
|
6163
|
+
// Leitura de ficheiros (read-only)
|
|
6164
|
+
"ls_tool",
|
|
6165
|
+
"read_file_lines",
|
|
6166
|
+
"count_file_lines",
|
|
6167
|
+
"view_file_outline",
|
|
6168
|
+
"find_by_name",
|
|
6169
|
+
"grep_search",
|
|
6170
|
+
// Status de comandos (read-only)
|
|
6171
|
+
"command_status"
|
|
6172
|
+
];
|
|
4109
6173
|
var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
4110
6174
|
const agentInstance = useRef4(null);
|
|
4111
|
-
const [history, setHistory] =
|
|
4112
|
-
const [statusMessage, setStatusMessage] =
|
|
6175
|
+
const [history, setHistory] = useState6([]);
|
|
6176
|
+
const [statusMessage, setStatusMessage] = useState6(
|
|
4113
6177
|
"Initializing agent..."
|
|
4114
6178
|
);
|
|
4115
|
-
const [toolsCount, setToolsCount] =
|
|
4116
|
-
const [mcpStatus, setMcpStatus] =
|
|
6179
|
+
const [toolsCount, setToolsCount] = useState6(null);
|
|
6180
|
+
const [mcpStatus, setMcpStatus] = useState6(
|
|
4117
6181
|
"connecting"
|
|
4118
6182
|
);
|
|
4119
|
-
const [isProcessing, setIsProcessing] =
|
|
4120
|
-
const [pendingConfirmation, setPendingConfirmation] =
|
|
6183
|
+
const [isProcessing, setIsProcessing] = useState6(true);
|
|
6184
|
+
const [pendingConfirmation, setPendingConfirmation] = useState6(
|
|
4121
6185
|
null
|
|
4122
6186
|
);
|
|
4123
|
-
const [confirmationPreview, setConfirmationPreview] =
|
|
6187
|
+
const [confirmationPreview, setConfirmationPreview] = useState6(
|
|
4124
6188
|
null
|
|
4125
6189
|
);
|
|
4126
|
-
const [isInitAgentActive, setIsInitAgentActive] =
|
|
6190
|
+
const [isInitAgentActive, setIsInitAgentActive] = useState6(false);
|
|
4127
6191
|
const alwaysAcceptList = useRef4([]);
|
|
4128
6192
|
const workdir = process.cwd();
|
|
4129
6193
|
const updateCheckRan = useRef4(false);
|
|
6194
|
+
const sessionStartTime = useRef4(Date.now());
|
|
6195
|
+
const [toolCallCount, setToolCallCount] = useState6(0);
|
|
4130
6196
|
const handleInterrupt = useCallback2(() => {
|
|
4131
6197
|
if (!isProcessing) return;
|
|
4132
6198
|
eventBus2.emit("user_interrupt");
|
|
@@ -4135,7 +6201,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4135
6201
|
...prev,
|
|
4136
6202
|
{
|
|
4137
6203
|
id: prev.length,
|
|
4138
|
-
component: /* @__PURE__ */
|
|
6204
|
+
component: /* @__PURE__ */ jsx16(Text15, { color: "yellow", children: "-- Task cancelled by dev. --" })
|
|
4139
6205
|
}
|
|
4140
6206
|
]);
|
|
4141
6207
|
}, [isProcessing, eventBus2]);
|
|
@@ -4159,11 +6225,11 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4159
6225
|
...prev,
|
|
4160
6226
|
{
|
|
4161
6227
|
id: prev.length,
|
|
4162
|
-
component: /* @__PURE__ */
|
|
6228
|
+
component: /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { color: "white", dimColor: true, children: text }) })
|
|
4163
6229
|
},
|
|
4164
6230
|
{
|
|
4165
6231
|
id: prev.length + 1,
|
|
4166
|
-
component: /* @__PURE__ */
|
|
6232
|
+
component: /* @__PURE__ */ jsx16(
|
|
4167
6233
|
SlashCommands_default,
|
|
4168
6234
|
{
|
|
4169
6235
|
input: text,
|
|
@@ -4183,8 +6249,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4183
6249
|
id: prev.length,
|
|
4184
6250
|
component: (
|
|
4185
6251
|
// Uma única Box para o espaçamento
|
|
4186
|
-
/* @__PURE__ */
|
|
4187
|
-
/* @__PURE__ */
|
|
6252
|
+
/* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "white", dimColor: true, children: [
|
|
6253
|
+
/* @__PURE__ */ jsxs15(Text15, { color: "white", children: [
|
|
4188
6254
|
">",
|
|
4189
6255
|
" "
|
|
4190
6256
|
] }),
|
|
@@ -4219,7 +6285,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4219
6285
|
[]
|
|
4220
6286
|
);
|
|
4221
6287
|
useEffect5(() => {
|
|
4222
|
-
setHistory([{ id: 0, component: /* @__PURE__ */
|
|
6288
|
+
setHistory([{ id: 0, component: /* @__PURE__ */ jsx16(Header, { sessionId: sessionId2, workdir }) }]);
|
|
4223
6289
|
const initializeAgent = async () => {
|
|
4224
6290
|
try {
|
|
4225
6291
|
agentInstance.current = new Agent(sessionId2, eventBus2);
|
|
@@ -4248,6 +6314,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4248
6314
|
}
|
|
4249
6315
|
if (parsed.type === "confirmation_request") {
|
|
4250
6316
|
const toolToConfirm = parsed.tool_calls[0].function.name;
|
|
6317
|
+
if (SAFE_AUTO_APPROVE_TOOLS.includes(toolToConfirm)) {
|
|
6318
|
+
handleConfirmation("accept", parsed.tool_calls);
|
|
6319
|
+
return;
|
|
6320
|
+
}
|
|
4251
6321
|
if (alwaysAcceptList.current.includes(toolToConfirm)) {
|
|
4252
6322
|
handleConfirmation("accept", parsed.tool_calls);
|
|
4253
6323
|
return;
|
|
@@ -4281,7 +6351,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4281
6351
|
...prev,
|
|
4282
6352
|
{
|
|
4283
6353
|
id: prev.length,
|
|
4284
|
-
component: /* @__PURE__ */
|
|
6354
|
+
component: /* @__PURE__ */ jsx16(UpdateNotice_default, { message: msg })
|
|
4285
6355
|
}
|
|
4286
6356
|
]);
|
|
4287
6357
|
}
|
|
@@ -4295,10 +6365,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4295
6365
|
}
|
|
4296
6366
|
let newComponent = null;
|
|
4297
6367
|
if (parsed.type === "debug") {
|
|
4298
|
-
newComponent = /* @__PURE__ */
|
|
6368
|
+
newComponent = /* @__PURE__ */ jsx16(Text15, { color: "gray", children: parsed.message });
|
|
4299
6369
|
} else if (parsed.type === "protocol_violation") {
|
|
4300
|
-
newComponent = /* @__PURE__ */
|
|
4301
|
-
|
|
6370
|
+
newComponent = /* @__PURE__ */ jsxs15(
|
|
6371
|
+
Box16,
|
|
4302
6372
|
{
|
|
4303
6373
|
borderStyle: "round",
|
|
4304
6374
|
borderColor: "yellow",
|
|
@@ -4306,14 +6376,14 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4306
6376
|
marginBottom: 1,
|
|
4307
6377
|
paddingX: 1,
|
|
4308
6378
|
children: [
|
|
4309
|
-
/* @__PURE__ */
|
|
4310
|
-
/* @__PURE__ */
|
|
4311
|
-
/* @__PURE__ */
|
|
6379
|
+
/* @__PURE__ */ jsx16(Text15, { color: "yellow", bold: true, children: "Protocol Violation" }),
|
|
6380
|
+
/* @__PURE__ */ jsx16(Text15, { color: "gray", children: parsed.content }),
|
|
6381
|
+
/* @__PURE__ */ jsx16(Text15, { color: "yellow", children: parsed.message })
|
|
4312
6382
|
]
|
|
4313
6383
|
}
|
|
4314
6384
|
);
|
|
4315
6385
|
} else if (parsed.type === "error") {
|
|
4316
|
-
newComponent = /* @__PURE__ */
|
|
6386
|
+
newComponent = /* @__PURE__ */ jsx16(
|
|
4317
6387
|
ErrorMessage_default,
|
|
4318
6388
|
{
|
|
4319
6389
|
message: parsed.message,
|
|
@@ -4322,8 +6392,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4322
6392
|
}
|
|
4323
6393
|
);
|
|
4324
6394
|
} else if (parsed.type === "tool_call") {
|
|
4325
|
-
const
|
|
4326
|
-
newComponent = /* @__PURE__ */
|
|
6395
|
+
const nextId2 = history.length;
|
|
6396
|
+
newComponent = /* @__PURE__ */ jsx16(
|
|
4327
6397
|
ToolCallDisplay,
|
|
4328
6398
|
{
|
|
4329
6399
|
toolName: parsed.tool_name,
|
|
@@ -4332,7 +6402,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4332
6402
|
}
|
|
4333
6403
|
);
|
|
4334
6404
|
} else if (parsed.type === "tool_result") {
|
|
4335
|
-
newComponent = /* @__PURE__ */
|
|
6405
|
+
newComponent = /* @__PURE__ */ jsx16(
|
|
4336
6406
|
ToolResultDisplay,
|
|
4337
6407
|
{
|
|
4338
6408
|
toolName: parsed.tool_name,
|
|
@@ -4340,15 +6410,17 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4340
6410
|
}
|
|
4341
6411
|
);
|
|
4342
6412
|
} else if (parsed.type === "user_overlay") {
|
|
4343
|
-
newComponent = /* @__PURE__ */
|
|
4344
|
-
/* @__PURE__ */
|
|
6413
|
+
newComponent = /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
|
|
6414
|
+
/* @__PURE__ */ jsxs15(Text15, { color: "magenta", children: [
|
|
4345
6415
|
">",
|
|
4346
6416
|
" "
|
|
4347
6417
|
] }),
|
|
4348
6418
|
parsed.payload
|
|
4349
6419
|
] }) });
|
|
6420
|
+
} else if (parsed.type === "reasoning") {
|
|
6421
|
+
newComponent = /* @__PURE__ */ jsx16(ReasoningDisplay, { reasoning: parsed.content });
|
|
4350
6422
|
} else if (parsed.type === "log") {
|
|
4351
|
-
newComponent = /* @__PURE__ */
|
|
6423
|
+
newComponent = /* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
|
|
4352
6424
|
"\u2139\uFE0F ",
|
|
4353
6425
|
parsed.message,
|
|
4354
6426
|
parsed.payload ? `: ${parsed.payload}` : ""
|
|
@@ -4381,7 +6453,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4381
6453
|
return;
|
|
4382
6454
|
}
|
|
4383
6455
|
if (pendingConfirmation) {
|
|
4384
|
-
return /* @__PURE__ */
|
|
6456
|
+
return /* @__PURE__ */ jsx16(
|
|
4385
6457
|
ConfirmationPrompt,
|
|
4386
6458
|
{
|
|
4387
6459
|
toolCalls: pendingConfirmation,
|
|
@@ -4393,9 +6465,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4393
6465
|
}
|
|
4394
6466
|
);
|
|
4395
6467
|
}
|
|
4396
|
-
return /* @__PURE__ */
|
|
4397
|
-
isProcessing && !pendingConfirmation && /* @__PURE__ */
|
|
4398
|
-
/* @__PURE__ */
|
|
6468
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
6469
|
+
isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx16(WorkingTimer, {}),
|
|
6470
|
+
/* @__PURE__ */ jsx16(
|
|
4399
6471
|
InputPrompt,
|
|
4400
6472
|
{
|
|
4401
6473
|
onSubmit: handleSubmit,
|
|
@@ -4406,12 +6478,12 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4406
6478
|
)
|
|
4407
6479
|
] });
|
|
4408
6480
|
};
|
|
4409
|
-
return /* @__PURE__ */
|
|
4410
|
-
/* @__PURE__ */
|
|
6481
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
6482
|
+
/* @__PURE__ */ jsx16(Static, { items: history, children: (item) => /* @__PURE__ */ jsx16(Box16, { children: item.component }, item.id) }),
|
|
4411
6483
|
renderInteractiveComponent()
|
|
4412
6484
|
] });
|
|
4413
6485
|
};
|
|
4414
|
-
var App =
|
|
6486
|
+
var App = memo10(AppComponent);
|
|
4415
6487
|
var App_default = App;
|
|
4416
6488
|
|
|
4417
6489
|
// src/app/ui/utils/terminalTitle.ts
|
|
@@ -4473,9 +6545,9 @@ function stopTitleKeeper() {
|
|
|
4473
6545
|
var BLUMA_TITLE = process.env.BLUMA_TITLE || "BluMa - NomadEngenuity";
|
|
4474
6546
|
startTitleKeeper(BLUMA_TITLE);
|
|
4475
6547
|
var eventBus = new EventEmitter2();
|
|
4476
|
-
var sessionId =
|
|
6548
|
+
var sessionId = uuidv43();
|
|
4477
6549
|
var props = {
|
|
4478
6550
|
eventBus,
|
|
4479
6551
|
sessionId
|
|
4480
6552
|
};
|
|
4481
|
-
render(
|
|
6553
|
+
render(React10.createElement(App_default, props));
|