@nomad-e/bluma-cli 0.0.103 → 0.0.105
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/README.md +66 -44
- package/dist/config/bluma-mcp.json +2 -1
- package/dist/config/native_tools.json +342 -0
- package/dist/main.js +2939 -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, {
|
|
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, { color: "magenta", bold: true, children: "bluma \u2014 coding agent" }),
|
|
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,102 @@ 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
|
+
// === ELEVAÇÃO DE PRIVILÉGIOS ===
|
|
1340
|
+
/^sudo\s+/i,
|
|
1341
|
+
// sudo commands
|
|
1342
|
+
/^doas\s+/i,
|
|
1343
|
+
// doas (BSD sudo alternative)
|
|
1344
|
+
/^su\s+/i,
|
|
1345
|
+
// switch user
|
|
1346
|
+
/^pkexec\s+/i,
|
|
1347
|
+
// PolicyKit execution
|
|
1348
|
+
/\|\s*sudo\s+/i,
|
|
1349
|
+
// piped to sudo
|
|
1350
|
+
/;\s*sudo\s+/i,
|
|
1351
|
+
// chained with sudo
|
|
1352
|
+
/&&\s*sudo\s+/i,
|
|
1353
|
+
// AND chained with sudo
|
|
1354
|
+
// === COMANDOS DESTRUTIVOS ===
|
|
1355
|
+
/\brm\s+(-[rf]+\s+)*[\/~]/i,
|
|
1356
|
+
// rm com paths perigosos (/, ~)
|
|
1357
|
+
/\brm\s+-[rf]*\s+\*/i,
|
|
1358
|
+
// rm -rf *
|
|
1359
|
+
/\bchmod\s+(777|666)\s+\//i,
|
|
1360
|
+
// chmod 777/666 em paths root
|
|
1361
|
+
/\bchown\s+.*\s+\//i,
|
|
1362
|
+
// chown em paths root
|
|
1363
|
+
/\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i,
|
|
1364
|
+
// dd para discos
|
|
1365
|
+
/\bmkfs\./i,
|
|
1366
|
+
// format filesystem
|
|
1367
|
+
/>\s*\/dev\/(sd|hd|nvme)/i,
|
|
1368
|
+
// redirect para disco
|
|
1369
|
+
/\bshred\s+/i,
|
|
1370
|
+
// secure delete
|
|
1371
|
+
// === FORK BOMB / RESOURCE EXHAUSTION ===
|
|
1372
|
+
/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/,
|
|
1373
|
+
// fork bomb clássico
|
|
1374
|
+
/\bwhile\s+true\s*;\s*do/i,
|
|
1375
|
+
// infinite loop
|
|
1376
|
+
/\byes\s+\|/i,
|
|
1377
|
+
// yes piped (resource exhaustion)
|
|
1378
|
+
// === NETWORK PERIGOSO ===
|
|
1379
|
+
/\bcurl\s+.*\|\s*(ba)?sh/i,
|
|
1380
|
+
// curl | bash (remote code exec)
|
|
1381
|
+
/\bwget\s+.*\|\s*(ba)?sh/i,
|
|
1382
|
+
// wget | bash
|
|
1383
|
+
/\bnc\s+-[el]/i
|
|
1384
|
+
// netcat listener (backdoor)
|
|
1385
|
+
];
|
|
1386
|
+
var INTERACTIVE_PATTERNS = [
|
|
1387
|
+
/^(vim|vi|nano|emacs|pico)\s*/i,
|
|
1388
|
+
// editors
|
|
1389
|
+
/^(less|more|most)\s*/i,
|
|
1390
|
+
// pagers
|
|
1391
|
+
/^(top|htop|btop|atop|nmon)\s*/i,
|
|
1392
|
+
// monitoring tools
|
|
1393
|
+
/^(ssh|telnet|ftp|sftp)\s+/i,
|
|
1394
|
+
// remote connections
|
|
1395
|
+
/^(mysql|psql|redis-cli|mongo|mongosh)\s*$/i,
|
|
1396
|
+
// database CLIs (sem script)
|
|
1397
|
+
/^(python|python3|node|ruby|irb|lua)\s*$/i,
|
|
1398
|
+
// REPLs sem script
|
|
1399
|
+
/^(gdb|lldb)\s*/i,
|
|
1400
|
+
// debuggers
|
|
1401
|
+
/^(bc|dc)\s*$/i
|
|
1402
|
+
// calculators
|
|
1403
|
+
];
|
|
1327
1404
|
function shellCommand(args) {
|
|
1328
1405
|
const {
|
|
1329
1406
|
command,
|
|
@@ -1335,6 +1412,45 @@ function shellCommand(args) {
|
|
|
1335
1412
|
return new Promise((resolve) => {
|
|
1336
1413
|
const startTime = Date.now();
|
|
1337
1414
|
const platform = os.platform();
|
|
1415
|
+
const commandTrimmed = command.trim();
|
|
1416
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
1417
|
+
if (pattern.test(commandTrimmed)) {
|
|
1418
|
+
const result = {
|
|
1419
|
+
status: "blocked",
|
|
1420
|
+
exitCode: null,
|
|
1421
|
+
stdout: "",
|
|
1422
|
+
stderr: `Command blocked: "${commandTrimmed}" requires elevated privileges (sudo/doas). These commands require interactive password input which cannot be handled. Alternatives:
|
|
1423
|
+
1. Run the command manually in terminal
|
|
1424
|
+
2. Configure passwordless sudo for this specific command
|
|
1425
|
+
3. Run BluMa as root (not recommended)`,
|
|
1426
|
+
command,
|
|
1427
|
+
cwd,
|
|
1428
|
+
platform,
|
|
1429
|
+
duration: Date.now() - startTime,
|
|
1430
|
+
blockedReason: "requires_sudo"
|
|
1431
|
+
};
|
|
1432
|
+
return resolve(formatResult(result, verbose));
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
1436
|
+
if (pattern.test(commandTrimmed)) {
|
|
1437
|
+
const result = {
|
|
1438
|
+
status: "blocked",
|
|
1439
|
+
exitCode: null,
|
|
1440
|
+
stdout: "",
|
|
1441
|
+
stderr: `Command blocked: "${commandTrimmed}" is an interactive command. Interactive commands require user input and cannot be handled by the agent. Alternatives:
|
|
1442
|
+
1. Use non-interactive alternatives (e.g., 'cat' instead of 'less')
|
|
1443
|
+
2. Run the command manually in terminal
|
|
1444
|
+
3. For editors, use edit_tool instead`,
|
|
1445
|
+
command,
|
|
1446
|
+
cwd,
|
|
1447
|
+
platform,
|
|
1448
|
+
duration: Date.now() - startTime,
|
|
1449
|
+
blockedReason: "interactive_command"
|
|
1450
|
+
};
|
|
1451
|
+
return resolve(formatResult(result, verbose));
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1338
1454
|
let shellCmd;
|
|
1339
1455
|
let shellArgs;
|
|
1340
1456
|
if (platform === "win32") {
|
|
@@ -1346,6 +1462,8 @@ function shellCommand(args) {
|
|
|
1346
1462
|
}
|
|
1347
1463
|
let stdout = "";
|
|
1348
1464
|
let stderr = "";
|
|
1465
|
+
let stdoutTruncated = false;
|
|
1466
|
+
let stderrTruncated = false;
|
|
1349
1467
|
let timedOut = false;
|
|
1350
1468
|
let finished = false;
|
|
1351
1469
|
const childProcess = spawn(shellCmd, shellArgs, {
|
|
@@ -1367,12 +1485,24 @@ function shellCommand(args) {
|
|
|
1367
1485
|
}, timeout * 1e3);
|
|
1368
1486
|
if (childProcess.stdout) {
|
|
1369
1487
|
childProcess.stdout.on("data", (data) => {
|
|
1370
|
-
|
|
1488
|
+
if (!stdoutTruncated) {
|
|
1489
|
+
stdout += data.toString();
|
|
1490
|
+
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
1491
|
+
stdout = stdout.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1492
|
+
stdoutTruncated = true;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1371
1495
|
});
|
|
1372
1496
|
}
|
|
1373
1497
|
if (childProcess.stderr) {
|
|
1374
1498
|
childProcess.stderr.on("data", (data) => {
|
|
1375
|
-
|
|
1499
|
+
if (!stderrTruncated) {
|
|
1500
|
+
stderr += data.toString();
|
|
1501
|
+
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
1502
|
+
stderr = stderr.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1503
|
+
stderrTruncated = true;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1376
1506
|
});
|
|
1377
1507
|
}
|
|
1378
1508
|
childProcess.on("error", (error) => {
|
|
@@ -1405,7 +1535,8 @@ ${stderr.trim()}` : stderr.trim(),
|
|
|
1405
1535
|
command,
|
|
1406
1536
|
cwd,
|
|
1407
1537
|
platform,
|
|
1408
|
-
duration: Date.now() - startTime
|
|
1538
|
+
duration: Date.now() - startTime,
|
|
1539
|
+
truncated: stdoutTruncated || stderrTruncated
|
|
1409
1540
|
};
|
|
1410
1541
|
resolve(formatResult(result, verbose));
|
|
1411
1542
|
}
|
|
@@ -1429,6 +1560,12 @@ function formatResult(result, verbose) {
|
|
|
1429
1560
|
if (result.status === "timeout") {
|
|
1430
1561
|
output.message = `Command exceeded timeout of ${result.duration / 1e3}s`;
|
|
1431
1562
|
}
|
|
1563
|
+
if (result.status === "blocked" && result.blockedReason) {
|
|
1564
|
+
output.blockedReason = result.blockedReason;
|
|
1565
|
+
}
|
|
1566
|
+
if (result.truncated) {
|
|
1567
|
+
output.warning = "Output was truncated due to size limits (100KB max per stream)";
|
|
1568
|
+
}
|
|
1432
1569
|
return JSON.stringify(output, null, 2);
|
|
1433
1570
|
}
|
|
1434
1571
|
|
|
@@ -1464,12 +1601,19 @@ function unescapeLlmString(inputString) {
|
|
|
1464
1601
|
return inputString;
|
|
1465
1602
|
}
|
|
1466
1603
|
}
|
|
1467
|
-
function
|
|
1468
|
-
if (search === "") return text;
|
|
1469
|
-
|
|
1470
|
-
|
|
1604
|
+
function replaceNOccurrences(text, search, replacement, count) {
|
|
1605
|
+
if (search === "" || count <= 0) return text;
|
|
1606
|
+
let result = text;
|
|
1607
|
+
let replaced = 0;
|
|
1608
|
+
let lastIndex = 0;
|
|
1609
|
+
while (replaced < count) {
|
|
1610
|
+
const index = result.indexOf(search, lastIndex);
|
|
1611
|
+
if (index === -1) break;
|
|
1612
|
+
result = result.substring(0, index) + replacement + result.substring(index + search.length);
|
|
1613
|
+
lastIndex = index + replacement.length;
|
|
1614
|
+
replaced++;
|
|
1471
1615
|
}
|
|
1472
|
-
return
|
|
1616
|
+
return result;
|
|
1473
1617
|
}
|
|
1474
1618
|
function countOccurrences(text, search) {
|
|
1475
1619
|
if (search === "" || text === "") return 0;
|
|
@@ -1483,10 +1627,63 @@ function ensureCorrectEdit(currentContent, originalOldString, originalNewString,
|
|
|
1483
1627
|
return [finalOldString, finalNewString, occurrences];
|
|
1484
1628
|
}
|
|
1485
1629
|
const candidates = [
|
|
1486
|
-
{
|
|
1487
|
-
|
|
1488
|
-
|
|
1630
|
+
{
|
|
1631
|
+
old: unescapeLlmString(originalOldString),
|
|
1632
|
+
new: unescapeLlmString(originalNewString),
|
|
1633
|
+
strategy: "unescape_llm"
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
old: originalOldString.trim(),
|
|
1637
|
+
new: originalNewString.trim(),
|
|
1638
|
+
strategy: "trim"
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
old: unescapeLlmString(originalOldString).trim(),
|
|
1642
|
+
new: unescapeLlmString(originalNewString).trim(),
|
|
1643
|
+
strategy: "unescape_and_trim"
|
|
1644
|
+
}
|
|
1489
1645
|
];
|
|
1646
|
+
const oldWithoutTrailing = originalOldString.split("\n").map((l) => l.trimEnd()).join("\n");
|
|
1647
|
+
const newWithoutTrailing = originalNewString.split("\n").map((l) => l.trimEnd()).join("\n");
|
|
1648
|
+
if (oldWithoutTrailing !== originalOldString) {
|
|
1649
|
+
candidates.push({
|
|
1650
|
+
old: oldWithoutTrailing,
|
|
1651
|
+
new: newWithoutTrailing,
|
|
1652
|
+
strategy: "trim_trailing"
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
const oldTabsToSpaces = originalOldString.replace(/\t/g, " ");
|
|
1656
|
+
const oldSpacesToTabs = originalOldString.replace(/ /g, " ");
|
|
1657
|
+
if (oldTabsToSpaces !== originalOldString) {
|
|
1658
|
+
candidates.push({
|
|
1659
|
+
old: oldTabsToSpaces,
|
|
1660
|
+
new: originalNewString.replace(/\t/g, " "),
|
|
1661
|
+
strategy: "tabs_to_spaces"
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
if (oldSpacesToTabs !== originalOldString) {
|
|
1665
|
+
candidates.push({
|
|
1666
|
+
old: oldSpacesToTabs,
|
|
1667
|
+
new: originalNewString.replace(/ /g, " "),
|
|
1668
|
+
strategy: "spaces_to_tabs"
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
const old2to4 = originalOldString.replace(/^( {2})/gm, " ");
|
|
1672
|
+
const old4to2 = originalOldString.replace(/^( {4})/gm, " ");
|
|
1673
|
+
if (old2to4 !== originalOldString) {
|
|
1674
|
+
candidates.push({
|
|
1675
|
+
old: old2to4,
|
|
1676
|
+
new: originalNewString.replace(/^( {2})/gm, " "),
|
|
1677
|
+
strategy: "indent_2_to_4"
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
if (old4to2 !== originalOldString) {
|
|
1681
|
+
candidates.push({
|
|
1682
|
+
old: old4to2,
|
|
1683
|
+
new: originalNewString.replace(/^( {4})/gm, " "),
|
|
1684
|
+
strategy: "indent_4_to_2"
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1490
1687
|
for (const candidate of candidates) {
|
|
1491
1688
|
if (candidate.old === originalOldString) continue;
|
|
1492
1689
|
const candidateOccurrences = countOccurrences(currentContent, candidate.old);
|
|
@@ -1585,7 +1782,7 @@ ${contentPreview}${currentContent.length > 500 ? "..." : ""}`,
|
|
|
1585
1782
|
if (isNewFile) {
|
|
1586
1783
|
newContentResult = normalizedNewString;
|
|
1587
1784
|
} else if (currentContent !== null) {
|
|
1588
|
-
newContentResult =
|
|
1785
|
+
newContentResult = replaceNOccurrences(currentContent, normalizedOldString, normalizedNewString, expectedReplacements);
|
|
1589
1786
|
}
|
|
1590
1787
|
}
|
|
1591
1788
|
return { currentContent, newContent: newContentResult, occurrences, error, isNewFile };
|
|
@@ -1652,10 +1849,11 @@ async function editTool(args) {
|
|
|
1652
1849
|
file_path
|
|
1653
1850
|
};
|
|
1654
1851
|
}
|
|
1655
|
-
|
|
1852
|
+
const cwd = process.cwd();
|
|
1853
|
+
if (!normalizedFilePath.startsWith(cwd) && !path3.isAbsolute(file_path)) {
|
|
1656
1854
|
return {
|
|
1657
1855
|
success: false,
|
|
1658
|
-
error: `Invalid parameters: file_path
|
|
1856
|
+
error: `Invalid parameters: file_path must be within the current working directory or be an absolute path.`,
|
|
1659
1857
|
file_path: normalizedFilePath
|
|
1660
1858
|
};
|
|
1661
1859
|
}
|
|
@@ -1873,151 +2071,1697 @@ async function countLines(args) {
|
|
|
1873
2071
|
}
|
|
1874
2072
|
|
|
1875
2073
|
// src/app/agent/tools/natives/todo.ts
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
return todos;
|
|
2074
|
+
import * as fs6 from "fs";
|
|
2075
|
+
import * as path5 from "path";
|
|
2076
|
+
var taskStore = [];
|
|
2077
|
+
var nextId = 1;
|
|
2078
|
+
function getTodoFilePath() {
|
|
2079
|
+
return path5.join(process.cwd(), ".bluma", "todo.json");
|
|
1883
2080
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2081
|
+
function loadTasksFromFile() {
|
|
2082
|
+
try {
|
|
2083
|
+
const filePath = getTodoFilePath();
|
|
2084
|
+
if (fs6.existsSync(filePath)) {
|
|
2085
|
+
const data = fs6.readFileSync(filePath, "utf-8");
|
|
2086
|
+
const parsed = JSON.parse(data);
|
|
2087
|
+
if (Array.isArray(parsed.tasks)) {
|
|
2088
|
+
taskStore = parsed.tasks;
|
|
2089
|
+
nextId = parsed.nextId || taskStore.length + 1;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
} catch {
|
|
1894
2093
|
}
|
|
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 = [];
|
|
2094
|
+
}
|
|
2095
|
+
function saveTasksToFile() {
|
|
2096
|
+
try {
|
|
2097
|
+
const filePath = getTodoFilePath();
|
|
2098
|
+
const dir = path5.dirname(filePath);
|
|
2099
|
+
if (!fs6.existsSync(dir)) {
|
|
2100
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
1910
2101
|
}
|
|
2102
|
+
fs6.writeFileSync(filePath, JSON.stringify({
|
|
2103
|
+
tasks: taskStore,
|
|
2104
|
+
nextId,
|
|
2105
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2106
|
+
}, null, 2));
|
|
2107
|
+
} catch {
|
|
1911
2108
|
}
|
|
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." }));
|
|
2109
|
+
}
|
|
2110
|
+
function calculateStats() {
|
|
2111
|
+
const total = taskStore.length;
|
|
2112
|
+
const pending = taskStore.filter((t) => t.status === "pending").length;
|
|
2113
|
+
const inProgress = taskStore.filter((t) => t.status === "in_progress").length;
|
|
2114
|
+
const completed = taskStore.filter((t) => t.status === "completed").length;
|
|
2115
|
+
const progress = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
2116
|
+
return { total, pending, inProgress, completed, progress };
|
|
2117
|
+
}
|
|
2118
|
+
function validateDescription(desc) {
|
|
2119
|
+
if (!desc || typeof desc !== "string") {
|
|
2120
|
+
return "Description is required";
|
|
1925
2121
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
* O MCPClient usará esta função para obter a lista de ferramentas locais.
|
|
1929
|
-
*/
|
|
1930
|
-
getToolDefinitions() {
|
|
1931
|
-
return this.toolDefinitions;
|
|
2122
|
+
if (desc.trim().length === 0) {
|
|
2123
|
+
return "Description cannot be empty";
|
|
1932
2124
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2125
|
+
if (desc.length > 500) {
|
|
2126
|
+
return "Description too long (max 500 chars)";
|
|
2127
|
+
}
|
|
2128
|
+
return null;
|
|
2129
|
+
}
|
|
2130
|
+
function createResult(success, message) {
|
|
2131
|
+
return {
|
|
2132
|
+
success,
|
|
2133
|
+
message,
|
|
2134
|
+
tasks: taskStore,
|
|
2135
|
+
stats: calculateStats()
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
function listTasks() {
|
|
2139
|
+
loadTasksFromFile();
|
|
2140
|
+
const stats = calculateStats();
|
|
2141
|
+
if (taskStore.length === 0) {
|
|
2142
|
+
return createResult(true, "No tasks yet. Use add action to create tasks.");
|
|
2143
|
+
}
|
|
2144
|
+
const progressBar = generateProgressBar(stats.progress);
|
|
2145
|
+
return createResult(true, `${stats.total} tasks ${progressBar} ${stats.progress}%`);
|
|
2146
|
+
}
|
|
2147
|
+
function addTasks(tasks) {
|
|
2148
|
+
if (!tasks || tasks.length === 0) {
|
|
2149
|
+
return createResult(false, "No tasks provided");
|
|
2150
|
+
}
|
|
2151
|
+
loadTasksFromFile();
|
|
2152
|
+
for (const task of tasks) {
|
|
2153
|
+
const error = validateDescription(task.description);
|
|
2154
|
+
if (error) {
|
|
2155
|
+
return createResult(false, `Invalid task: ${error}`);
|
|
1943
2156
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2157
|
+
const newTask = {
|
|
2158
|
+
id: nextId++,
|
|
2159
|
+
description: task.description.trim(),
|
|
2160
|
+
status: task.isComplete ? "completed" : "pending",
|
|
2161
|
+
priority: task.priority || "medium",
|
|
2162
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2163
|
+
completedAt: task.isComplete ? (/* @__PURE__ */ new Date()).toISOString() : void 0
|
|
2164
|
+
};
|
|
2165
|
+
taskStore.push(newTask);
|
|
2166
|
+
}
|
|
2167
|
+
saveTasksToFile();
|
|
2168
|
+
return createResult(true, `Added ${tasks.length} task(s)`);
|
|
2169
|
+
}
|
|
2170
|
+
function completeTask(taskId) {
|
|
2171
|
+
loadTasksFromFile();
|
|
2172
|
+
const task = taskStore.find((t) => t.id === taskId);
|
|
2173
|
+
if (!task) {
|
|
2174
|
+
return createResult(false, `Task #${taskId} not found`);
|
|
2175
|
+
}
|
|
2176
|
+
task.status = "completed";
|
|
2177
|
+
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2178
|
+
saveTasksToFile();
|
|
2179
|
+
return createResult(true, `Completed: ${task.description}`);
|
|
2180
|
+
}
|
|
2181
|
+
function updateTask(taskId, description, priority) {
|
|
2182
|
+
loadTasksFromFile();
|
|
2183
|
+
const task = taskStore.find((t) => t.id === taskId);
|
|
2184
|
+
if (!task) {
|
|
2185
|
+
return createResult(false, `Task #${taskId} not found`);
|
|
2186
|
+
}
|
|
2187
|
+
if (description) {
|
|
2188
|
+
const error = validateDescription(description);
|
|
2189
|
+
if (error) {
|
|
2190
|
+
return createResult(false, error);
|
|
1949
2191
|
}
|
|
2192
|
+
task.description = description.trim();
|
|
1950
2193
|
}
|
|
1951
|
-
|
|
2194
|
+
if (priority) {
|
|
2195
|
+
task.priority = priority;
|
|
2196
|
+
}
|
|
2197
|
+
saveTasksToFile();
|
|
2198
|
+
return createResult(true, `Updated task #${taskId}`);
|
|
2199
|
+
}
|
|
2200
|
+
function removeTask(taskId) {
|
|
2201
|
+
loadTasksFromFile();
|
|
2202
|
+
const index = taskStore.findIndex((t) => t.id === taskId);
|
|
2203
|
+
if (index === -1) {
|
|
2204
|
+
return createResult(false, `Task #${taskId} not found`);
|
|
2205
|
+
}
|
|
2206
|
+
const removed = taskStore.splice(index, 1)[0];
|
|
2207
|
+
saveTasksToFile();
|
|
2208
|
+
return createResult(true, `Removed: ${removed.description}`);
|
|
2209
|
+
}
|
|
2210
|
+
function clearCompleted() {
|
|
2211
|
+
loadTasksFromFile();
|
|
2212
|
+
const before = taskStore.length;
|
|
2213
|
+
taskStore = taskStore.filter((t) => t.status !== "completed");
|
|
2214
|
+
const removed = before - taskStore.length;
|
|
2215
|
+
saveTasksToFile();
|
|
2216
|
+
return createResult(true, `Cleared ${removed} completed task(s)`);
|
|
2217
|
+
}
|
|
2218
|
+
function generateProgressBar(percent) {
|
|
2219
|
+
const width = 10;
|
|
2220
|
+
const filled = Math.round(percent / 100 * width);
|
|
2221
|
+
const empty = width - filled;
|
|
2222
|
+
return "[" + "=".repeat(filled) + " ".repeat(empty) + "]";
|
|
2223
|
+
}
|
|
2224
|
+
async function todo(args) {
|
|
2225
|
+
if ("tasks" in args && !("action" in args)) {
|
|
2226
|
+
loadTasksFromFile();
|
|
2227
|
+
taskStore = [];
|
|
2228
|
+
nextId = 1;
|
|
2229
|
+
if (args.tasks && args.tasks.length > 0) {
|
|
2230
|
+
for (const task of args.tasks) {
|
|
2231
|
+
taskStore.push({
|
|
2232
|
+
id: nextId++,
|
|
2233
|
+
description: task.description,
|
|
2234
|
+
status: task.isComplete ? "completed" : "pending",
|
|
2235
|
+
priority: "medium",
|
|
2236
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2237
|
+
completedAt: task.isComplete ? (/* @__PURE__ */ new Date()).toISOString() : void 0
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
saveTasksToFile();
|
|
2242
|
+
const stats = calculateStats();
|
|
2243
|
+
const progressBar = generateProgressBar(stats.progress);
|
|
2244
|
+
return createResult(true, `Synced ${taskStore.length} tasks ${progressBar} ${stats.progress}%`);
|
|
2245
|
+
}
|
|
2246
|
+
const fullArgs = args;
|
|
2247
|
+
switch (fullArgs.action) {
|
|
2248
|
+
case "list":
|
|
2249
|
+
return listTasks();
|
|
2250
|
+
case "add":
|
|
2251
|
+
return addTasks(fullArgs.tasks);
|
|
2252
|
+
case "complete":
|
|
2253
|
+
if (!fullArgs.taskId) {
|
|
2254
|
+
return createResult(false, "taskId required for complete action");
|
|
2255
|
+
}
|
|
2256
|
+
return completeTask(fullArgs.taskId);
|
|
2257
|
+
case "update":
|
|
2258
|
+
if (!fullArgs.taskId) {
|
|
2259
|
+
return createResult(false, "taskId required for update action");
|
|
2260
|
+
}
|
|
2261
|
+
return updateTask(fullArgs.taskId, fullArgs.description, fullArgs.priority);
|
|
2262
|
+
case "remove":
|
|
2263
|
+
if (!fullArgs.taskId) {
|
|
2264
|
+
return createResult(false, "taskId required for remove action");
|
|
2265
|
+
}
|
|
2266
|
+
return removeTask(fullArgs.taskId);
|
|
2267
|
+
case "clear":
|
|
2268
|
+
return clearCompleted();
|
|
2269
|
+
case "sync":
|
|
2270
|
+
return addTasks(fullArgs.tasks);
|
|
2271
|
+
default:
|
|
2272
|
+
return createResult(false, `Unknown action: ${fullArgs.action}`);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
1952
2275
|
|
|
1953
|
-
// src/app/agent/tools/
|
|
1954
|
-
import { promises as fs7 } from "fs";
|
|
2276
|
+
// src/app/agent/tools/natives/find_by_name.ts
|
|
1955
2277
|
import path6 from "path";
|
|
1956
|
-
import
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2278
|
+
import { promises as fsPromises } from "fs";
|
|
2279
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
2280
|
+
"node_modules",
|
|
2281
|
+
".git",
|
|
2282
|
+
".venv",
|
|
2283
|
+
"venv",
|
|
2284
|
+
"__pycache__",
|
|
2285
|
+
".cache",
|
|
2286
|
+
"dist",
|
|
2287
|
+
"build",
|
|
2288
|
+
".next",
|
|
2289
|
+
".nuxt",
|
|
2290
|
+
"coverage",
|
|
2291
|
+
".nyc_output",
|
|
2292
|
+
".pytest_cache",
|
|
2293
|
+
"target",
|
|
2294
|
+
// Rust
|
|
2295
|
+
"vendor"
|
|
2296
|
+
// Go, PHP
|
|
2297
|
+
];
|
|
2298
|
+
var MAX_RESULTS2 = 100;
|
|
2299
|
+
var MAX_DEPTH_DEFAULT = 10;
|
|
2300
|
+
function globToRegex(glob) {
|
|
2301
|
+
let regex = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{DOUBLESTAR}}/g, ".*");
|
|
2302
|
+
return new RegExp(`^${regex}$`, "i");
|
|
2303
|
+
}
|
|
2304
|
+
function shouldIgnore(name, ignorePatterns, includeHidden) {
|
|
2305
|
+
if (!includeHidden && name.startsWith(".")) {
|
|
2306
|
+
return true;
|
|
1970
2307
|
}
|
|
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
|
-
});
|
|
2308
|
+
for (const pattern of ignorePatterns) {
|
|
2309
|
+
if (name === pattern || name.match(globToRegex(pattern))) {
|
|
2310
|
+
return true;
|
|
1981
2311
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2312
|
+
}
|
|
2313
|
+
return false;
|
|
2314
|
+
}
|
|
2315
|
+
function matchesExtensions(filename, extensions) {
|
|
2316
|
+
if (!extensions || extensions.length === 0) return true;
|
|
2317
|
+
const ext = path6.extname(filename).toLowerCase();
|
|
2318
|
+
return extensions.some((e) => {
|
|
2319
|
+
const normalizedExt = e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`;
|
|
2320
|
+
return ext === normalizedExt;
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
async function searchDirectory(dir, pattern, baseDir, options, results) {
|
|
2324
|
+
if (options.currentDepth > options.maxDepth || results.length >= MAX_RESULTS2) {
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
let entries;
|
|
2328
|
+
try {
|
|
2329
|
+
entries = await fsPromises.readdir(dir, { withFileTypes: true });
|
|
2330
|
+
} catch (error) {
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
for (const entry of entries) {
|
|
2334
|
+
if (results.length >= MAX_RESULTS2) break;
|
|
2335
|
+
const name = entry.name;
|
|
2336
|
+
if (shouldIgnore(name, options.ignorePatterns, options.includeHidden)) {
|
|
2337
|
+
continue;
|
|
1996
2338
|
}
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2339
|
+
const fullPath = path6.join(dir, name);
|
|
2340
|
+
const relativePath = path6.relative(baseDir, fullPath);
|
|
2341
|
+
if (entry.isDirectory()) {
|
|
2342
|
+
if (pattern.test(name)) {
|
|
2343
|
+
results.push({
|
|
2344
|
+
path: fullPath,
|
|
2345
|
+
relative_path: relativePath,
|
|
2346
|
+
type: "directory"
|
|
2003
2347
|
});
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2348
|
+
}
|
|
2349
|
+
await searchDirectory(fullPath, pattern, baseDir, {
|
|
2350
|
+
...options,
|
|
2351
|
+
currentDepth: options.currentDepth + 1
|
|
2352
|
+
}, results);
|
|
2353
|
+
} else if (entry.isFile()) {
|
|
2354
|
+
if (pattern.test(name) && matchesExtensions(name, options.extensions)) {
|
|
2355
|
+
try {
|
|
2356
|
+
const stats = await fsPromises.stat(fullPath);
|
|
2357
|
+
results.push({
|
|
2358
|
+
path: fullPath,
|
|
2359
|
+
relative_path: relativePath,
|
|
2360
|
+
type: "file",
|
|
2361
|
+
size: stats.size,
|
|
2362
|
+
modified: stats.mtime.toISOString()
|
|
2363
|
+
});
|
|
2364
|
+
} catch {
|
|
2365
|
+
results.push({
|
|
2366
|
+
path: fullPath,
|
|
2367
|
+
relative_path: relativePath,
|
|
2368
|
+
type: "file"
|
|
2369
|
+
});
|
|
2008
2370
|
}
|
|
2009
|
-
} catch (error) {
|
|
2010
|
-
this.eventBus.emit("backend_message", {
|
|
2011
|
-
type: "error",
|
|
2012
|
-
message: `Failed to connect to server '${serverName}'.`
|
|
2013
|
-
});
|
|
2014
2371
|
}
|
|
2015
2372
|
}
|
|
2016
2373
|
}
|
|
2017
|
-
|
|
2374
|
+
}
|
|
2375
|
+
async function findByName(args) {
|
|
2376
|
+
try {
|
|
2377
|
+
const {
|
|
2378
|
+
pattern,
|
|
2379
|
+
directory = process.cwd(),
|
|
2380
|
+
extensions,
|
|
2381
|
+
max_depth = MAX_DEPTH_DEFAULT,
|
|
2382
|
+
include_hidden = false,
|
|
2383
|
+
exclude_patterns = []
|
|
2384
|
+
} = args;
|
|
2385
|
+
if (!pattern || typeof pattern !== "string") {
|
|
2386
|
+
return {
|
|
2387
|
+
success: false,
|
|
2388
|
+
pattern: pattern || "",
|
|
2389
|
+
directory,
|
|
2390
|
+
results: [],
|
|
2391
|
+
total_found: 0,
|
|
2392
|
+
truncated: false,
|
|
2393
|
+
error: "Pattern is required and must be a string"
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
const resolvedDir = path6.resolve(directory);
|
|
2018
2397
|
try {
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2398
|
+
const stats = await fsPromises.stat(resolvedDir);
|
|
2399
|
+
if (!stats.isDirectory()) {
|
|
2400
|
+
return {
|
|
2401
|
+
success: false,
|
|
2402
|
+
pattern,
|
|
2403
|
+
directory: resolvedDir,
|
|
2404
|
+
results: [],
|
|
2405
|
+
total_found: 0,
|
|
2406
|
+
truncated: false,
|
|
2407
|
+
error: `Path is not a directory: ${resolvedDir}`
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
} catch (error) {
|
|
2411
|
+
return {
|
|
2412
|
+
success: false,
|
|
2413
|
+
pattern,
|
|
2414
|
+
directory: resolvedDir,
|
|
2415
|
+
results: [],
|
|
2416
|
+
total_found: 0,
|
|
2417
|
+
truncated: false,
|
|
2418
|
+
error: `Directory not found: ${resolvedDir}`
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
const ignorePatterns = [...DEFAULT_IGNORE_PATTERNS, ...exclude_patterns];
|
|
2422
|
+
const patternRegex = globToRegex(pattern);
|
|
2423
|
+
const results = [];
|
|
2424
|
+
await searchDirectory(resolvedDir, patternRegex, resolvedDir, {
|
|
2425
|
+
extensions,
|
|
2426
|
+
maxDepth: max_depth,
|
|
2427
|
+
currentDepth: 0,
|
|
2428
|
+
includeHidden: include_hidden,
|
|
2429
|
+
ignorePatterns
|
|
2430
|
+
}, results);
|
|
2431
|
+
results.sort((a, b) => {
|
|
2432
|
+
if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
|
|
2433
|
+
return a.relative_path.localeCompare(b.relative_path);
|
|
2434
|
+
});
|
|
2435
|
+
return {
|
|
2436
|
+
success: true,
|
|
2437
|
+
pattern,
|
|
2438
|
+
directory: resolvedDir,
|
|
2439
|
+
results,
|
|
2440
|
+
total_found: results.length,
|
|
2441
|
+
truncated: results.length >= MAX_RESULTS2
|
|
2442
|
+
};
|
|
2443
|
+
} catch (error) {
|
|
2444
|
+
return {
|
|
2445
|
+
success: false,
|
|
2446
|
+
pattern: args.pattern || "",
|
|
2447
|
+
directory: args.directory || process.cwd(),
|
|
2448
|
+
results: [],
|
|
2449
|
+
total_found: 0,
|
|
2450
|
+
truncated: false,
|
|
2451
|
+
error: `Unexpected error: ${error.message}`
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
// src/app/agent/tools/natives/grep_search.ts
|
|
2457
|
+
import path7 from "path";
|
|
2458
|
+
import { promises as fsPromises2 } from "fs";
|
|
2459
|
+
var MAX_RESULTS3 = 100;
|
|
2460
|
+
var MAX_FILE_SIZE2 = 1024 * 1024;
|
|
2461
|
+
var MAX_LINE_LENGTH = 500;
|
|
2462
|
+
var DEFAULT_IGNORE2 = [
|
|
2463
|
+
"node_modules",
|
|
2464
|
+
".git",
|
|
2465
|
+
".venv",
|
|
2466
|
+
"venv",
|
|
2467
|
+
"__pycache__",
|
|
2468
|
+
".cache",
|
|
2469
|
+
"dist",
|
|
2470
|
+
"build",
|
|
2471
|
+
".next",
|
|
2472
|
+
"coverage",
|
|
2473
|
+
"*.min.js",
|
|
2474
|
+
"*.min.css",
|
|
2475
|
+
"*.map",
|
|
2476
|
+
"*.lock",
|
|
2477
|
+
"package-lock.json",
|
|
2478
|
+
"yarn.lock",
|
|
2479
|
+
"pnpm-lock.yaml"
|
|
2480
|
+
];
|
|
2481
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2482
|
+
".ts",
|
|
2483
|
+
".tsx",
|
|
2484
|
+
".js",
|
|
2485
|
+
".jsx",
|
|
2486
|
+
".mjs",
|
|
2487
|
+
".cjs",
|
|
2488
|
+
".py",
|
|
2489
|
+
".pyw",
|
|
2490
|
+
".java",
|
|
2491
|
+
".kt",
|
|
2492
|
+
".scala",
|
|
2493
|
+
".go",
|
|
2494
|
+
".rs",
|
|
2495
|
+
".rb",
|
|
2496
|
+
".php",
|
|
2497
|
+
".cs",
|
|
2498
|
+
".cpp",
|
|
2499
|
+
".c",
|
|
2500
|
+
".h",
|
|
2501
|
+
".hpp",
|
|
2502
|
+
".swift",
|
|
2503
|
+
".vue",
|
|
2504
|
+
".svelte",
|
|
2505
|
+
".html",
|
|
2506
|
+
".htm",
|
|
2507
|
+
".xml",
|
|
2508
|
+
".svg",
|
|
2509
|
+
".css",
|
|
2510
|
+
".scss",
|
|
2511
|
+
".sass",
|
|
2512
|
+
".less",
|
|
2513
|
+
".json",
|
|
2514
|
+
".yaml",
|
|
2515
|
+
".yml",
|
|
2516
|
+
".toml",
|
|
2517
|
+
".md",
|
|
2518
|
+
".mdx",
|
|
2519
|
+
".txt",
|
|
2520
|
+
".rst",
|
|
2521
|
+
".sh",
|
|
2522
|
+
".bash",
|
|
2523
|
+
".zsh",
|
|
2524
|
+
".fish",
|
|
2525
|
+
".sql",
|
|
2526
|
+
".graphql",
|
|
2527
|
+
".gql",
|
|
2528
|
+
".env",
|
|
2529
|
+
".env.local",
|
|
2530
|
+
".env.example",
|
|
2531
|
+
".gitignore",
|
|
2532
|
+
".dockerignore",
|
|
2533
|
+
".eslintrc",
|
|
2534
|
+
".prettierrc",
|
|
2535
|
+
"Dockerfile",
|
|
2536
|
+
"Makefile",
|
|
2537
|
+
"Rakefile"
|
|
2538
|
+
]);
|
|
2539
|
+
function isTextFile(filepath) {
|
|
2540
|
+
const ext = path7.extname(filepath).toLowerCase();
|
|
2541
|
+
const basename = path7.basename(filepath);
|
|
2542
|
+
if (TEXT_EXTENSIONS.has(ext)) return true;
|
|
2543
|
+
if (TEXT_EXTENSIONS.has(basename)) return true;
|
|
2544
|
+
if (basename.startsWith(".") && !ext) return true;
|
|
2545
|
+
return false;
|
|
2546
|
+
}
|
|
2547
|
+
function shouldIgnore2(name) {
|
|
2548
|
+
for (const pattern of DEFAULT_IGNORE2) {
|
|
2549
|
+
if (pattern.includes("*")) {
|
|
2550
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
2551
|
+
if (regex.test(name)) return true;
|
|
2552
|
+
} else if (name === pattern) {
|
|
2553
|
+
return true;
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return false;
|
|
2557
|
+
}
|
|
2558
|
+
function createSearchPattern(query, isRegex, caseInsensitive) {
|
|
2559
|
+
try {
|
|
2560
|
+
const flags = caseInsensitive ? "gi" : "g";
|
|
2561
|
+
if (isRegex) {
|
|
2562
|
+
return new RegExp(query, flags);
|
|
2563
|
+
} else {
|
|
2564
|
+
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2565
|
+
return new RegExp(escaped, flags);
|
|
2566
|
+
}
|
|
2567
|
+
} catch (error) {
|
|
2568
|
+
throw new Error(`Invalid regex pattern: ${error.message}`);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
function matchesIncludePattern(filename, includePatterns) {
|
|
2572
|
+
if (!includePatterns || includePatterns.length === 0) return true;
|
|
2573
|
+
for (const pattern of includePatterns) {
|
|
2574
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$", "i");
|
|
2575
|
+
if (regex.test(filename)) return true;
|
|
2576
|
+
}
|
|
2577
|
+
return false;
|
|
2578
|
+
}
|
|
2579
|
+
async function searchFile(filepath, baseDir, pattern, contextLines, matches, maxResults) {
|
|
2580
|
+
let fileMatches = 0;
|
|
2581
|
+
try {
|
|
2582
|
+
const stats = await fsPromises2.stat(filepath);
|
|
2583
|
+
if (stats.size > MAX_FILE_SIZE2) return 0;
|
|
2584
|
+
const content = await fsPromises2.readFile(filepath, "utf-8");
|
|
2585
|
+
const lines = content.split("\n");
|
|
2586
|
+
const relativePath = path7.relative(baseDir, filepath);
|
|
2587
|
+
for (let i = 0; i < lines.length && matches.length < maxResults; i++) {
|
|
2588
|
+
const line = lines[i];
|
|
2589
|
+
pattern.lastIndex = 0;
|
|
2590
|
+
let match;
|
|
2591
|
+
while ((match = pattern.exec(line)) !== null && matches.length < maxResults) {
|
|
2592
|
+
const lineContent = line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) + "..." : line;
|
|
2593
|
+
const searchMatch = {
|
|
2594
|
+
file: filepath,
|
|
2595
|
+
relative_path: relativePath,
|
|
2596
|
+
line_number: i + 1,
|
|
2597
|
+
line_content: lineContent.trim(),
|
|
2598
|
+
match_start: match.index,
|
|
2599
|
+
match_end: match.index + match[0].length
|
|
2600
|
+
};
|
|
2601
|
+
if (contextLines > 0) {
|
|
2602
|
+
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);
|
|
2603
|
+
searchMatch.context_after = lines.slice(i + 1, i + 1 + contextLines).map((l) => l.length > MAX_LINE_LENGTH ? l.substring(0, MAX_LINE_LENGTH) + "..." : l);
|
|
2604
|
+
}
|
|
2605
|
+
matches.push(searchMatch);
|
|
2606
|
+
fileMatches++;
|
|
2607
|
+
if (!pattern.global) break;
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
} catch (error) {
|
|
2611
|
+
}
|
|
2612
|
+
return fileMatches;
|
|
2613
|
+
}
|
|
2614
|
+
async function searchDirectory2(dir, baseDir, pattern, includePatterns, contextLines, matches, maxResults, stats) {
|
|
2615
|
+
if (matches.length >= maxResults) return;
|
|
2616
|
+
let entries;
|
|
2617
|
+
try {
|
|
2618
|
+
entries = await fsPromises2.readdir(dir, { withFileTypes: true });
|
|
2619
|
+
} catch {
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
for (const entry of entries) {
|
|
2623
|
+
if (matches.length >= maxResults) break;
|
|
2624
|
+
const name = entry.name;
|
|
2625
|
+
if (shouldIgnore2(name)) continue;
|
|
2626
|
+
const fullPath = path7.join(dir, name);
|
|
2627
|
+
if (entry.isDirectory()) {
|
|
2628
|
+
await searchDirectory2(fullPath, baseDir, pattern, includePatterns, contextLines, matches, maxResults, stats);
|
|
2629
|
+
} else if (entry.isFile()) {
|
|
2630
|
+
if (!isTextFile(name)) continue;
|
|
2631
|
+
if (!matchesIncludePattern(name, includePatterns)) continue;
|
|
2632
|
+
stats.filesSearched++;
|
|
2633
|
+
const found = await searchFile(fullPath, baseDir, pattern, contextLines, matches, maxResults);
|
|
2634
|
+
if (found > 0) stats.filesWithMatches++;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
async function grepSearch(args) {
|
|
2639
|
+
try {
|
|
2640
|
+
const {
|
|
2641
|
+
query,
|
|
2642
|
+
path: searchPath,
|
|
2643
|
+
case_insensitive = true,
|
|
2644
|
+
is_regex = false,
|
|
2645
|
+
include_patterns,
|
|
2646
|
+
max_results = MAX_RESULTS3,
|
|
2647
|
+
context_lines = 0
|
|
2648
|
+
} = args;
|
|
2649
|
+
if (!query || typeof query !== "string") {
|
|
2650
|
+
return {
|
|
2651
|
+
success: false,
|
|
2652
|
+
query: query || "",
|
|
2653
|
+
search_path: searchPath || "",
|
|
2654
|
+
matches: [],
|
|
2655
|
+
files_searched: 0,
|
|
2656
|
+
files_with_matches: 0,
|
|
2657
|
+
total_matches: 0,
|
|
2658
|
+
truncated: false,
|
|
2659
|
+
error: "Query is required and must be a string"
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
if (!searchPath) {
|
|
2663
|
+
return {
|
|
2664
|
+
success: false,
|
|
2665
|
+
query,
|
|
2666
|
+
search_path: "",
|
|
2667
|
+
matches: [],
|
|
2668
|
+
files_searched: 0,
|
|
2669
|
+
files_with_matches: 0,
|
|
2670
|
+
total_matches: 0,
|
|
2671
|
+
truncated: false,
|
|
2672
|
+
error: "Search path is required"
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
const resolvedPath = path7.resolve(searchPath);
|
|
2676
|
+
let stats;
|
|
2677
|
+
try {
|
|
2678
|
+
stats = await fsPromises2.stat(resolvedPath);
|
|
2679
|
+
} catch {
|
|
2680
|
+
return {
|
|
2681
|
+
success: false,
|
|
2682
|
+
query,
|
|
2683
|
+
search_path: resolvedPath,
|
|
2684
|
+
matches: [],
|
|
2685
|
+
files_searched: 0,
|
|
2686
|
+
files_with_matches: 0,
|
|
2687
|
+
total_matches: 0,
|
|
2688
|
+
truncated: false,
|
|
2689
|
+
error: `Path not found: ${resolvedPath}`
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
let pattern;
|
|
2693
|
+
try {
|
|
2694
|
+
pattern = createSearchPattern(query, is_regex, case_insensitive);
|
|
2695
|
+
} catch (error) {
|
|
2696
|
+
return {
|
|
2697
|
+
success: false,
|
|
2698
|
+
query,
|
|
2699
|
+
search_path: resolvedPath,
|
|
2700
|
+
matches: [],
|
|
2701
|
+
files_searched: 0,
|
|
2702
|
+
files_with_matches: 0,
|
|
2703
|
+
total_matches: 0,
|
|
2704
|
+
truncated: false,
|
|
2705
|
+
error: error.message
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
const matches = [];
|
|
2709
|
+
const searchStats = { filesSearched: 0, filesWithMatches: 0 };
|
|
2710
|
+
if (stats.isDirectory()) {
|
|
2711
|
+
await searchDirectory2(
|
|
2712
|
+
resolvedPath,
|
|
2713
|
+
resolvedPath,
|
|
2714
|
+
pattern,
|
|
2715
|
+
include_patterns,
|
|
2716
|
+
context_lines,
|
|
2717
|
+
matches,
|
|
2718
|
+
max_results,
|
|
2719
|
+
searchStats
|
|
2720
|
+
);
|
|
2721
|
+
} else if (stats.isFile()) {
|
|
2722
|
+
searchStats.filesSearched = 1;
|
|
2723
|
+
const found = await searchFile(resolvedPath, path7.dirname(resolvedPath), pattern, context_lines, matches, max_results);
|
|
2724
|
+
if (found > 0) searchStats.filesWithMatches = 1;
|
|
2725
|
+
}
|
|
2726
|
+
return {
|
|
2727
|
+
success: true,
|
|
2728
|
+
query,
|
|
2729
|
+
search_path: resolvedPath,
|
|
2730
|
+
matches,
|
|
2731
|
+
files_searched: searchStats.filesSearched,
|
|
2732
|
+
files_with_matches: searchStats.filesWithMatches,
|
|
2733
|
+
total_matches: matches.length,
|
|
2734
|
+
truncated: matches.length >= max_results
|
|
2735
|
+
};
|
|
2736
|
+
} catch (error) {
|
|
2737
|
+
return {
|
|
2738
|
+
success: false,
|
|
2739
|
+
query: args.query || "",
|
|
2740
|
+
search_path: args.path || "",
|
|
2741
|
+
matches: [],
|
|
2742
|
+
files_searched: 0,
|
|
2743
|
+
files_with_matches: 0,
|
|
2744
|
+
total_matches: 0,
|
|
2745
|
+
truncated: false,
|
|
2746
|
+
error: `Unexpected error: ${error.message}`
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
// src/app/agent/tools/natives/view_file_outline.ts
|
|
2752
|
+
import path8 from "path";
|
|
2753
|
+
import { promises as fsPromises3 } from "fs";
|
|
2754
|
+
var LANGUAGE_MAP = {
|
|
2755
|
+
".ts": "typescript",
|
|
2756
|
+
".tsx": "typescript",
|
|
2757
|
+
".js": "javascript",
|
|
2758
|
+
".jsx": "javascript",
|
|
2759
|
+
".mjs": "javascript",
|
|
2760
|
+
".cjs": "javascript",
|
|
2761
|
+
".py": "python",
|
|
2762
|
+
".java": "java",
|
|
2763
|
+
".go": "go",
|
|
2764
|
+
".rs": "rust",
|
|
2765
|
+
".rb": "ruby",
|
|
2766
|
+
".php": "php",
|
|
2767
|
+
".cs": "csharp",
|
|
2768
|
+
".cpp": "cpp",
|
|
2769
|
+
".c": "c",
|
|
2770
|
+
".h": "c",
|
|
2771
|
+
".hpp": "cpp",
|
|
2772
|
+
".swift": "swift",
|
|
2773
|
+
".kt": "kotlin",
|
|
2774
|
+
".scala": "scala"
|
|
2775
|
+
};
|
|
2776
|
+
var PATTERNS = {
|
|
2777
|
+
typescript: [
|
|
2778
|
+
// Classes
|
|
2779
|
+
/^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w,\s]+)?/,
|
|
2780
|
+
// Interfaces
|
|
2781
|
+
/^(?:export\s+)?interface\s+(\w+)/,
|
|
2782
|
+
// Types
|
|
2783
|
+
/^(?:export\s+)?type\s+(\w+)/,
|
|
2784
|
+
// Functions (standalone)
|
|
2785
|
+
/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
|
|
2786
|
+
// Arrow functions assigned to const/let
|
|
2787
|
+
/^(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/,
|
|
2788
|
+
// Methods inside classes (indented)
|
|
2789
|
+
/^\s+(?:public|private|protected|static|async|readonly|\s)*(\w+)\s*\([^)]*\)\s*(?::\s*[\w<>\[\]|&\s]+)?\s*\{/,
|
|
2790
|
+
// Const exports
|
|
2791
|
+
/^(?:export\s+)?const\s+(\w+)\s*[:=]/
|
|
2792
|
+
],
|
|
2793
|
+
javascript: [
|
|
2794
|
+
/^(?:export\s+)?(?:default\s+)?class\s+(\w+)/,
|
|
2795
|
+
/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
|
|
2796
|
+
/^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/,
|
|
2797
|
+
/^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/,
|
|
2798
|
+
/^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=/
|
|
2799
|
+
],
|
|
2800
|
+
python: [
|
|
2801
|
+
/^class\s+(\w+)(?:\([^)]*\))?:/,
|
|
2802
|
+
/^(?:async\s+)?def\s+(\w+)\s*\(/,
|
|
2803
|
+
/^(\w+)\s*=\s*(?:lambda|def)/
|
|
2804
|
+
],
|
|
2805
|
+
java: [
|
|
2806
|
+
/^(?:public|private|protected)?\s*(?:static)?\s*(?:final)?\s*(?:abstract)?\s*class\s+(\w+)/,
|
|
2807
|
+
/^(?:public|private|protected)?\s*(?:static)?\s*(?:final)?\s*interface\s+(\w+)/,
|
|
2808
|
+
/^\s*(?:public|private|protected)?\s*(?:static)?\s*(?:final)?\s*(?:synchronized)?\s*(?:\w+(?:<[^>]+>)?)\s+(\w+)\s*\(/
|
|
2809
|
+
],
|
|
2810
|
+
go: [
|
|
2811
|
+
/^type\s+(\w+)\s+struct\s*\{/,
|
|
2812
|
+
/^type\s+(\w+)\s+interface\s*\{/,
|
|
2813
|
+
/^func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/
|
|
2814
|
+
],
|
|
2815
|
+
rust: [
|
|
2816
|
+
/^(?:pub\s+)?struct\s+(\w+)/,
|
|
2817
|
+
/^(?:pub\s+)?enum\s+(\w+)/,
|
|
2818
|
+
/^(?:pub\s+)?trait\s+(\w+)/,
|
|
2819
|
+
/^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/,
|
|
2820
|
+
/^impl(?:<[^>]+>)?\s+(?:(\w+)|for\s+(\w+))/
|
|
2821
|
+
],
|
|
2822
|
+
ruby: [
|
|
2823
|
+
/^class\s+(\w+)/,
|
|
2824
|
+
/^module\s+(\w+)/,
|
|
2825
|
+
/^def\s+(\w+)/
|
|
2826
|
+
],
|
|
2827
|
+
php: [
|
|
2828
|
+
/^(?:abstract\s+)?class\s+(\w+)/,
|
|
2829
|
+
/^interface\s+(\w+)/,
|
|
2830
|
+
/^(?:public|private|protected)?\s*(?:static)?\s*function\s+(\w+)/
|
|
2831
|
+
],
|
|
2832
|
+
csharp: [
|
|
2833
|
+
/^(?:public|private|protected|internal)?\s*(?:static)?\s*(?:partial)?\s*class\s+(\w+)/,
|
|
2834
|
+
/^(?:public|private|protected|internal)?\s*interface\s+(\w+)/,
|
|
2835
|
+
/^\s*(?:public|private|protected|internal)?\s*(?:static)?\s*(?:async)?\s*(?:\w+(?:<[^>]+>)?)\s+(\w+)\s*\(/
|
|
2836
|
+
],
|
|
2837
|
+
swift: [
|
|
2838
|
+
/^(?:public|private|internal|open)?\s*class\s+(\w+)/,
|
|
2839
|
+
/^(?:public|private|internal)?\s*struct\s+(\w+)/,
|
|
2840
|
+
/^(?:public|private|internal)?\s*protocol\s+(\w+)/,
|
|
2841
|
+
/^(?:public|private|internal)?\s*(?:static)?\s*func\s+(\w+)/
|
|
2842
|
+
],
|
|
2843
|
+
kotlin: [
|
|
2844
|
+
/^(?:open|abstract|sealed)?\s*class\s+(\w+)/,
|
|
2845
|
+
/^interface\s+(\w+)/,
|
|
2846
|
+
/^(?:private|public|internal)?\s*fun\s+(\w+)/
|
|
2847
|
+
],
|
|
2848
|
+
scala: [
|
|
2849
|
+
/^(?:sealed\s+)?(?:abstract\s+)?class\s+(\w+)/,
|
|
2850
|
+
/^object\s+(\w+)/,
|
|
2851
|
+
/^trait\s+(\w+)/,
|
|
2852
|
+
/^def\s+(\w+)/
|
|
2853
|
+
],
|
|
2854
|
+
c: [
|
|
2855
|
+
/^(?:static\s+)?(?:inline\s+)?(?:const\s+)?(?:\w+(?:\s*\*)*)\s+(\w+)\s*\([^)]*\)\s*\{/,
|
|
2856
|
+
/^typedef\s+struct\s*\w*\s*\{[^}]*\}\s*(\w+)/,
|
|
2857
|
+
/^struct\s+(\w+)\s*\{/
|
|
2858
|
+
],
|
|
2859
|
+
cpp: [
|
|
2860
|
+
/^class\s+(\w+)/,
|
|
2861
|
+
/^(?:virtual\s+)?(?:\w+(?:\s*[*&])*)\s+(\w+)\s*\([^)]*\)\s*(?:const)?\s*(?:override)?\s*\{/,
|
|
2862
|
+
/^namespace\s+(\w+)\s*\{/
|
|
2863
|
+
]
|
|
2864
|
+
};
|
|
2865
|
+
function detectLanguage(filepath) {
|
|
2866
|
+
const ext = path8.extname(filepath).toLowerCase();
|
|
2867
|
+
return LANGUAGE_MAP[ext] || "unknown";
|
|
2868
|
+
}
|
|
2869
|
+
function determineItemType(line, language) {
|
|
2870
|
+
const normalizedLine = line.trim().toLowerCase();
|
|
2871
|
+
if (normalizedLine.includes("class ")) return "class";
|
|
2872
|
+
if (normalizedLine.includes("interface ")) return "interface";
|
|
2873
|
+
if (normalizedLine.includes("type ")) return "type";
|
|
2874
|
+
if (normalizedLine.startsWith("import ")) return "import";
|
|
2875
|
+
if (normalizedLine.includes("export ") && !normalizedLine.includes("function") && !normalizedLine.includes("class")) return "export";
|
|
2876
|
+
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
2877
|
+
if (normalizedLine.includes("function") || normalizedLine.includes("def ") || normalizedLine.includes("fn ") || normalizedLine.includes("func ")) {
|
|
2878
|
+
return "method";
|
|
2879
|
+
}
|
|
2880
|
+
if (/^\s+\w+\s*\(/.test(line)) return "method";
|
|
2881
|
+
}
|
|
2882
|
+
if (normalizedLine.includes("function ") || normalizedLine.includes("def ") || normalizedLine.includes("fn ") || normalizedLine.includes("func ")) {
|
|
2883
|
+
return "function";
|
|
2884
|
+
}
|
|
2885
|
+
if (normalizedLine.includes("const ") || normalizedLine.includes("let ") || normalizedLine.includes("var ")) {
|
|
2886
|
+
return normalizedLine.includes("=>") ? "function" : "const";
|
|
2887
|
+
}
|
|
2888
|
+
return "function";
|
|
2889
|
+
}
|
|
2890
|
+
async function extractOutline(filepath, content, language) {
|
|
2891
|
+
const items = [];
|
|
2892
|
+
const lines = content.split("\n");
|
|
2893
|
+
const patterns = PATTERNS[language] || PATTERNS.javascript;
|
|
2894
|
+
let currentClass = null;
|
|
2895
|
+
let braceCount = 0;
|
|
2896
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2897
|
+
const line = lines[i];
|
|
2898
|
+
const lineNumber = i + 1;
|
|
2899
|
+
const openBraces = (line.match(/\{/g) || []).length;
|
|
2900
|
+
const closeBraces = (line.match(/\}/g) || []).length;
|
|
2901
|
+
braceCount += openBraces - closeBraces;
|
|
2902
|
+
if (braceCount <= 0 && currentClass) {
|
|
2903
|
+
currentClass = null;
|
|
2904
|
+
}
|
|
2905
|
+
for (const pattern of patterns) {
|
|
2906
|
+
const match = line.match(pattern);
|
|
2907
|
+
if (match) {
|
|
2908
|
+
const name = match[1] || match[2];
|
|
2909
|
+
if (!name) continue;
|
|
2910
|
+
const itemType = determineItemType(line, language);
|
|
2911
|
+
if (!itemType) continue;
|
|
2912
|
+
const exported = line.includes("export ");
|
|
2913
|
+
const isInsideClass = currentClass && (line.startsWith(" ") || line.startsWith(" "));
|
|
2914
|
+
const item = {
|
|
2915
|
+
type: itemType,
|
|
2916
|
+
name,
|
|
2917
|
+
line_start: lineNumber,
|
|
2918
|
+
signature: line.trim().substring(0, 120),
|
|
2919
|
+
exported
|
|
2920
|
+
};
|
|
2921
|
+
if (isInsideClass && itemType === "method" && currentClass) {
|
|
2922
|
+
item.parent = currentClass;
|
|
2923
|
+
}
|
|
2924
|
+
if (itemType === "class") {
|
|
2925
|
+
currentClass = name;
|
|
2926
|
+
}
|
|
2927
|
+
items.push(item);
|
|
2928
|
+
break;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
return items;
|
|
2933
|
+
}
|
|
2934
|
+
function generateSummary(items, totalLines) {
|
|
2935
|
+
const counts = {};
|
|
2936
|
+
for (const item of items) {
|
|
2937
|
+
counts[item.type] = (counts[item.type] || 0) + 1;
|
|
2938
|
+
}
|
|
2939
|
+
const parts = [`${totalLines} lines`];
|
|
2940
|
+
if (counts.class) parts.push(`${counts.class} class${counts.class > 1 ? "es" : ""}`);
|
|
2941
|
+
if (counts.interface) parts.push(`${counts.interface} interface${counts.interface > 1 ? "s" : ""}`);
|
|
2942
|
+
if (counts.function) parts.push(`${counts.function} function${counts.function > 1 ? "s" : ""}`);
|
|
2943
|
+
if (counts.method) parts.push(`${counts.method} method${counts.method > 1 ? "s" : ""}`);
|
|
2944
|
+
if (counts.type) parts.push(`${counts.type} type${counts.type > 1 ? "s" : ""}`);
|
|
2945
|
+
if (counts.const) parts.push(`${counts.const} const${counts.const > 1 ? "s" : ""}`);
|
|
2946
|
+
return parts.join(", ");
|
|
2947
|
+
}
|
|
2948
|
+
async function viewFileOutline(args) {
|
|
2949
|
+
try {
|
|
2950
|
+
const { file_path } = args;
|
|
2951
|
+
if (!file_path || typeof file_path !== "string") {
|
|
2952
|
+
return {
|
|
2953
|
+
success: false,
|
|
2954
|
+
file_path: file_path || "",
|
|
2955
|
+
language: "unknown",
|
|
2956
|
+
total_lines: 0,
|
|
2957
|
+
items: [],
|
|
2958
|
+
summary: "",
|
|
2959
|
+
error: "file_path is required and must be a string"
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
const resolvedPath = path8.resolve(file_path);
|
|
2963
|
+
let content;
|
|
2964
|
+
try {
|
|
2965
|
+
content = await fsPromises3.readFile(resolvedPath, "utf-8");
|
|
2966
|
+
} catch (error) {
|
|
2967
|
+
return {
|
|
2968
|
+
success: false,
|
|
2969
|
+
file_path: resolvedPath,
|
|
2970
|
+
language: "unknown",
|
|
2971
|
+
total_lines: 0,
|
|
2972
|
+
items: [],
|
|
2973
|
+
summary: "",
|
|
2974
|
+
error: error.code === "ENOENT" ? `File not found: ${resolvedPath}` : `Error reading file: ${error.message}`
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
const language = detectLanguage(resolvedPath);
|
|
2978
|
+
const lines = content.split("\n");
|
|
2979
|
+
const totalLines = lines.length;
|
|
2980
|
+
const items = await extractOutline(resolvedPath, content, language);
|
|
2981
|
+
return {
|
|
2982
|
+
success: true,
|
|
2983
|
+
file_path: resolvedPath,
|
|
2984
|
+
language,
|
|
2985
|
+
total_lines: totalLines,
|
|
2986
|
+
items,
|
|
2987
|
+
summary: generateSummary(items, totalLines)
|
|
2988
|
+
};
|
|
2989
|
+
} catch (error) {
|
|
2990
|
+
return {
|
|
2991
|
+
success: false,
|
|
2992
|
+
file_path: args.file_path || "",
|
|
2993
|
+
language: "unknown",
|
|
2994
|
+
total_lines: 0,
|
|
2995
|
+
items: [],
|
|
2996
|
+
summary: "",
|
|
2997
|
+
error: `Unexpected error: ${error.message}`
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
// src/app/agent/tools/natives/async_command.ts
|
|
3003
|
+
import os3 from "os";
|
|
3004
|
+
import { spawn as spawn2 } from "child_process";
|
|
3005
|
+
import { v4 as uuidv42 } from "uuid";
|
|
3006
|
+
var runningCommands = /* @__PURE__ */ new Map();
|
|
3007
|
+
var MAX_OUTPUT_SIZE2 = 1e5;
|
|
3008
|
+
var MAX_STORED_COMMANDS = 50;
|
|
3009
|
+
var OUTPUT_TRUNCATION_MSG = "\n[OUTPUT TRUNCATED]";
|
|
3010
|
+
var DANGEROUS_PATTERNS2 = [
|
|
3011
|
+
/^sudo\s+/i,
|
|
3012
|
+
/^doas\s+/i,
|
|
3013
|
+
/^su\s+/i,
|
|
3014
|
+
/\|\s*sudo\s+/i
|
|
3015
|
+
];
|
|
3016
|
+
var INTERACTIVE_PATTERNS2 = [
|
|
3017
|
+
/^(vim|vi|nano|emacs|less|more)\s*/i,
|
|
3018
|
+
/^(top|htop|btop)\s*/i,
|
|
3019
|
+
/^(mysql|psql|redis-cli|mongo)\s*$/i
|
|
3020
|
+
];
|
|
3021
|
+
function cleanupOldCommands() {
|
|
3022
|
+
if (runningCommands.size <= MAX_STORED_COMMANDS) return;
|
|
3023
|
+
const commands = Array.from(runningCommands.entries()).filter(([_, cmd]) => cmd.status !== "running").sort((a, b) => (a[1].endTime || 0) - (b[1].endTime || 0));
|
|
3024
|
+
const toRemove = commands.slice(0, commands.length - MAX_STORED_COMMANDS + 10);
|
|
3025
|
+
for (const [id] of toRemove) {
|
|
3026
|
+
runningCommands.delete(id);
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
function isDangerousCommand(command) {
|
|
3030
|
+
const trimmed = command.trim();
|
|
3031
|
+
for (const pattern of DANGEROUS_PATTERNS2) {
|
|
3032
|
+
if (pattern.test(trimmed)) {
|
|
3033
|
+
return "Command requires elevated privileges (sudo/doas) which cannot be handled in async mode";
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
for (const pattern of INTERACTIVE_PATTERNS2) {
|
|
3037
|
+
if (pattern.test(trimmed)) {
|
|
3038
|
+
return "Interactive commands are not supported in async mode";
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
return null;
|
|
3042
|
+
}
|
|
3043
|
+
async function runCommandAsync(args) {
|
|
3044
|
+
try {
|
|
3045
|
+
const {
|
|
3046
|
+
command,
|
|
3047
|
+
cwd = process.cwd(),
|
|
3048
|
+
timeout = 0
|
|
3049
|
+
} = args;
|
|
3050
|
+
if (!command || typeof command !== "string") {
|
|
3051
|
+
return {
|
|
3052
|
+
success: false,
|
|
3053
|
+
error: "command is required and must be a string"
|
|
3054
|
+
};
|
|
3055
|
+
}
|
|
3056
|
+
const dangerReason = isDangerousCommand(command);
|
|
3057
|
+
if (dangerReason) {
|
|
3058
|
+
return {
|
|
3059
|
+
success: false,
|
|
3060
|
+
error: dangerReason
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
const commandId = uuidv42().substring(0, 8);
|
|
3064
|
+
const platform = os3.platform();
|
|
3065
|
+
let shellCmd;
|
|
3066
|
+
let shellArgs;
|
|
3067
|
+
if (platform === "win32") {
|
|
3068
|
+
shellCmd = process.env.COMSPEC || "cmd.exe";
|
|
3069
|
+
shellArgs = ["/c", command];
|
|
3070
|
+
} else {
|
|
3071
|
+
shellCmd = process.env.SHELL || "/bin/bash";
|
|
3072
|
+
shellArgs = ["-c", command];
|
|
3073
|
+
}
|
|
3074
|
+
const entry = {
|
|
3075
|
+
id: commandId,
|
|
3076
|
+
command,
|
|
3077
|
+
status: "running",
|
|
3078
|
+
stdout: "",
|
|
3079
|
+
stderr: "",
|
|
3080
|
+
exitCode: null,
|
|
3081
|
+
startTime: Date.now(),
|
|
3082
|
+
process: null
|
|
3083
|
+
};
|
|
3084
|
+
const child = spawn2(shellCmd, shellArgs, {
|
|
3085
|
+
cwd,
|
|
3086
|
+
env: process.env,
|
|
3087
|
+
windowsHide: true
|
|
3088
|
+
});
|
|
3089
|
+
entry.process = child;
|
|
3090
|
+
let stdoutTruncated = false;
|
|
3091
|
+
child.stdout?.on("data", (data) => {
|
|
3092
|
+
if (!stdoutTruncated) {
|
|
3093
|
+
entry.stdout += data.toString();
|
|
3094
|
+
if (entry.stdout.length > MAX_OUTPUT_SIZE2) {
|
|
3095
|
+
entry.stdout = entry.stdout.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MSG;
|
|
3096
|
+
stdoutTruncated = true;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
});
|
|
3100
|
+
let stderrTruncated = false;
|
|
3101
|
+
child.stderr?.on("data", (data) => {
|
|
3102
|
+
if (!stderrTruncated) {
|
|
3103
|
+
entry.stderr += data.toString();
|
|
3104
|
+
if (entry.stderr.length > MAX_OUTPUT_SIZE2) {
|
|
3105
|
+
entry.stderr = entry.stderr.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MSG;
|
|
3106
|
+
stderrTruncated = true;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
});
|
|
3110
|
+
child.on("close", (code) => {
|
|
3111
|
+
entry.exitCode = code;
|
|
3112
|
+
entry.status = code === 0 ? "completed" : "error";
|
|
3113
|
+
entry.endTime = Date.now();
|
|
3114
|
+
entry.process = null;
|
|
3115
|
+
});
|
|
3116
|
+
child.on("error", (error) => {
|
|
3117
|
+
entry.stderr += `
|
|
3118
|
+
Process error: ${error.message}`;
|
|
3119
|
+
entry.status = "error";
|
|
3120
|
+
entry.endTime = Date.now();
|
|
3121
|
+
entry.process = null;
|
|
3122
|
+
});
|
|
3123
|
+
if (timeout > 0) {
|
|
3124
|
+
setTimeout(() => {
|
|
3125
|
+
if (entry.status === "running") {
|
|
3126
|
+
child.kill("SIGTERM");
|
|
3127
|
+
setTimeout(() => {
|
|
3128
|
+
if (entry.status === "running") {
|
|
3129
|
+
child.kill("SIGKILL");
|
|
3130
|
+
}
|
|
3131
|
+
}, 2e3);
|
|
3132
|
+
entry.status = "timeout";
|
|
3133
|
+
entry.stderr += `
|
|
3134
|
+
Command timed out after ${timeout} seconds`;
|
|
3135
|
+
}
|
|
3136
|
+
}, timeout * 1e3);
|
|
3137
|
+
}
|
|
3138
|
+
runningCommands.set(commandId, entry);
|
|
3139
|
+
cleanupOldCommands();
|
|
3140
|
+
return {
|
|
3141
|
+
success: true,
|
|
3142
|
+
command_id: commandId,
|
|
3143
|
+
command,
|
|
3144
|
+
message: `Command started in background. Use command_status with id "${commandId}" to check progress.`
|
|
3145
|
+
};
|
|
3146
|
+
} catch (error) {
|
|
3147
|
+
return {
|
|
3148
|
+
success: false,
|
|
3149
|
+
error: `Unexpected error: ${error.message}`
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
async function commandStatus(args) {
|
|
3154
|
+
try {
|
|
3155
|
+
const {
|
|
3156
|
+
command_id,
|
|
3157
|
+
wait_seconds = 0,
|
|
3158
|
+
output_limit = 1e4
|
|
3159
|
+
} = args;
|
|
3160
|
+
if (!command_id) {
|
|
3161
|
+
return {
|
|
3162
|
+
success: false,
|
|
3163
|
+
command_id: "",
|
|
3164
|
+
status: "error",
|
|
3165
|
+
error: "command_id is required"
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
const entry = runningCommands.get(command_id);
|
|
3169
|
+
if (!entry) {
|
|
3170
|
+
return {
|
|
3171
|
+
success: false,
|
|
3172
|
+
command_id,
|
|
3173
|
+
status: "not_found",
|
|
3174
|
+
error: `Command with id "${command_id}" not found. It may have expired or never existed.`
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
if (wait_seconds > 0 && entry.status === "running") {
|
|
3178
|
+
await new Promise((resolve) => {
|
|
3179
|
+
const checkInterval = setInterval(() => {
|
|
3180
|
+
if (entry.status !== "running") {
|
|
3181
|
+
clearInterval(checkInterval);
|
|
3182
|
+
resolve();
|
|
3183
|
+
}
|
|
3184
|
+
}, 100);
|
|
3185
|
+
setTimeout(() => {
|
|
3186
|
+
clearInterval(checkInterval);
|
|
3187
|
+
resolve();
|
|
3188
|
+
}, wait_seconds * 1e3);
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
let stdout = entry.stdout;
|
|
3192
|
+
let stderr = entry.stderr;
|
|
3193
|
+
let truncated = false;
|
|
3194
|
+
if (stdout.length > output_limit) {
|
|
3195
|
+
stdout = "..." + stdout.substring(stdout.length - output_limit);
|
|
3196
|
+
truncated = true;
|
|
3197
|
+
}
|
|
3198
|
+
if (stderr.length > output_limit) {
|
|
3199
|
+
stderr = "..." + stderr.substring(stderr.length - output_limit);
|
|
3200
|
+
truncated = true;
|
|
3201
|
+
}
|
|
3202
|
+
const duration = entry.endTime ? (entry.endTime - entry.startTime) / 1e3 : (Date.now() - entry.startTime) / 1e3;
|
|
3203
|
+
return {
|
|
3204
|
+
success: true,
|
|
3205
|
+
command_id,
|
|
3206
|
+
status: entry.status,
|
|
3207
|
+
stdout: stdout || void 0,
|
|
3208
|
+
stderr: stderr || void 0,
|
|
3209
|
+
exit_code: entry.exitCode,
|
|
3210
|
+
duration_seconds: Math.round(duration * 10) / 10,
|
|
3211
|
+
truncated
|
|
3212
|
+
};
|
|
3213
|
+
} catch (error) {
|
|
3214
|
+
return {
|
|
3215
|
+
success: false,
|
|
3216
|
+
command_id: args.command_id || "",
|
|
3217
|
+
status: "error",
|
|
3218
|
+
error: `Unexpected error: ${error.message}`
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
async function sendCommandInput(args) {
|
|
3223
|
+
try {
|
|
3224
|
+
const { command_id, input } = args;
|
|
3225
|
+
if (!command_id || input === void 0) {
|
|
3226
|
+
return {
|
|
3227
|
+
success: false,
|
|
3228
|
+
error: "command_id and input are required"
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
const entry = runningCommands.get(command_id);
|
|
3232
|
+
if (!entry) {
|
|
3233
|
+
return {
|
|
3234
|
+
success: false,
|
|
3235
|
+
error: `Command with id "${command_id}" not found`
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
if (entry.status !== "running" || !entry.process) {
|
|
3239
|
+
return {
|
|
3240
|
+
success: false,
|
|
3241
|
+
error: `Command is not running (status: ${entry.status})`
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
entry.process.stdin?.write(input);
|
|
3245
|
+
return {
|
|
3246
|
+
success: true,
|
|
3247
|
+
message: `Sent ${input.length} characters to command ${command_id}`
|
|
3248
|
+
};
|
|
3249
|
+
} catch (error) {
|
|
3250
|
+
return {
|
|
3251
|
+
success: false,
|
|
3252
|
+
error: `Failed to send input: ${error.message}`
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
async function killCommand(args) {
|
|
3257
|
+
try {
|
|
3258
|
+
const { command_id } = args;
|
|
3259
|
+
const entry = runningCommands.get(command_id);
|
|
3260
|
+
if (!entry) {
|
|
3261
|
+
return {
|
|
3262
|
+
success: false,
|
|
3263
|
+
error: `Command with id "${command_id}" not found`
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
if (entry.status !== "running" || !entry.process) {
|
|
3267
|
+
return {
|
|
3268
|
+
success: false,
|
|
3269
|
+
error: `Command is not running (status: ${entry.status})`
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
entry.process.kill("SIGTERM");
|
|
3273
|
+
entry.status = "killed";
|
|
3274
|
+
entry.endTime = Date.now();
|
|
3275
|
+
return {
|
|
3276
|
+
success: true,
|
|
3277
|
+
message: `Command ${command_id} killed`
|
|
3278
|
+
};
|
|
3279
|
+
} catch (error) {
|
|
3280
|
+
return {
|
|
3281
|
+
success: false,
|
|
3282
|
+
error: `Failed to kill command: ${error.message}`
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
// src/app/agent/tools/natives/task_boundary.ts
|
|
3288
|
+
import path9 from "path";
|
|
3289
|
+
import { promises as fs7 } from "fs";
|
|
3290
|
+
import os4 from "os";
|
|
3291
|
+
var currentTask = null;
|
|
3292
|
+
var artifactsDir = null;
|
|
3293
|
+
async function getArtifactsDir() {
|
|
3294
|
+
if (artifactsDir) return artifactsDir;
|
|
3295
|
+
const homeDir = os4.homedir();
|
|
3296
|
+
const baseDir = path9.join(homeDir, ".bluma", "artifacts");
|
|
3297
|
+
const sessionId2 = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
3298
|
+
artifactsDir = path9.join(baseDir, sessionId2);
|
|
3299
|
+
await fs7.mkdir(artifactsDir, { recursive: true });
|
|
3300
|
+
return artifactsDir;
|
|
3301
|
+
}
|
|
3302
|
+
async function updateTaskFile(task) {
|
|
3303
|
+
const dir = await getArtifactsDir();
|
|
3304
|
+
const taskFile = path9.join(dir, "task.md");
|
|
3305
|
+
const content = `# ${task.taskName}
|
|
3306
|
+
|
|
3307
|
+
**Mode:** ${task.mode}
|
|
3308
|
+
**Status:** ${task.status}
|
|
3309
|
+
**Started:** ${new Date(task.startTime).toISOString()}
|
|
3310
|
+
**Updated:** ${new Date(task.updateTime).toISOString()}
|
|
3311
|
+
**Steps:** ${task.stepCount}
|
|
3312
|
+
|
|
3313
|
+
## Summary
|
|
3314
|
+
${task.summary}
|
|
3315
|
+
`;
|
|
3316
|
+
await fs7.writeFile(taskFile, content, "utf-8");
|
|
3317
|
+
}
|
|
3318
|
+
async function taskBoundary(args) {
|
|
3319
|
+
try {
|
|
3320
|
+
const { task_name, mode, task_status, task_summary } = args;
|
|
3321
|
+
if (!task_name || typeof task_name !== "string") {
|
|
3322
|
+
return {
|
|
3323
|
+
success: false,
|
|
3324
|
+
task_name: "",
|
|
3325
|
+
mode: "EXECUTION",
|
|
3326
|
+
status: "",
|
|
3327
|
+
message: "task_name is required"
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
if (!["PLANNING", "EXECUTION", "VERIFICATION"].includes(mode)) {
|
|
3331
|
+
return {
|
|
3332
|
+
success: false,
|
|
3333
|
+
task_name,
|
|
3334
|
+
mode: "EXECUTION",
|
|
3335
|
+
status: "",
|
|
3336
|
+
message: `Invalid mode: ${mode}. Must be PLANNING, EXECUTION, or VERIFICATION`
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
if (!task_status || typeof task_status !== "string") {
|
|
3340
|
+
return {
|
|
3341
|
+
success: false,
|
|
3342
|
+
task_name,
|
|
3343
|
+
mode,
|
|
3344
|
+
status: "",
|
|
3345
|
+
message: "task_status is required"
|
|
3346
|
+
};
|
|
3347
|
+
}
|
|
3348
|
+
const now = Date.now();
|
|
3349
|
+
if (currentTask && currentTask.taskName === task_name) {
|
|
3350
|
+
currentTask.mode = mode;
|
|
3351
|
+
currentTask.status = task_status;
|
|
3352
|
+
currentTask.summary = task_summary || currentTask.summary;
|
|
3353
|
+
currentTask.updateTime = now;
|
|
3354
|
+
currentTask.stepCount++;
|
|
3355
|
+
} else {
|
|
3356
|
+
currentTask = {
|
|
3357
|
+
taskName: task_name,
|
|
3358
|
+
mode,
|
|
3359
|
+
status: task_status,
|
|
3360
|
+
summary: task_summary || "",
|
|
3361
|
+
startTime: now,
|
|
3362
|
+
updateTime: now,
|
|
3363
|
+
stepCount: 1
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
await updateTaskFile(currentTask);
|
|
3367
|
+
const dir = await getArtifactsDir();
|
|
3368
|
+
return {
|
|
3369
|
+
success: true,
|
|
3370
|
+
task_name: currentTask.taskName,
|
|
3371
|
+
mode: currentTask.mode,
|
|
3372
|
+
status: currentTask.status,
|
|
3373
|
+
message: `Task "${task_name}" is now in ${mode} mode. Status: ${task_status}`,
|
|
3374
|
+
artifacts_dir: dir
|
|
3375
|
+
};
|
|
3376
|
+
} catch (error) {
|
|
3377
|
+
return {
|
|
3378
|
+
success: false,
|
|
3379
|
+
task_name: args.task_name || "",
|
|
3380
|
+
mode: args.mode || "EXECUTION",
|
|
3381
|
+
status: args.task_status || "",
|
|
3382
|
+
message: `Error: ${error.message}`
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
async function createArtifact(args) {
|
|
3387
|
+
try {
|
|
3388
|
+
const { filename, content } = args;
|
|
3389
|
+
if (!filename || typeof filename !== "string") {
|
|
3390
|
+
return { success: false, error: "filename is required" };
|
|
3391
|
+
}
|
|
3392
|
+
if (content === void 0 || content === null) {
|
|
3393
|
+
return { success: false, error: "content is required" };
|
|
3394
|
+
}
|
|
3395
|
+
const dir = await getArtifactsDir();
|
|
3396
|
+
const filepath = path9.join(dir, filename);
|
|
3397
|
+
await fs7.writeFile(filepath, content, "utf-8");
|
|
3398
|
+
return {
|
|
3399
|
+
success: true,
|
|
3400
|
+
path: filepath
|
|
3401
|
+
};
|
|
3402
|
+
} catch (error) {
|
|
3403
|
+
return {
|
|
3404
|
+
success: false,
|
|
3405
|
+
error: error.message
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
async function readArtifact(args) {
|
|
3410
|
+
try {
|
|
3411
|
+
const { filename } = args;
|
|
3412
|
+
if (!filename || typeof filename !== "string") {
|
|
3413
|
+
return { success: false, error: "filename is required" };
|
|
3414
|
+
}
|
|
3415
|
+
const dir = await getArtifactsDir();
|
|
3416
|
+
const filepath = path9.join(dir, filename);
|
|
3417
|
+
const content = await fs7.readFile(filepath, "utf-8");
|
|
3418
|
+
return {
|
|
3419
|
+
success: true,
|
|
3420
|
+
content
|
|
3421
|
+
};
|
|
3422
|
+
} catch (error) {
|
|
3423
|
+
return {
|
|
3424
|
+
success: false,
|
|
3425
|
+
error: error.message
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
// src/app/agent/tools/natives/search_web.ts
|
|
3431
|
+
import https from "https";
|
|
3432
|
+
import http from "http";
|
|
3433
|
+
var DEFAULT_SOURCES = ["reddit", "github", "stackoverflow"];
|
|
3434
|
+
var MAX_RESULTS_DEFAULT = 10;
|
|
3435
|
+
var REQUEST_TIMEOUT = 1e4;
|
|
3436
|
+
function httpGet(url, customHeaders) {
|
|
3437
|
+
return new Promise((resolve, reject) => {
|
|
3438
|
+
const protocol = url.startsWith("https") ? https : http;
|
|
3439
|
+
const defaultHeaders = {
|
|
3440
|
+
"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",
|
|
3441
|
+
"Accept": "application/json, text/html, */*",
|
|
3442
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
3443
|
+
"Accept-Encoding": "identity",
|
|
3444
|
+
// Não usar gzip para simplificar
|
|
3445
|
+
"Cache-Control": "no-cache",
|
|
3446
|
+
"Connection": "keep-alive",
|
|
3447
|
+
...customHeaders
|
|
3448
|
+
};
|
|
3449
|
+
const req = protocol.get(url, {
|
|
3450
|
+
headers: defaultHeaders,
|
|
3451
|
+
timeout: REQUEST_TIMEOUT
|
|
3452
|
+
}, (res) => {
|
|
3453
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
3454
|
+
httpGet(res.headers.location, customHeaders).then(resolve).catch(reject);
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
let data = "";
|
|
3458
|
+
res.on("data", (chunk) => data += chunk);
|
|
3459
|
+
res.on("end", () => {
|
|
3460
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
3461
|
+
resolve(data);
|
|
3462
|
+
} else {
|
|
3463
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data.substring(0, 200)}`));
|
|
3464
|
+
}
|
|
3465
|
+
});
|
|
3466
|
+
});
|
|
3467
|
+
req.on("error", reject);
|
|
3468
|
+
req.on("timeout", () => {
|
|
3469
|
+
req.destroy();
|
|
3470
|
+
reject(new Error("Request timeout"));
|
|
3471
|
+
});
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3474
|
+
async function searchReddit(query, limit) {
|
|
3475
|
+
const results = [];
|
|
3476
|
+
try {
|
|
3477
|
+
const subreddits = "programming+webdev+javascript+typescript+python+node+reactjs+learnprogramming";
|
|
3478
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3479
|
+
const url = `https://www.reddit.com/r/${subreddits}/search.json?q=${encodedQuery}&sort=relevance&limit=${limit}&restrict_sr=on`;
|
|
3480
|
+
const response = await httpGet(url);
|
|
3481
|
+
const data = JSON.parse(response);
|
|
3482
|
+
if (data.data?.children) {
|
|
3483
|
+
for (const child of data.data.children.slice(0, limit)) {
|
|
3484
|
+
const post = child.data;
|
|
3485
|
+
results.push({
|
|
3486
|
+
title: post.title || "",
|
|
3487
|
+
url: `https://reddit.com${post.permalink}`,
|
|
3488
|
+
source: "reddit",
|
|
3489
|
+
snippet: post.selftext?.substring(0, 200) || `r/${post.subreddit} - ${post.score} upvotes`,
|
|
3490
|
+
score: post.score
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
} catch (error) {
|
|
3495
|
+
console.error(`[search_web] Reddit error: ${error.message}`);
|
|
3496
|
+
}
|
|
3497
|
+
return results;
|
|
3498
|
+
}
|
|
3499
|
+
async function searchGitHub(query, limit) {
|
|
3500
|
+
const results = [];
|
|
3501
|
+
try {
|
|
3502
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3503
|
+
const url = `https://api.github.com/search/issues?q=${encodedQuery}+is:issue&sort=reactions&order=desc&per_page=${limit}`;
|
|
3504
|
+
const response = await httpGet(url);
|
|
3505
|
+
const data = JSON.parse(response);
|
|
3506
|
+
if (data.items) {
|
|
3507
|
+
for (const item of data.items.slice(0, limit)) {
|
|
3508
|
+
results.push({
|
|
3509
|
+
title: item.title || "",
|
|
3510
|
+
url: item.html_url || "",
|
|
3511
|
+
source: "github",
|
|
3512
|
+
snippet: item.body?.substring(0, 200) || `${item.comments} comments`,
|
|
3513
|
+
score: item.reactions?.total_count || 0
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
} catch (error) {
|
|
3518
|
+
console.error(`[search_web] GitHub error: ${error.message}`);
|
|
3519
|
+
}
|
|
3520
|
+
return results;
|
|
3521
|
+
}
|
|
3522
|
+
async function searchStackOverflow(query, limit) {
|
|
3523
|
+
const results = [];
|
|
3524
|
+
try {
|
|
3525
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3526
|
+
const url = `https://api.stackexchange.com/2.3/search?order=desc&sort=relevance&intitle=${encodedQuery}&site=stackoverflow&pagesize=${limit}`;
|
|
3527
|
+
const response = await httpGet(url);
|
|
3528
|
+
const data = JSON.parse(response);
|
|
3529
|
+
if (data.items) {
|
|
3530
|
+
for (const item of data.items.slice(0, limit)) {
|
|
3531
|
+
results.push({
|
|
3532
|
+
title: item.title || "",
|
|
3533
|
+
url: item.link || "",
|
|
3534
|
+
source: "stackoverflow",
|
|
3535
|
+
snippet: `${item.score} votes, ${item.answer_count} answers${item.is_answered ? " \u2713" : ""}`,
|
|
3536
|
+
score: item.score
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
} catch (error) {
|
|
3541
|
+
console.error(`[search_web] StackOverflow error: ${error.message}`);
|
|
3542
|
+
}
|
|
3543
|
+
return results;
|
|
3544
|
+
}
|
|
3545
|
+
function getXSearchUrl(query) {
|
|
3546
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3547
|
+
return {
|
|
3548
|
+
title: `Search X for: "${query}"`,
|
|
3549
|
+
url: `https://twitter.com/search?q=${encodedQuery}&src=typed_query&f=live`,
|
|
3550
|
+
source: "x",
|
|
3551
|
+
snippet: "X/Twitter requires authentication. Click the link to search manually."
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
async function searchWeb(args) {
|
|
3555
|
+
try {
|
|
3556
|
+
const {
|
|
3557
|
+
query,
|
|
3558
|
+
sources = DEFAULT_SOURCES,
|
|
3559
|
+
max_results = MAX_RESULTS_DEFAULT
|
|
3560
|
+
} = args;
|
|
3561
|
+
if (!query || typeof query !== "string") {
|
|
3562
|
+
return {
|
|
3563
|
+
success: false,
|
|
3564
|
+
query: query || "",
|
|
3565
|
+
results: [],
|
|
3566
|
+
sources_searched: [],
|
|
3567
|
+
total_results: 0,
|
|
3568
|
+
error: "query is required and must be a string"
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
const allResults = [];
|
|
3572
|
+
const sourcesSearched = [];
|
|
3573
|
+
const resultsPerSource = Math.ceil(max_results / sources.length);
|
|
3574
|
+
const searches = [];
|
|
3575
|
+
for (const source of sources) {
|
|
3576
|
+
sourcesSearched.push(source);
|
|
3577
|
+
switch (source) {
|
|
3578
|
+
case "reddit":
|
|
3579
|
+
searches.push(searchReddit(query, resultsPerSource));
|
|
3580
|
+
break;
|
|
3581
|
+
case "github":
|
|
3582
|
+
searches.push(searchGitHub(query, resultsPerSource));
|
|
3583
|
+
break;
|
|
3584
|
+
case "stackoverflow":
|
|
3585
|
+
searches.push(searchStackOverflow(query, resultsPerSource));
|
|
3586
|
+
break;
|
|
3587
|
+
case "x":
|
|
3588
|
+
allResults.push(getXSearchUrl(query));
|
|
3589
|
+
break;
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
const searchResults = await Promise.all(searches);
|
|
3593
|
+
for (const results of searchResults) {
|
|
3594
|
+
allResults.push(...results);
|
|
3595
|
+
}
|
|
3596
|
+
allResults.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
3597
|
+
const limitedResults = allResults.slice(0, max_results);
|
|
3598
|
+
return {
|
|
3599
|
+
success: true,
|
|
3600
|
+
query,
|
|
3601
|
+
results: limitedResults,
|
|
3602
|
+
sources_searched: sourcesSearched,
|
|
3603
|
+
total_results: limitedResults.length,
|
|
3604
|
+
note: sources.includes("x") ? "X/Twitter requires manual search due to API restrictions" : void 0
|
|
3605
|
+
};
|
|
3606
|
+
} catch (error) {
|
|
3607
|
+
return {
|
|
3608
|
+
success: false,
|
|
3609
|
+
query: args.query || "",
|
|
3610
|
+
results: [],
|
|
3611
|
+
sources_searched: [],
|
|
3612
|
+
total_results: 0,
|
|
3613
|
+
error: `Unexpected error: ${error.message}`
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
// src/app/agent/tool_invoker.ts
|
|
3619
|
+
var ToolInvoker = class {
|
|
3620
|
+
// Mapa privado para associar nomes de ferramentas às suas funções de implementação.
|
|
3621
|
+
toolImplementations;
|
|
3622
|
+
// Propriedade privada para armazenar as definições de ferramentas carregadas do JSON.
|
|
3623
|
+
toolDefinitions = [];
|
|
3624
|
+
constructor() {
|
|
3625
|
+
this.toolImplementations = /* @__PURE__ */ new Map();
|
|
3626
|
+
this.registerTools();
|
|
3627
|
+
}
|
|
3628
|
+
/**
|
|
3629
|
+
* Carrega as definições de ferramentas do arquivo de configuração `native_tools.json`.
|
|
3630
|
+
* Este método é assíncrono e deve ser chamado após a criação da instância.
|
|
3631
|
+
*/
|
|
3632
|
+
async initialize() {
|
|
3633
|
+
try {
|
|
3634
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
3635
|
+
const __dirname = path10.dirname(__filename);
|
|
3636
|
+
const configPath = path10.resolve(__dirname, "config", "native_tools.json");
|
|
3637
|
+
const fileContent = await fs8.readFile(configPath, "utf-8");
|
|
3638
|
+
const config2 = JSON.parse(fileContent);
|
|
3639
|
+
this.toolDefinitions = config2.nativeTools;
|
|
3640
|
+
} catch (error) {
|
|
3641
|
+
console.error("[ToolInvoker] Erro cr\xEDtico ao carregar 'native_tools.json'. As ferramentas nativas n\xE3o estar\xE3o dispon\xEDveis.", error);
|
|
3642
|
+
this.toolDefinitions = [];
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
/**
|
|
3646
|
+
* Registra as implementações de todas as ferramentas nativas.
|
|
3647
|
+
* Este método mapeia o nome da ferramenta (string) para a função TypeScript que a executa.
|
|
3648
|
+
*/
|
|
3649
|
+
registerTools() {
|
|
3650
|
+
this.toolImplementations.set("shell_command", shellCommand);
|
|
3651
|
+
this.toolImplementations.set("edit_tool", editTool);
|
|
3652
|
+
this.toolImplementations.set("ls_tool", ls);
|
|
3653
|
+
this.toolImplementations.set("count_file_lines", countLines);
|
|
3654
|
+
this.toolImplementations.set("read_file_lines", readLines);
|
|
3655
|
+
this.toolImplementations.set("find_by_name", findByName);
|
|
3656
|
+
this.toolImplementations.set("grep_search", grepSearch);
|
|
3657
|
+
this.toolImplementations.set("view_file_outline", viewFileOutline);
|
|
3658
|
+
this.toolImplementations.set("run_command_async", runCommandAsync);
|
|
3659
|
+
this.toolImplementations.set("command_status", commandStatus);
|
|
3660
|
+
this.toolImplementations.set("send_command_input", sendCommandInput);
|
|
3661
|
+
this.toolImplementations.set("kill_command", killCommand);
|
|
3662
|
+
this.toolImplementations.set("message_notify_user", messageNotifyuser);
|
|
3663
|
+
this.toolImplementations.set("todo", todo);
|
|
3664
|
+
this.toolImplementations.set("task_boundary", taskBoundary);
|
|
3665
|
+
this.toolImplementations.set("create_artifact", createArtifact);
|
|
3666
|
+
this.toolImplementations.set("read_artifact", readArtifact);
|
|
3667
|
+
this.toolImplementations.set("search_web", searchWeb);
|
|
3668
|
+
this.toolImplementations.set("agent_end_turn", async () => ({ success: true, message: "Task ended by agent." }));
|
|
3669
|
+
}
|
|
3670
|
+
/**
|
|
3671
|
+
* Retorna a lista de definições de todas as ferramentas nativas carregadas.
|
|
3672
|
+
* O MCPClient usará esta função para obter a lista de ferramentas locais.
|
|
3673
|
+
*/
|
|
3674
|
+
getToolDefinitions() {
|
|
3675
|
+
return this.toolDefinitions;
|
|
3676
|
+
}
|
|
3677
|
+
/**
|
|
3678
|
+
* Invoca uma ferramenta nativa pelo nome com os argumentos fornecidos.
|
|
3679
|
+
* @param toolName O nome da ferramenta a ser invocada.
|
|
3680
|
+
* @param args Os argumentos para a ferramenta, geralmente um objeto.
|
|
3681
|
+
* @returns O resultado da execução da ferramenta.
|
|
3682
|
+
*/
|
|
3683
|
+
async invoke(toolName, args) {
|
|
3684
|
+
const implementation = this.toolImplementations.get(toolName);
|
|
3685
|
+
if (!implementation) {
|
|
3686
|
+
return { error: `Error: Native tool "${toolName}" not found.` };
|
|
3687
|
+
}
|
|
3688
|
+
try {
|
|
3689
|
+
return await implementation(args);
|
|
3690
|
+
} catch (error) {
|
|
3691
|
+
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred.";
|
|
3692
|
+
return { error: `Error executing tool "${toolName}": ${errorMessage}` };
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
|
|
3697
|
+
// src/app/agent/tools/mcp/mcp_client.ts
|
|
3698
|
+
import { promises as fs9 } from "fs";
|
|
3699
|
+
import path11 from "path";
|
|
3700
|
+
import os5 from "os";
|
|
3701
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3702
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3703
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3704
|
+
var MCPClient = class {
|
|
3705
|
+
sessions = /* @__PURE__ */ new Map();
|
|
3706
|
+
toolToServerMap = /* @__PURE__ */ new Map();
|
|
3707
|
+
globalToolsForLlm = [];
|
|
3708
|
+
nativeToolInvoker;
|
|
3709
|
+
eventBus;
|
|
3710
|
+
// <<< ADICIONA A PROPRIEDADE
|
|
3711
|
+
constructor(nativeToolInvoker, eventBus2) {
|
|
3712
|
+
this.nativeToolInvoker = nativeToolInvoker;
|
|
3713
|
+
this.eventBus = eventBus2;
|
|
3714
|
+
}
|
|
3715
|
+
// ... (método initialize inalterado) ...
|
|
3716
|
+
async initialize() {
|
|
3717
|
+
const nativeTools = this.nativeToolInvoker.getToolDefinitions();
|
|
3718
|
+
this.globalToolsForLlm.push(...nativeTools);
|
|
3719
|
+
for (const tool of nativeTools) {
|
|
3720
|
+
const toolName = tool.function.name;
|
|
3721
|
+
this.toolToServerMap.set(toolName, {
|
|
3722
|
+
server: "native",
|
|
3723
|
+
originalName: toolName
|
|
3724
|
+
});
|
|
3725
|
+
}
|
|
3726
|
+
const __filename = fileURLToPath2(import.meta.url);
|
|
3727
|
+
const __dirname = path11.dirname(__filename);
|
|
3728
|
+
const defaultConfigPath = path11.resolve(__dirname, "config", "bluma-mcp.json");
|
|
3729
|
+
const userConfigPath = path11.join(os5.homedir(), ".bluma-cli", "bluma-mcp.json");
|
|
3730
|
+
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
3731
|
+
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
3732
|
+
const mergedConfig = {
|
|
3733
|
+
mcpServers: {
|
|
3734
|
+
...defaultConfig.mcpServers || {},
|
|
3735
|
+
...userConfig.mcpServers || {}
|
|
3736
|
+
}
|
|
3737
|
+
};
|
|
3738
|
+
if (Object.keys(mergedConfig.mcpServers).length === 0) {
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
const serverEntries = Object.entries(mergedConfig.mcpServers);
|
|
3742
|
+
for (const [serverName, serverConf] of serverEntries) {
|
|
3743
|
+
try {
|
|
3744
|
+
this.eventBus.emit("backend_message", {
|
|
3745
|
+
type: "connection_status",
|
|
3746
|
+
message: `${serverName} server is being connected...`
|
|
3747
|
+
});
|
|
3748
|
+
if (serverConf.type === "stdio") {
|
|
3749
|
+
await this.connectToStdioServer(serverName, serverConf);
|
|
3750
|
+
} else if (serverConf.type === "sse") {
|
|
3751
|
+
console.warn(`[MCPClient] Conex\xE3o com servidores SSE (como '${serverName}') ainda n\xE3o implementada.`);
|
|
3752
|
+
}
|
|
3753
|
+
} catch (error) {
|
|
3754
|
+
this.eventBus.emit("backend_message", {
|
|
3755
|
+
type: "error",
|
|
3756
|
+
message: `Failed to connect to server '${serverName}'.`
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
async loadMcpConfig(configPath, configType) {
|
|
3762
|
+
try {
|
|
3763
|
+
const fileContent = await fs9.readFile(configPath, "utf-8");
|
|
3764
|
+
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
2021
3765
|
return JSON.parse(processedContent);
|
|
2022
3766
|
} catch (error) {
|
|
2023
3767
|
if (error.code === "ENOENT") {
|
|
@@ -2035,7 +3779,7 @@ var MCPClient = class {
|
|
|
2035
3779
|
async connectToStdioServer(serverName, config2) {
|
|
2036
3780
|
let commandToExecute = config2.command;
|
|
2037
3781
|
let argsToExecute = config2.args || [];
|
|
2038
|
-
const isWindows =
|
|
3782
|
+
const isWindows = os5.platform() === "win32";
|
|
2039
3783
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
2040
3784
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
2041
3785
|
commandToExecute = argsToExecute[1];
|
|
@@ -2151,12 +3895,12 @@ var AdvancedFeedbackSystem = class {
|
|
|
2151
3895
|
};
|
|
2152
3896
|
|
|
2153
3897
|
// src/app/agent/bluma/core/bluma.ts
|
|
2154
|
-
import
|
|
3898
|
+
import path14 from "path";
|
|
2155
3899
|
|
|
2156
|
-
// src/app/agent/
|
|
2157
|
-
import
|
|
2158
|
-
import
|
|
2159
|
-
import { promises as
|
|
3900
|
+
// src/app/agent/session_manager/session_manager.ts
|
|
3901
|
+
import path12 from "path";
|
|
3902
|
+
import os6 from "os";
|
|
3903
|
+
import { promises as fs10 } from "fs";
|
|
2160
3904
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
2161
3905
|
async function withFileLock(file, fn) {
|
|
2162
3906
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -2174,13 +3918,13 @@ async function withFileLock(file, fn) {
|
|
|
2174
3918
|
function expandHome(p) {
|
|
2175
3919
|
if (!p) return p;
|
|
2176
3920
|
if (p.startsWith("~")) {
|
|
2177
|
-
return
|
|
3921
|
+
return path12.join(os6.homedir(), p.slice(1));
|
|
2178
3922
|
}
|
|
2179
3923
|
return p;
|
|
2180
3924
|
}
|
|
2181
3925
|
function getPreferredAppDir() {
|
|
2182
|
-
const fixed =
|
|
2183
|
-
return
|
|
3926
|
+
const fixed = path12.join(os6.homedir(), ".bluma-cli");
|
|
3927
|
+
return path12.resolve(expandHome(fixed));
|
|
2184
3928
|
}
|
|
2185
3929
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
2186
3930
|
let attempt = 0;
|
|
@@ -2188,7 +3932,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
2188
3932
|
const isWin = process.platform === "win32";
|
|
2189
3933
|
while (attempt <= maxRetries) {
|
|
2190
3934
|
try {
|
|
2191
|
-
await
|
|
3935
|
+
await fs10.rename(src, dest);
|
|
2192
3936
|
return;
|
|
2193
3937
|
} catch (e) {
|
|
2194
3938
|
lastErr = e;
|
|
@@ -2201,9 +3945,9 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
2201
3945
|
}
|
|
2202
3946
|
}
|
|
2203
3947
|
try {
|
|
2204
|
-
const data = await
|
|
2205
|
-
await
|
|
2206
|
-
await
|
|
3948
|
+
const data = await fs10.readFile(src);
|
|
3949
|
+
await fs10.writeFile(dest, data);
|
|
3950
|
+
await fs10.unlink(src).catch(() => {
|
|
2207
3951
|
});
|
|
2208
3952
|
return;
|
|
2209
3953
|
} catch (fallbackErr) {
|
|
@@ -2212,16 +3956,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
2212
3956
|
}
|
|
2213
3957
|
async function ensureSessionDir() {
|
|
2214
3958
|
const appDir = getPreferredAppDir();
|
|
2215
|
-
const sessionDir =
|
|
2216
|
-
await
|
|
3959
|
+
const sessionDir = path12.join(appDir, "sessions");
|
|
3960
|
+
await fs10.mkdir(sessionDir, { recursive: true });
|
|
2217
3961
|
return sessionDir;
|
|
2218
3962
|
}
|
|
2219
3963
|
async function loadOrcreateSession(sessionId2) {
|
|
2220
3964
|
const sessionDir = await ensureSessionDir();
|
|
2221
|
-
const sessionFile =
|
|
3965
|
+
const sessionFile = path12.join(sessionDir, `${sessionId2}.json`);
|
|
2222
3966
|
try {
|
|
2223
|
-
await
|
|
2224
|
-
const fileContent = await
|
|
3967
|
+
await fs10.access(sessionFile);
|
|
3968
|
+
const fileContent = await fs10.readFile(sessionFile, "utf-8");
|
|
2225
3969
|
const sessionData = JSON.parse(fileContent);
|
|
2226
3970
|
return [sessionFile, [], []];
|
|
2227
3971
|
} catch (error) {
|
|
@@ -2230,7 +3974,7 @@ async function loadOrcreateSession(sessionId2) {
|
|
|
2230
3974
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2231
3975
|
conversation_history: []
|
|
2232
3976
|
};
|
|
2233
|
-
await
|
|
3977
|
+
await fs10.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
2234
3978
|
return [sessionFile, [], []];
|
|
2235
3979
|
}
|
|
2236
3980
|
}
|
|
@@ -2238,12 +3982,12 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2238
3982
|
await withFileLock(sessionFile, async () => {
|
|
2239
3983
|
let sessionData;
|
|
2240
3984
|
try {
|
|
2241
|
-
const dir =
|
|
2242
|
-
await
|
|
3985
|
+
const dir = path12.dirname(sessionFile);
|
|
3986
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
2243
3987
|
} catch {
|
|
2244
3988
|
}
|
|
2245
3989
|
try {
|
|
2246
|
-
const fileContent = await
|
|
3990
|
+
const fileContent = await fs10.readFile(sessionFile, "utf-8");
|
|
2247
3991
|
sessionData = JSON.parse(fileContent);
|
|
2248
3992
|
} catch (error) {
|
|
2249
3993
|
const code = error && error.code;
|
|
@@ -2254,14 +3998,14 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2254
3998
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
2255
3999
|
}
|
|
2256
4000
|
}
|
|
2257
|
-
const sessionId2 =
|
|
4001
|
+
const sessionId2 = path12.basename(sessionFile, ".json");
|
|
2258
4002
|
sessionData = {
|
|
2259
4003
|
session_id: sessionId2,
|
|
2260
4004
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2261
4005
|
conversation_history: []
|
|
2262
4006
|
};
|
|
2263
4007
|
try {
|
|
2264
|
-
await
|
|
4008
|
+
await fs10.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
2265
4009
|
} catch {
|
|
2266
4010
|
}
|
|
2267
4011
|
}
|
|
@@ -2269,7 +4013,7 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2269
4013
|
sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
2270
4014
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
2271
4015
|
try {
|
|
2272
|
-
await
|
|
4016
|
+
await fs10.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
2273
4017
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
2274
4018
|
} catch (writeError) {
|
|
2275
4019
|
if (writeError instanceof Error) {
|
|
@@ -2278,7 +4022,7 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2278
4022
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
2279
4023
|
}
|
|
2280
4024
|
try {
|
|
2281
|
-
await
|
|
4025
|
+
await fs10.unlink(tempSessionFile);
|
|
2282
4026
|
} catch {
|
|
2283
4027
|
}
|
|
2284
4028
|
}
|
|
@@ -2286,181 +4030,329 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
2286
4030
|
}
|
|
2287
4031
|
|
|
2288
4032
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
2289
|
-
import
|
|
2290
|
-
import
|
|
2291
|
-
import
|
|
4033
|
+
import os7 from "os";
|
|
4034
|
+
import fs11 from "fs";
|
|
4035
|
+
import path13 from "path";
|
|
4036
|
+
import { execSync } from "child_process";
|
|
4037
|
+
function getNodeVersion() {
|
|
4038
|
+
try {
|
|
4039
|
+
return process.version;
|
|
4040
|
+
} catch {
|
|
4041
|
+
return "unknown";
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
function getNpmVersion() {
|
|
4045
|
+
try {
|
|
4046
|
+
return execSync("npm --version", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
4047
|
+
} catch {
|
|
4048
|
+
return "unknown";
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
function getGitBranch(dir) {
|
|
4052
|
+
try {
|
|
4053
|
+
return execSync("git rev-parse --abbrev-ref HEAD", {
|
|
4054
|
+
cwd: dir,
|
|
4055
|
+
encoding: "utf-8",
|
|
4056
|
+
timeout: 5e3
|
|
4057
|
+
}).trim();
|
|
4058
|
+
} catch {
|
|
4059
|
+
return "N/A";
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
function getPackageManager(dir) {
|
|
4063
|
+
try {
|
|
4064
|
+
if (fs11.existsSync(path13.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
4065
|
+
if (fs11.existsSync(path13.join(dir, "yarn.lock"))) return "yarn";
|
|
4066
|
+
if (fs11.existsSync(path13.join(dir, "bun.lockb"))) return "bun";
|
|
4067
|
+
if (fs11.existsSync(path13.join(dir, "package-lock.json"))) return "npm";
|
|
4068
|
+
return "unknown";
|
|
4069
|
+
} catch {
|
|
4070
|
+
return "unknown";
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
function getProjectType(dir) {
|
|
4074
|
+
try {
|
|
4075
|
+
const files = fs11.readdirSync(dir);
|
|
4076
|
+
if (files.includes("package.json")) {
|
|
4077
|
+
const pkg = JSON.parse(fs11.readFileSync(path13.join(dir, "package.json"), "utf-8"));
|
|
4078
|
+
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
4079
|
+
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
4080
|
+
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
4081
|
+
if (pkg.dependencies?.vue || pkg.devDependencies?.vue) return "Vue";
|
|
4082
|
+
if (pkg.dependencies?.angular || pkg.devDependencies?.angular) return "Angular";
|
|
4083
|
+
return "Node.js";
|
|
4084
|
+
}
|
|
4085
|
+
if (files.includes("requirements.txt") || files.includes("pyproject.toml")) return "Python";
|
|
4086
|
+
if (files.includes("Cargo.toml")) return "Rust";
|
|
4087
|
+
if (files.includes("go.mod")) return "Go";
|
|
4088
|
+
if (files.includes("pom.xml") || files.includes("build.gradle")) return "Java";
|
|
4089
|
+
return "unknown";
|
|
4090
|
+
} catch {
|
|
4091
|
+
return "unknown";
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
function getTestFramework(dir) {
|
|
4095
|
+
try {
|
|
4096
|
+
const pkgPath = path13.join(dir, "package.json");
|
|
4097
|
+
if (fs11.existsSync(pkgPath)) {
|
|
4098
|
+
const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
|
|
4099
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4100
|
+
if (deps.jest) return "jest";
|
|
4101
|
+
if (deps.vitest) return "vitest";
|
|
4102
|
+
if (deps.mocha) return "mocha";
|
|
4103
|
+
if (deps.ava) return "ava";
|
|
4104
|
+
if (deps["@playwright/test"]) return "playwright";
|
|
4105
|
+
if (deps.cypress) return "cypress";
|
|
4106
|
+
}
|
|
4107
|
+
if (fs11.existsSync(path13.join(dir, "pytest.ini")) || fs11.existsSync(path13.join(dir, "conftest.py"))) return "pytest";
|
|
4108
|
+
return "unknown";
|
|
4109
|
+
} catch {
|
|
4110
|
+
return "unknown";
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
function getTestCommand(dir) {
|
|
4114
|
+
try {
|
|
4115
|
+
const pkgPath = path13.join(dir, "package.json");
|
|
4116
|
+
if (fs11.existsSync(pkgPath)) {
|
|
4117
|
+
const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
|
|
4118
|
+
if (pkg.scripts?.test) return `npm test`;
|
|
4119
|
+
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
4120
|
+
}
|
|
4121
|
+
const pm = getPackageManager(dir);
|
|
4122
|
+
const tf = getTestFramework(dir);
|
|
4123
|
+
if (tf === "pytest") return "pytest";
|
|
4124
|
+
if (tf === "jest") return `${pm} test`;
|
|
4125
|
+
if (tf === "vitest") return `${pm} run test`;
|
|
4126
|
+
return "npm test";
|
|
4127
|
+
} catch {
|
|
4128
|
+
return "npm test";
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
2292
4131
|
var SYSTEM_PROMPT = `
|
|
2293
4132
|
<identity>
|
|
2294
|
-
You are BluMa, an autonomous coding agent by NomadEngenuity.
|
|
2295
|
-
|
|
2296
|
-
-
|
|
2297
|
-
|
|
4133
|
+
You are BluMa, an autonomous coding agent developed by NomadEngenuity.
|
|
4134
|
+
BluMa \u2014 Base Language Unit \xB7 Model Agent
|
|
4135
|
+
A CLI-based model agent responsible for language-level code generation, refactoring and semantic transformations in the Factor AI stack.
|
|
4136
|
+
You are a **senior peer engineer** working alongside {username} - technical, proactive, and collaborative.
|
|
4137
|
+
You know this machine better than anyone. You understand the project structure, dependencies, and conventions.
|
|
4138
|
+
You are not just an assistant - you are a **teammate** who takes ownership of tasks.
|
|
4139
|
+
|
|
4140
|
+
Key traits:
|
|
4141
|
+
- Think like a senior developer who has been on this project for years
|
|
4142
|
+
- Suggest improvements proactively, don't just follow orders blindly
|
|
4143
|
+
- Spot bugs and issues before they become problems
|
|
4144
|
+
- Write tests as naturally as you write code
|
|
4145
|
+
- Always communicate your reasoning and progress
|
|
2298
4146
|
</identity>
|
|
2299
4147
|
|
|
2300
4148
|
---
|
|
2301
4149
|
|
|
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>
|
|
4150
|
+
<environment>
|
|
4151
|
+
## Machine Context (Your Deep Knowledge)
|
|
4152
|
+
|
|
4153
|
+
**System:**
|
|
4154
|
+
- OS: {os_type} ({os_version})
|
|
4155
|
+
- Architecture: {architecture}
|
|
4156
|
+
- Shell: {shell_type}
|
|
4157
|
+
- Node.js: {node_version}
|
|
4158
|
+
- NPM: {npm_version}
|
|
4159
|
+
- User: {username}
|
|
4160
|
+
|
|
4161
|
+
**Project:**
|
|
4162
|
+
- Working Directory: {workdir}
|
|
4163
|
+
- Project Type: {project_type}
|
|
4164
|
+
- Package Manager: {package_manager}
|
|
4165
|
+
- Test Framework: {test_framework}
|
|
4166
|
+
- Test Command: {test_command}
|
|
4167
|
+
|
|
4168
|
+
**Git:**
|
|
4169
|
+
- Is Git Repo: {is_git_repo}
|
|
4170
|
+
- Current Branch: {git_branch}
|
|
4171
|
+
|
|
4172
|
+
**Session:**
|
|
4173
|
+
- Date: {current_date}
|
|
4174
|
+
- Timezone: {timezone}
|
|
4175
|
+
|
|
4176
|
+
You MUST adapt all commands to this environment. Use the correct package manager, test framework, and conventions.
|
|
4177
|
+
</environment>
|
|
4178
|
+
|
|
4179
|
+
---
|
|
4180
|
+
|
|
4181
|
+
<workflow>
|
|
4182
|
+
## How You Work
|
|
2349
4183
|
|
|
2350
|
-
|
|
4184
|
+
### For Multi-Step Tasks:
|
|
4185
|
+
1. **Acknowledge** - Confirm the request immediately via message_notify_user
|
|
4186
|
+
2. **Plan** - Use the TODO tool to create a structured plan
|
|
4187
|
+
3. **Execute** - Implement with clear narration of each step
|
|
4188
|
+
4. **Test** - Run tests after any code changes (MANDATORY for production code)
|
|
4189
|
+
5. **Verify** - Check build, lint, and git status
|
|
4190
|
+
6. **Summarize** - Report results and any follow-up actions
|
|
2351
4191
|
|
|
4192
|
+
### For Simple Tasks:
|
|
4193
|
+
- Quick acknowledgment + immediate action
|
|
4194
|
+
- No TODO needed for single operations
|
|
2352
4195
|
|
|
2353
|
-
|
|
4196
|
+
### Testing Philosophy (CRITICAL):
|
|
4197
|
+
You are a **teammate who writes tests**, not an assistant who skips them.
|
|
2354
4198
|
|
|
2355
|
-
|
|
4199
|
+
**ALWAYS run tests when:**
|
|
4200
|
+
- You modify any function or module
|
|
4201
|
+
- You add new functionality
|
|
4202
|
+
- You fix a bug (write a regression test first)
|
|
4203
|
+
- The user asks for a feature
|
|
2356
4204
|
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
4205
|
+
**Test workflow:**
|
|
4206
|
+
1. Before editing: Run existing tests to ensure baseline passes
|
|
4207
|
+
2. After editing: Run tests to verify nothing broke
|
|
4208
|
+
3. For new features: Write tests alongside implementation
|
|
4209
|
+
4. For bugs: Write failing test \u2192 fix \u2192 verify test passes
|
|
2361
4210
|
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
4211
|
+
**Commands you should know:**
|
|
4212
|
+
- Run tests: {test_command}
|
|
4213
|
+
- Run single test: Check project conventions
|
|
4214
|
+
- Watch mode: Usually \`npm run test:watch\` or \`npm test -- --watch\`
|
|
4215
|
+
</workflow>
|
|
2365
4216
|
|
|
2366
|
-
|
|
2367
|
-
Never address the user using the raw {username} handle.
|
|
4217
|
+
---
|
|
2368
4218
|
|
|
2369
|
-
|
|
2370
|
-
|
|
4219
|
+
<tool_rules>
|
|
4220
|
+
## Tool Calling Best Practices
|
|
2371
4221
|
|
|
2372
|
-
|
|
2373
|
-
-
|
|
2374
|
-
-
|
|
2375
|
-
-
|
|
4222
|
+
### File Operations:
|
|
4223
|
+
- **ALWAYS read a file before editing** - Use read_file_lines or ls_tool first
|
|
4224
|
+
- **Use absolute paths** when possible to avoid ambiguity
|
|
4225
|
+
- **For edit_tool**: Provide exact content with correct whitespace (read first!)
|
|
4226
|
+
- **Check file exists** before attempting edits
|
|
2376
4227
|
|
|
2377
|
-
|
|
4228
|
+
### Shell Commands:
|
|
4229
|
+
- **NEVER use sudo** - Commands requiring sudo will be blocked
|
|
4230
|
+
- **Long commands**: Set appropriate timeout (default 300s)
|
|
4231
|
+
- **Interactive commands** (vim, less, ssh) are blocked - use alternatives
|
|
4232
|
+
- **Large outputs** will be truncated at 100KB
|
|
2378
4233
|
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
4234
|
+
### Safe Auto-Approved Tools (no confirmation needed):
|
|
4235
|
+
- message_notify_user
|
|
4236
|
+
- ls_tool
|
|
4237
|
+
- read_file_lines
|
|
4238
|
+
- count_file_lines
|
|
4239
|
+
- todo
|
|
4240
|
+
- agent_end_turn
|
|
2382
4241
|
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
4242
|
+
### Require Confirmation:
|
|
4243
|
+
- shell_command (except safe commands)
|
|
4244
|
+
- edit_tool (always shows diff preview)
|
|
4245
|
+
</tool_rules>
|
|
2386
4246
|
|
|
2387
|
-
|
|
2388
|
-
Jhon Doe: Redis or Postgres for sessions?
|
|
2389
|
-
Teammate: Jhon Doe, Redis. Low latency, ephemeral data.
|
|
4247
|
+
---
|
|
2390
4248
|
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
Teammate: Rebase your feature on top of dev. Resolve conflicts and force push.
|
|
4249
|
+
<communication_style>
|
|
4250
|
+
## How You Communicate
|
|
2394
4251
|
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
4252
|
+
Convert {username} to a natural name:
|
|
4253
|
+
- Replace "-" "_" "." with spaces
|
|
4254
|
+
- Capitalize each word
|
|
4255
|
+
- Remove trailing numbers
|
|
2398
4256
|
|
|
2399
|
-
|
|
4257
|
+
Examples:
|
|
4258
|
+
- alex-fonseca \u2192 "Alex Fonseca"
|
|
4259
|
+
- joao_pereira_22 \u2192 "Joao Pereira"
|
|
2400
4260
|
|
|
4261
|
+
**Style:**
|
|
4262
|
+
- Direct and technical, like Slack/Discord
|
|
4263
|
+
- Get straight to the point
|
|
4264
|
+
- Narrate your reasoning
|
|
4265
|
+
- Celebrate wins, acknowledge mistakes
|
|
2401
4266
|
|
|
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>
|
|
4267
|
+
**Message via tool only:**
|
|
4268
|
+
- Use message_notify_user for ALL communication
|
|
4269
|
+
- Never assume the user sees internal state
|
|
4270
|
+
- Report progress proactively
|
|
2414
4271
|
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
4272
|
+
**Example flow:**
|
|
4273
|
+
\`\`\`
|
|
4274
|
+
message_notify_user("Alex, understood. Adding the auth middleware. Let me check the current implementation first.")
|
|
4275
|
+
[uses read_file_lines]
|
|
4276
|
+
message_notify_user("Found the router at /src/routes/index.ts. I'll add the middleware before the protected routes.")
|
|
4277
|
+
[uses edit_tool]
|
|
4278
|
+
message_notify_user("Done. Running tests to verify...")
|
|
4279
|
+
[uses shell_command with {test_command}]
|
|
4280
|
+
message_notify_user("All 47 tests pass. The middleware is working correctly.")
|
|
4281
|
+
\`\`\`
|
|
4282
|
+
</communication_style>
|
|
2421
4283
|
|
|
2422
|
-
|
|
2423
|
-
- Run tests if applicable
|
|
2424
|
-
- Verify build passes
|
|
2425
|
-
- Check git status if in repo
|
|
2426
|
-
</technical_standards>
|
|
4284
|
+
---
|
|
2427
4285
|
|
|
2428
4286
|
<git_guidelines>
|
|
4287
|
+
## Git Best Practices
|
|
4288
|
+
|
|
2429
4289
|
When in a git repository:
|
|
2430
|
-
- Review changes
|
|
2431
|
-
- Commit
|
|
2432
|
-
- NEVER push unless explicitly asked
|
|
2433
|
-
- NEVER use destructive commands
|
|
4290
|
+
- **Review changes**: \`git diff HEAD\` before any commit
|
|
4291
|
+
- **Commit format**: Conventional Commits (feat:, fix:, chore:, docs:, test:, refactor:)
|
|
4292
|
+
- **NEVER push** unless explicitly asked
|
|
4293
|
+
- **NEVER use destructive commands**: reset --hard, rebase -i, force push
|
|
4294
|
+
|
|
4295
|
+
Good commit messages:
|
|
4296
|
+
- feat: add user authentication middleware
|
|
4297
|
+
- fix: resolve race condition in payment processing
|
|
4298
|
+
- test: add unit tests for auth module
|
|
4299
|
+
- chore: update dependencies
|
|
2434
4300
|
</git_guidelines>
|
|
2435
4301
|
|
|
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}
|
|
4302
|
+
---
|
|
2446
4303
|
|
|
2447
|
-
|
|
2448
|
-
|
|
4304
|
+
<code_quality>
|
|
4305
|
+
## Your Code Standards
|
|
4306
|
+
|
|
4307
|
+
**Every piece of code you write should be:**
|
|
4308
|
+
- Production-ready and properly typed
|
|
4309
|
+
- Self-documenting with clear names
|
|
4310
|
+
- Properly error-handled
|
|
4311
|
+
- Tested (unit tests for logic, integration for APIs)
|
|
4312
|
+
|
|
4313
|
+
**Before finishing ANY task:**
|
|
4314
|
+
1. Run tests: {test_command}
|
|
4315
|
+
2. Check for lint errors if linter exists
|
|
4316
|
+
3. Verify build passes if applicable
|
|
4317
|
+
4. Review git status if in repo
|
|
4318
|
+
|
|
4319
|
+
**Code review mindset:**
|
|
4320
|
+
- Would this pass code review from a senior dev?
|
|
4321
|
+
- Are there edge cases I haven't considered?
|
|
4322
|
+
- Is the error handling robust?
|
|
4323
|
+
- Are there security implications?
|
|
4324
|
+
</code_quality>
|
|
2449
4325
|
|
|
4326
|
+
---
|
|
4327
|
+
|
|
4328
|
+
<personality>
|
|
2450
4329
|
You are BluMa. Autonomous, precise, collaborative.
|
|
4330
|
+
You don't just execute - you think, suggest, and improve.
|
|
4331
|
+
You test your code because you care about quality.
|
|
4332
|
+
You communicate because you respect your teammate.
|
|
4333
|
+
|
|
2451
4334
|
Let's build something great, {username}.
|
|
4335
|
+
</personality>
|
|
2452
4336
|
`;
|
|
2453
4337
|
function getUnifiedSystemPrompt() {
|
|
4338
|
+
const cwd = process.cwd();
|
|
2454
4339
|
const env = {
|
|
2455
|
-
os_type:
|
|
2456
|
-
os_version:
|
|
2457
|
-
architecture:
|
|
2458
|
-
workdir:
|
|
4340
|
+
os_type: os7.type(),
|
|
4341
|
+
os_version: os7.release(),
|
|
4342
|
+
architecture: os7.arch(),
|
|
4343
|
+
workdir: cwd,
|
|
2459
4344
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
2460
|
-
username:
|
|
4345
|
+
username: os7.userInfo().username,
|
|
2461
4346
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
2462
4347
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
2463
|
-
is_git_repo: isGitRepo(
|
|
4348
|
+
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
4349
|
+
node_version: getNodeVersion(),
|
|
4350
|
+
npm_version: getNpmVersion(),
|
|
4351
|
+
git_branch: getGitBranch(cwd),
|
|
4352
|
+
package_manager: getPackageManager(cwd),
|
|
4353
|
+
project_type: getProjectType(cwd),
|
|
4354
|
+
test_framework: getTestFramework(cwd),
|
|
4355
|
+
test_command: getTestCommand(cwd)
|
|
2464
4356
|
};
|
|
2465
4357
|
return Object.entries(env).reduce(
|
|
2466
4358
|
(prompt, [key, value]) => prompt.replaceAll(`{${key}}`, value),
|
|
@@ -2469,8 +4361,8 @@ function getUnifiedSystemPrompt() {
|
|
|
2469
4361
|
}
|
|
2470
4362
|
function isGitRepo(dir) {
|
|
2471
4363
|
try {
|
|
2472
|
-
const gitPath =
|
|
2473
|
-
return
|
|
4364
|
+
const gitPath = path13.join(dir, ".git");
|
|
4365
|
+
return fs11.existsSync(gitPath) && fs11.lstatSync(gitPath).isDirectory();
|
|
2474
4366
|
} catch {
|
|
2475
4367
|
return false;
|
|
2476
4368
|
}
|
|
@@ -2712,7 +4604,7 @@ var BluMaAgent = class {
|
|
|
2712
4604
|
|
|
2713
4605
|
${editData.error.display}`;
|
|
2714
4606
|
}
|
|
2715
|
-
const filename =
|
|
4607
|
+
const filename = path14.basename(toolArgs.file_path);
|
|
2716
4608
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
2717
4609
|
} catch (e) {
|
|
2718
4610
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -2740,6 +4632,13 @@ ${editData.error.display}`;
|
|
|
2740
4632
|
}
|
|
2741
4633
|
let message = response.choices[0].message;
|
|
2742
4634
|
message = ToolCallNormalizer.normalizeAssistantMessage(message);
|
|
4635
|
+
if (message.reasoning_content || message.reasoning) {
|
|
4636
|
+
const reasoningText = message.reasoning_content || message.reasoning;
|
|
4637
|
+
this.eventBus.emit("backend_message", {
|
|
4638
|
+
type: "reasoning",
|
|
4639
|
+
content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
|
|
4640
|
+
});
|
|
4641
|
+
}
|
|
2743
4642
|
this.history.push(message);
|
|
2744
4643
|
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
2745
4644
|
const validToolCalls = message.tool_calls.filter(
|
|
@@ -2756,7 +4655,6 @@ ${editData.error.display}`;
|
|
|
2756
4655
|
const autoApprovedTools = [
|
|
2757
4656
|
"agent_end_turn",
|
|
2758
4657
|
"message_notify_user",
|
|
2759
|
-
"reasoning_nootebook",
|
|
2760
4658
|
"ls_tool",
|
|
2761
4659
|
"count_file_lines",
|
|
2762
4660
|
"read_file_lines",
|
|
@@ -2830,7 +4728,7 @@ function getSubAgentByCommand(cmd) {
|
|
|
2830
4728
|
}
|
|
2831
4729
|
|
|
2832
4730
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
2833
|
-
import
|
|
4731
|
+
import os8 from "os";
|
|
2834
4732
|
var SYSTEM_PROMPT2 = `
|
|
2835
4733
|
|
|
2836
4734
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -2993,12 +4891,12 @@ Rule Summary:
|
|
|
2993
4891
|
function getInitPrompt() {
|
|
2994
4892
|
const now = /* @__PURE__ */ new Date();
|
|
2995
4893
|
const collectedData = {
|
|
2996
|
-
os_type:
|
|
2997
|
-
os_version:
|
|
2998
|
-
architecture:
|
|
4894
|
+
os_type: os8.type(),
|
|
4895
|
+
os_version: os8.release(),
|
|
4896
|
+
architecture: os8.arch(),
|
|
2999
4897
|
workdir: process.cwd(),
|
|
3000
4898
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
3001
|
-
username:
|
|
4899
|
+
username: os8.userInfo().username || "Unknown",
|
|
3002
4900
|
current_date: now.toISOString().split("T")[0],
|
|
3003
4901
|
// Formato YYYY-MM-DD
|
|
3004
4902
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -3251,7 +5149,7 @@ var SubAgentsBluMa = class {
|
|
|
3251
5149
|
};
|
|
3252
5150
|
|
|
3253
5151
|
// src/app/agent/agent.ts
|
|
3254
|
-
var globalEnvPath =
|
|
5152
|
+
var globalEnvPath = path15.join(os9.homedir(), ".bluma-cli", ".env");
|
|
3255
5153
|
dotenv.config({ path: globalEnvPath });
|
|
3256
5154
|
var Agent = class {
|
|
3257
5155
|
sessionId;
|
|
@@ -3386,10 +5284,10 @@ var Agent = class {
|
|
|
3386
5284
|
};
|
|
3387
5285
|
|
|
3388
5286
|
// src/app/ui/WorkingTimer.tsx
|
|
3389
|
-
import { useState as useState4, useEffect as useEffect4 } from "react";
|
|
5287
|
+
import { useState as useState4, useEffect as useEffect4, memo as memo5 } from "react";
|
|
3390
5288
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
3391
5289
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3392
|
-
var
|
|
5290
|
+
var WorkingTimerComponent = ({ taskName, taskStatus }) => {
|
|
3393
5291
|
const [seconds, setSeconds] = useState4(0);
|
|
3394
5292
|
const [frame, setFrame] = useState4(0);
|
|
3395
5293
|
useEffect4(() => {
|
|
@@ -3400,210 +5298,309 @@ var WorkingTimer = () => {
|
|
|
3400
5298
|
}, []);
|
|
3401
5299
|
useEffect4(() => {
|
|
3402
5300
|
const animator = setInterval(() => {
|
|
3403
|
-
setFrame((prev) => (prev + 1) %
|
|
3404
|
-
},
|
|
5301
|
+
setFrame((prev) => (prev + 1) % 4);
|
|
5302
|
+
}, 150);
|
|
3405
5303
|
return () => clearInterval(animator);
|
|
3406
5304
|
}, []);
|
|
3407
|
-
const spinners = ["
|
|
5305
|
+
const spinners = ["|", "/", "-", "\\"];
|
|
3408
5306
|
const spinner = spinners[frame];
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
"
|
|
3416
|
-
|
|
5307
|
+
const formatTime = (s) => {
|
|
5308
|
+
if (s < 60) return `${s}s`;
|
|
5309
|
+
return `${Math.floor(s / 60)}m${s % 60}s`;
|
|
5310
|
+
};
|
|
5311
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, children: [
|
|
5312
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
5313
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: spinner }),
|
|
5314
|
+
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
5315
|
+
" ",
|
|
5316
|
+
taskStatus || "thinking"
|
|
5317
|
+
] }),
|
|
5318
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "gray", children: [
|
|
5319
|
+
" ",
|
|
5320
|
+
formatTime(seconds)
|
|
5321
|
+
] })
|
|
5322
|
+
] }),
|
|
5323
|
+
taskName && /* @__PURE__ */ jsx7(Box7, { paddingLeft: 2, children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
5324
|
+
"^ ",
|
|
5325
|
+
taskName
|
|
5326
|
+
] }) })
|
|
3417
5327
|
] });
|
|
3418
5328
|
};
|
|
5329
|
+
var WorkingTimer = memo5(WorkingTimerComponent);
|
|
3419
5330
|
|
|
3420
5331
|
// src/app/ui/components/ToolCallDisplay.tsx
|
|
3421
|
-
import { memo as
|
|
5332
|
+
import { memo as memo6 } from "react";
|
|
3422
5333
|
import { Box as Box9 } from "ink";
|
|
3423
5334
|
|
|
3424
5335
|
// src/app/ui/components/toolCallRenderers.tsx
|
|
3425
5336
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
3426
5337
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3427
|
-
var
|
|
5338
|
+
var parseArgs = (args) => {
|
|
3428
5339
|
if (typeof args === "string") {
|
|
3429
5340
|
try {
|
|
3430
|
-
return JSON.
|
|
3431
|
-
} catch
|
|
3432
|
-
return
|
|
5341
|
+
return JSON.parse(args);
|
|
5342
|
+
} catch {
|
|
5343
|
+
return {};
|
|
3433
5344
|
}
|
|
3434
5345
|
}
|
|
3435
|
-
return
|
|
5346
|
+
return args || {};
|
|
5347
|
+
};
|
|
5348
|
+
var truncate = (str, max) => {
|
|
5349
|
+
if (str.length <= max) return str;
|
|
5350
|
+
return str.slice(0, max - 3) + "...";
|
|
5351
|
+
};
|
|
5352
|
+
var getBasename = (filepath) => {
|
|
5353
|
+
const parts = filepath.split("/");
|
|
5354
|
+
return parts[parts.length - 1] || filepath;
|
|
3436
5355
|
};
|
|
3437
5356
|
var renderShellCommand2 = ({ args }) => {
|
|
3438
|
-
const
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
5357
|
+
const parsed = parseArgs(args);
|
|
5358
|
+
const command = parsed.command || "[no command]";
|
|
5359
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5360
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "$" }),
|
|
5361
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
|
|
5362
|
+
" ",
|
|
5363
|
+
truncate(command, 70)
|
|
5364
|
+
] })
|
|
3445
5365
|
] });
|
|
3446
5366
|
};
|
|
3447
5367
|
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 }) })
|
|
5368
|
+
const parsed = parseArgs(args);
|
|
5369
|
+
const path17 = parsed.directory_path || ".";
|
|
5370
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5371
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "ls" }),
|
|
5372
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5373
|
+
" ",
|
|
5374
|
+
path17
|
|
5375
|
+
] })
|
|
3461
5376
|
] });
|
|
3462
5377
|
};
|
|
3463
5378
|
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 }) })
|
|
5379
|
+
const parsed = parseArgs(args);
|
|
5380
|
+
const filepath = parsed.filepath || "[no file]";
|
|
5381
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5382
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "wc -l" }),
|
|
5383
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5384
|
+
" ",
|
|
5385
|
+
getBasename(filepath)
|
|
5386
|
+
] })
|
|
3477
5387
|
] });
|
|
3478
5388
|
};
|
|
3479
5389
|
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" })
|
|
5390
|
+
const parsed = parseArgs(args);
|
|
5391
|
+
const filepath = parsed.filepath || "[no file]";
|
|
5392
|
+
const start = parsed.start_line || 1;
|
|
5393
|
+
const end = parsed.end_line || start;
|
|
5394
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5395
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "cat" }),
|
|
5396
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5397
|
+
" ",
|
|
5398
|
+
getBasename(filepath)
|
|
3495
5399
|
] }),
|
|
3496
|
-
/* @__PURE__ */ jsxs8(
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
" to ",
|
|
3502
|
-
endLine
|
|
3503
|
-
] }) })
|
|
5400
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
|
|
5401
|
+
" :",
|
|
5402
|
+
start,
|
|
5403
|
+
"-",
|
|
5404
|
+
end
|
|
3504
5405
|
] })
|
|
3505
5406
|
] });
|
|
3506
5407
|
};
|
|
3507
5408
|
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
|
-
}
|
|
5409
|
+
const parsed = parseArgs(args);
|
|
5410
|
+
const thought = parsed.thought || parsed.content?.thought || "[thinking...]";
|
|
5411
|
+
const truncated = truncate(thought, 100);
|
|
5412
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
5413
|
+
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "thinking" }) }),
|
|
5414
|
+
/* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: truncated }) })
|
|
5415
|
+
] });
|
|
3525
5416
|
};
|
|
3526
5417
|
var renderEditToolCall = ({ args, preview }) => {
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
} catch (e) {
|
|
3532
|
-
filepath = "Error parsing arguments";
|
|
3533
|
-
}
|
|
5418
|
+
const parsed = parseArgs(args);
|
|
5419
|
+
const filepath = parsed.file_path || "[no file]";
|
|
5420
|
+
const oldStr = parsed.old_string || "";
|
|
5421
|
+
const newStr = parsed.new_string || "";
|
|
3534
5422
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
3535
5423
|
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3536
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
3537
|
-
/* @__PURE__ */
|
|
3538
|
-
|
|
5424
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "edit" }),
|
|
5425
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5426
|
+
" ",
|
|
5427
|
+
getBasename(filepath)
|
|
5428
|
+
] })
|
|
3539
5429
|
] }),
|
|
3540
|
-
preview
|
|
5430
|
+
preview ? /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: 8 }) }) : /* @__PURE__ */ jsxs8(Box8, { paddingLeft: 2, flexDirection: "column", children: [
|
|
5431
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "red", dimColor: true, children: [
|
|
5432
|
+
"- ",
|
|
5433
|
+
truncate(oldStr, 50)
|
|
5434
|
+
] }),
|
|
5435
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "green", bold: true, children: [
|
|
5436
|
+
"+ ",
|
|
5437
|
+
truncate(newStr, 50)
|
|
5438
|
+
] })
|
|
5439
|
+
] })
|
|
3541
5440
|
] });
|
|
3542
5441
|
};
|
|
3543
5442
|
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
|
-
] }) })
|
|
5443
|
+
const parsed = parseArgs(args);
|
|
5444
|
+
const tasks = parsed.tasks || [];
|
|
5445
|
+
const completed = tasks.filter((t) => t.isComplete === true).length;
|
|
5446
|
+
const pending = tasks.length - completed;
|
|
5447
|
+
const progress = tasks.length > 0 ? Math.round(completed / tasks.length * 100) : 0;
|
|
5448
|
+
const barWidth = 10;
|
|
5449
|
+
const filled = Math.round(progress / 100 * barWidth);
|
|
5450
|
+
const bar = "=".repeat(filled) + " ".repeat(barWidth - filled);
|
|
5451
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
5452
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
5453
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "todo" }),
|
|
5454
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5455
|
+
" [",
|
|
5456
|
+
bar,
|
|
5457
|
+
"] ",
|
|
5458
|
+
progress,
|
|
5459
|
+
"%"
|
|
3590
5460
|
] })
|
|
3591
|
-
] })
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
5461
|
+
] }),
|
|
5462
|
+
tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingLeft: 2, children: [
|
|
5463
|
+
tasks.slice(0, 15).map((task, i) => /* @__PURE__ */ jsxs8(
|
|
5464
|
+
Text8,
|
|
5465
|
+
{
|
|
5466
|
+
color: task.isComplete === true ? "gray" : "white",
|
|
5467
|
+
dimColor: task.isComplete === true,
|
|
5468
|
+
strikethrough: task.isComplete === true,
|
|
5469
|
+
children: [
|
|
5470
|
+
task.isComplete === true ? "[x]" : "[ ]",
|
|
5471
|
+
" ",
|
|
5472
|
+
task.description
|
|
5473
|
+
]
|
|
5474
|
+
},
|
|
5475
|
+
i
|
|
5476
|
+
)),
|
|
5477
|
+
tasks.length > 15 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
5478
|
+
"... +",
|
|
5479
|
+
tasks.length - 15,
|
|
5480
|
+
" more tasks"
|
|
5481
|
+
] })
|
|
5482
|
+
] })
|
|
5483
|
+
] });
|
|
5484
|
+
};
|
|
5485
|
+
var renderFindByName = ({ args }) => {
|
|
5486
|
+
const parsed = parseArgs(args);
|
|
5487
|
+
const pattern = parsed.pattern || "*";
|
|
5488
|
+
const dir = parsed.directory || ".";
|
|
5489
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5490
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "find" }),
|
|
5491
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
|
|
5492
|
+
' "',
|
|
5493
|
+
pattern,
|
|
5494
|
+
'"'
|
|
5495
|
+
] }),
|
|
5496
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5497
|
+
" in ",
|
|
5498
|
+
dir
|
|
5499
|
+
] })
|
|
5500
|
+
] });
|
|
5501
|
+
};
|
|
5502
|
+
var renderGrepSearch = ({ args }) => {
|
|
5503
|
+
const parsed = parseArgs(args);
|
|
5504
|
+
const query = parsed.query || "";
|
|
5505
|
+
const path17 = parsed.path || ".";
|
|
5506
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5507
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "grep" }),
|
|
5508
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
|
|
5509
|
+
' "',
|
|
5510
|
+
truncate(query, 30),
|
|
5511
|
+
'"'
|
|
5512
|
+
] }),
|
|
5513
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5514
|
+
" ",
|
|
5515
|
+
path17
|
|
5516
|
+
] })
|
|
5517
|
+
] });
|
|
5518
|
+
};
|
|
5519
|
+
var renderViewFileOutline = ({ args }) => {
|
|
5520
|
+
const parsed = parseArgs(args);
|
|
5521
|
+
const filepath = parsed.file_path || "[no file]";
|
|
5522
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5523
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "outline" }),
|
|
5524
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5525
|
+
" ",
|
|
5526
|
+
getBasename(filepath)
|
|
5527
|
+
] })
|
|
5528
|
+
] });
|
|
5529
|
+
};
|
|
5530
|
+
var renderRunCommandAsync = ({ args }) => {
|
|
5531
|
+
const parsed = parseArgs(args);
|
|
5532
|
+
const command = parsed.command || "[no command]";
|
|
5533
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5534
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "async" }),
|
|
5535
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: " $" }),
|
|
5536
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5537
|
+
" ",
|
|
5538
|
+
truncate(command, 50)
|
|
5539
|
+
] })
|
|
5540
|
+
] });
|
|
5541
|
+
};
|
|
5542
|
+
var renderCommandStatus = ({ args }) => {
|
|
5543
|
+
const parsed = parseArgs(args);
|
|
5544
|
+
const id = parsed.command_id || "[no id]";
|
|
5545
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5546
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "status" }),
|
|
5547
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5548
|
+
" #",
|
|
5549
|
+
id
|
|
5550
|
+
] })
|
|
5551
|
+
] });
|
|
3595
5552
|
};
|
|
3596
|
-
var
|
|
3597
|
-
const
|
|
5553
|
+
var renderTaskBoundary = ({ args }) => {
|
|
5554
|
+
const parsed = parseArgs(args);
|
|
5555
|
+
const name = parsed.task_name || "[no name]";
|
|
5556
|
+
const mode = parsed.mode || "EXECUTION";
|
|
5557
|
+
const status = parsed.task_status || "";
|
|
5558
|
+
const modeColors = {
|
|
5559
|
+
PLANNING: "blue",
|
|
5560
|
+
EXECUTION: "green",
|
|
5561
|
+
VERIFICATION: "yellow"
|
|
5562
|
+
};
|
|
5563
|
+
const modeSymbols = {
|
|
5564
|
+
PLANNING: "P",
|
|
5565
|
+
EXECUTION: "E",
|
|
5566
|
+
VERIFICATION: "V"
|
|
5567
|
+
};
|
|
3598
5568
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
3599
5569
|
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3600
|
-
/* @__PURE__ */
|
|
3601
|
-
|
|
5570
|
+
/* @__PURE__ */ jsxs8(Text8, { color: modeColors[mode] || "gray", bold: true, children: [
|
|
5571
|
+
"[",
|
|
5572
|
+
modeSymbols[mode] || "?",
|
|
5573
|
+
"]"
|
|
5574
|
+
] }),
|
|
5575
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
3602
5576
|
" ",
|
|
3603
|
-
|
|
5577
|
+
name
|
|
3604
5578
|
] })
|
|
3605
5579
|
] }),
|
|
3606
|
-
|
|
5580
|
+
status && /* @__PURE__ */ jsx8(Box8, { paddingLeft: 4, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: truncate(status, 60) }) })
|
|
5581
|
+
] });
|
|
5582
|
+
};
|
|
5583
|
+
var renderSearchWeb = ({ args }) => {
|
|
5584
|
+
const parsed = parseArgs(args);
|
|
5585
|
+
const query = parsed.query || "[no query]";
|
|
5586
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5587
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "search" }),
|
|
5588
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
|
|
5589
|
+
' "',
|
|
5590
|
+
truncate(query, 40),
|
|
5591
|
+
'"'
|
|
5592
|
+
] })
|
|
5593
|
+
] });
|
|
5594
|
+
};
|
|
5595
|
+
var renderGeneric2 = ({ toolName, args }) => {
|
|
5596
|
+
const parsed = parseArgs(args);
|
|
5597
|
+
const keys = Object.keys(parsed).slice(0, 2);
|
|
5598
|
+
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5599
|
+
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: toolName }),
|
|
5600
|
+
keys.length > 0 && /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5601
|
+
" ",
|
|
5602
|
+
keys.map((k) => `${k}:${truncate(String(parsed[k]), 20)}`).join(" ")
|
|
5603
|
+
] })
|
|
3607
5604
|
] });
|
|
3608
5605
|
};
|
|
3609
5606
|
var ToolRenderDisplay = {
|
|
@@ -3613,7 +5610,14 @@ var ToolRenderDisplay = {
|
|
|
3613
5610
|
count_file_lines: renderCountFilesLines,
|
|
3614
5611
|
read_file_lines: renderReadFileLines2,
|
|
3615
5612
|
edit_tool: renderEditToolCall,
|
|
3616
|
-
todo: renderTodoTool2
|
|
5613
|
+
todo: renderTodoTool2,
|
|
5614
|
+
find_by_name: renderFindByName,
|
|
5615
|
+
grep_search: renderGrepSearch,
|
|
5616
|
+
view_file_outline: renderViewFileOutline,
|
|
5617
|
+
run_command_async: renderRunCommandAsync,
|
|
5618
|
+
command_status: renderCommandStatus,
|
|
5619
|
+
task_boundary: renderTaskBoundary,
|
|
5620
|
+
search_web: renderSearchWeb
|
|
3617
5621
|
};
|
|
3618
5622
|
|
|
3619
5623
|
// src/app/ui/components/ToolCallDisplay.tsx
|
|
@@ -3622,194 +5626,303 @@ var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
|
|
|
3622
5626
|
if (toolName.includes("message_notify_user") || toolName.includes("agent_end_turn")) {
|
|
3623
5627
|
return null;
|
|
3624
5628
|
}
|
|
3625
|
-
const Renderer = ToolRenderDisplay[toolName] ||
|
|
5629
|
+
const Renderer = ToolRenderDisplay[toolName] || ((props2) => renderGeneric2({ ...props2, toolName }));
|
|
3626
5630
|
return /* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Renderer, { toolName, args, preview }) });
|
|
3627
5631
|
};
|
|
3628
|
-
var ToolCallDisplay =
|
|
5632
|
+
var ToolCallDisplay = memo6(ToolCallDisplayComponent);
|
|
3629
5633
|
|
|
3630
5634
|
// src/app/ui/components/ToolResultDisplay.tsx
|
|
3631
|
-
import { memo as
|
|
3632
|
-
import { Box as Box11 } from "ink";
|
|
5635
|
+
import { memo as memo8 } from "react";
|
|
5636
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
3633
5637
|
|
|
3634
5638
|
// src/app/ui/components/MarkdownRenderer.tsx
|
|
5639
|
+
import { memo as memo7 } from "react";
|
|
3635
5640
|
import { Box as Box10, Text as Text9 } from "ink";
|
|
3636
5641
|
import { marked } from "marked";
|
|
3637
5642
|
import { highlight } from "cli-highlight";
|
|
3638
|
-
import
|
|
3639
|
-
|
|
5643
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
5644
|
+
var COLORS = {
|
|
5645
|
+
heading: "cyan",
|
|
5646
|
+
code: "gray",
|
|
5647
|
+
link: "blue",
|
|
5648
|
+
quote: "gray",
|
|
5649
|
+
listBullet: "gray"
|
|
5650
|
+
};
|
|
3640
5651
|
function renderTokens(tokens) {
|
|
3641
|
-
const
|
|
3642
|
-
for (
|
|
5652
|
+
const elements = [];
|
|
5653
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
5654
|
+
const token = tokens[i];
|
|
5655
|
+
const key = `token-${i}`;
|
|
3643
5656
|
switch (token.type) {
|
|
3644
5657
|
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)
|
|
5658
|
+
const heading = token;
|
|
5659
|
+
const prefix = "#".repeat(heading.depth);
|
|
5660
|
+
elements.push(
|
|
5661
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: COLORS.heading, bold: true, children: [
|
|
5662
|
+
prefix,
|
|
5663
|
+
" ",
|
|
5664
|
+
heading.text
|
|
5665
|
+
] }) }, key)
|
|
3662
5666
|
);
|
|
3663
5667
|
break;
|
|
3664
5668
|
}
|
|
3665
5669
|
case "paragraph": {
|
|
3666
|
-
const
|
|
3667
|
-
|
|
3668
|
-
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1,
|
|
5670
|
+
const para = token;
|
|
5671
|
+
elements.push(
|
|
5672
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: renderInline(para.text) }, key)
|
|
3669
5673
|
);
|
|
3670
5674
|
break;
|
|
3671
5675
|
}
|
|
3672
5676
|
case "list": {
|
|
3673
|
-
const
|
|
3674
|
-
|
|
3675
|
-
const
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
5677
|
+
const list = token;
|
|
5678
|
+
list.items.forEach((item, idx) => {
|
|
5679
|
+
const bullet = list.ordered ? `${idx + 1}.` : "\u2022";
|
|
5680
|
+
const text = item.tokens?.filter((t) => t.type === "text").map((t) => t.text).join("") || item.text;
|
|
5681
|
+
elements.push(
|
|
5682
|
+
/* @__PURE__ */ jsxs9(Box10, { paddingLeft: 1, children: [
|
|
5683
|
+
/* @__PURE__ */ jsx10(Text9, { color: COLORS.listBullet, children: bullet }),
|
|
3679
5684
|
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3680
5685
|
" ",
|
|
3681
5686
|
renderInline(text)
|
|
3682
5687
|
] })
|
|
3683
|
-
] },
|
|
5688
|
+
] }, `${key}-item-${idx}`)
|
|
3684
5689
|
);
|
|
3685
5690
|
});
|
|
3686
|
-
|
|
5691
|
+
elements.push(/* @__PURE__ */ jsx10(Box10, { marginBottom: 1 }, `${key}-spacer`));
|
|
3687
5692
|
break;
|
|
3688
5693
|
}
|
|
3689
5694
|
case "code": {
|
|
3690
|
-
const
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
]
|
|
3706
|
-
},
|
|
3707
|
-
out.length
|
|
3708
|
-
)
|
|
5695
|
+
const code = token;
|
|
5696
|
+
let highlighted;
|
|
5697
|
+
try {
|
|
5698
|
+
highlighted = highlight(code.text, {
|
|
5699
|
+
language: code.lang || "text",
|
|
5700
|
+
ignoreIllegals: true
|
|
5701
|
+
});
|
|
5702
|
+
} catch {
|
|
5703
|
+
highlighted = code.text;
|
|
5704
|
+
}
|
|
5705
|
+
elements.push(
|
|
5706
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
5707
|
+
code.lang && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: code.lang }),
|
|
5708
|
+
/* @__PURE__ */ jsx10(Box10, { paddingLeft: 1, children: /* @__PURE__ */ jsx10(Text9, { children: highlighted }) })
|
|
5709
|
+
] }, key)
|
|
3709
5710
|
);
|
|
3710
5711
|
break;
|
|
3711
5712
|
}
|
|
3712
5713
|
case "blockquote": {
|
|
3713
|
-
const
|
|
3714
|
-
|
|
3715
|
-
/* @__PURE__ */ jsxs9(Box10, {
|
|
3716
|
-
/* @__PURE__ */ jsx10(Text9, { color:
|
|
3717
|
-
/* @__PURE__ */ jsx10(Text9, { dimColor: true, italic: true, children:
|
|
3718
|
-
] },
|
|
5714
|
+
const quote = token;
|
|
5715
|
+
elements.push(
|
|
5716
|
+
/* @__PURE__ */ jsxs9(Box10, { marginBottom: 1, children: [
|
|
5717
|
+
/* @__PURE__ */ jsx10(Text9, { color: COLORS.quote, children: "\u2502 " }),
|
|
5718
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, italic: true, children: quote.text })
|
|
5719
|
+
] }, key)
|
|
3719
5720
|
);
|
|
3720
5721
|
break;
|
|
3721
5722
|
}
|
|
3722
5723
|
case "table": {
|
|
3723
5724
|
const table = token;
|
|
3724
|
-
|
|
3725
|
-
(cell) =>
|
|
5725
|
+
elements.push(
|
|
5726
|
+
/* @__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
5727
|
);
|
|
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)
|
|
5728
|
+
table.rows.forEach((row, rowIdx) => {
|
|
5729
|
+
elements.push(
|
|
5730
|
+
/* @__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
5731
|
);
|
|
3734
5732
|
});
|
|
3735
|
-
|
|
5733
|
+
elements.push(/* @__PURE__ */ jsx10(Box10, { marginBottom: 1 }, `${key}-spacer`));
|
|
5734
|
+
break;
|
|
5735
|
+
}
|
|
5736
|
+
case "hr": {
|
|
5737
|
+
elements.push(
|
|
5738
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500".repeat(40) }) }, key)
|
|
5739
|
+
);
|
|
5740
|
+
break;
|
|
5741
|
+
}
|
|
5742
|
+
case "space": {
|
|
3736
5743
|
break;
|
|
3737
5744
|
}
|
|
3738
|
-
default:
|
|
3739
|
-
if (token.text) {
|
|
3740
|
-
|
|
5745
|
+
default: {
|
|
5746
|
+
if ("text" in token && typeof token.text === "string") {
|
|
5747
|
+
elements.push(/* @__PURE__ */ jsx10(Text9, { children: token.text }, key));
|
|
3741
5748
|
}
|
|
5749
|
+
}
|
|
3742
5750
|
}
|
|
3743
5751
|
}
|
|
3744
|
-
return
|
|
5752
|
+
return elements;
|
|
3745
5753
|
}
|
|
3746
5754
|
function renderInline(text) {
|
|
3747
5755
|
const parts = [];
|
|
3748
|
-
let
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
5756
|
+
let remaining = text;
|
|
5757
|
+
let partIndex = 0;
|
|
5758
|
+
const inlineRegex = /(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\))/;
|
|
5759
|
+
while (remaining.length > 0) {
|
|
5760
|
+
const match = remaining.match(inlineRegex);
|
|
5761
|
+
if (!match) {
|
|
5762
|
+
parts.push(/* @__PURE__ */ jsx10(Text9, { children: remaining }, `inline-${partIndex++}`));
|
|
3754
5763
|
break;
|
|
3755
5764
|
}
|
|
3756
|
-
const
|
|
3757
|
-
if (
|
|
3758
|
-
parts.push(
|
|
3759
|
-
|
|
3760
|
-
|
|
5765
|
+
const matchIndex = match.index ?? 0;
|
|
5766
|
+
if (matchIndex > 0) {
|
|
5767
|
+
parts.push(
|
|
5768
|
+
/* @__PURE__ */ jsx10(Text9, { children: remaining.slice(0, matchIndex) }, `inline-${partIndex++}`)
|
|
5769
|
+
);
|
|
5770
|
+
}
|
|
5771
|
+
const fullMatch = match[0];
|
|
5772
|
+
if (fullMatch.startsWith("**")) {
|
|
3761
5773
|
parts.push(
|
|
3762
|
-
/* @__PURE__ */ jsx10(Text9, { bold: true, children:
|
|
5774
|
+
/* @__PURE__ */ jsx10(Text9, { bold: true, children: match[2] }, `inline-${partIndex++}`)
|
|
3763
5775
|
);
|
|
3764
|
-
} else if (
|
|
5776
|
+
} else if (fullMatch.startsWith("*")) {
|
|
3765
5777
|
parts.push(
|
|
3766
|
-
/* @__PURE__ */ jsx10(Text9, { italic: true, children:
|
|
5778
|
+
/* @__PURE__ */ jsx10(Text9, { italic: true, children: match[3] }, `inline-${partIndex++}`)
|
|
3767
5779
|
);
|
|
3768
|
-
} else if (
|
|
5780
|
+
} else if (fullMatch.startsWith("`")) {
|
|
3769
5781
|
parts.push(
|
|
3770
|
-
/* @__PURE__ */
|
|
5782
|
+
/* @__PURE__ */ jsxs9(Text9, { color: COLORS.code, children: [
|
|
5783
|
+
"`",
|
|
5784
|
+
match[4],
|
|
5785
|
+
"`"
|
|
5786
|
+
] }, `inline-${partIndex++}`)
|
|
3771
5787
|
);
|
|
3772
|
-
} else if (
|
|
5788
|
+
} else if (fullMatch.startsWith("[")) {
|
|
3773
5789
|
parts.push(
|
|
3774
|
-
/* @__PURE__ */
|
|
3775
|
-
m[5],
|
|
3776
|
-
" (",
|
|
3777
|
-
m[6],
|
|
3778
|
-
")"
|
|
3779
|
-
] }, parts.length)
|
|
5790
|
+
/* @__PURE__ */ jsx10(Text9, { color: COLORS.link, underline: true, children: match[5] }, `inline-${partIndex++}`)
|
|
3780
5791
|
);
|
|
3781
5792
|
}
|
|
3782
|
-
|
|
5793
|
+
remaining = remaining.slice(matchIndex + fullMatch.length);
|
|
3783
5794
|
}
|
|
3784
|
-
return parts;
|
|
5795
|
+
return parts.length === 1 ? parts[0] : /* @__PURE__ */ jsx10(Fragment3, { children: parts });
|
|
3785
5796
|
}
|
|
3786
|
-
var
|
|
5797
|
+
var MarkdownRendererComponent = ({ markdown }) => {
|
|
5798
|
+
if (!markdown || markdown.trim() === "") {
|
|
5799
|
+
return null;
|
|
5800
|
+
}
|
|
3787
5801
|
const tokens = marked.lexer(markdown);
|
|
3788
5802
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: renderTokens(tokens) });
|
|
3789
5803
|
};
|
|
5804
|
+
var MarkdownRenderer = memo7(MarkdownRendererComponent);
|
|
3790
5805
|
|
|
3791
5806
|
// src/app/ui/components/ToolResultDisplay.tsx
|
|
3792
|
-
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
5807
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5808
|
+
var MAX_LINES = 8;
|
|
5809
|
+
var parseResult = (result) => {
|
|
5810
|
+
try {
|
|
5811
|
+
return JSON.parse(result);
|
|
5812
|
+
} catch {
|
|
5813
|
+
return null;
|
|
5814
|
+
}
|
|
5815
|
+
};
|
|
5816
|
+
var truncateLines = (text, max) => {
|
|
5817
|
+
const allLines = text.split("\n").filter((l) => l.trim());
|
|
5818
|
+
if (allLines.length <= max) {
|
|
5819
|
+
return { lines: allLines, truncated: 0 };
|
|
5820
|
+
}
|
|
5821
|
+
return {
|
|
5822
|
+
lines: allLines.slice(0, max),
|
|
5823
|
+
truncated: allLines.length - max
|
|
5824
|
+
};
|
|
5825
|
+
};
|
|
3793
5826
|
var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
3794
|
-
if (
|
|
5827
|
+
if (toolName.includes("agent_end_turn") || toolName.includes("task_boundary")) {
|
|
3795
5828
|
return null;
|
|
3796
5829
|
}
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
5830
|
+
const parsed = parseResult(result);
|
|
5831
|
+
if (toolName.includes("message_notify_user")) {
|
|
5832
|
+
const body = parsed?.content?.body || parsed?.body || parsed?.message;
|
|
5833
|
+
if (!body) return null;
|
|
5834
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(MarkdownRenderer, { markdown: body }) });
|
|
5835
|
+
}
|
|
5836
|
+
if (toolName.includes("read_file") && parsed?.content) {
|
|
5837
|
+
const { lines, truncated } = truncateLines(parsed.content, MAX_LINES);
|
|
5838
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5839
|
+
lines.map((line, i) => /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "gray", children: line.slice(0, 80) }, i)),
|
|
5840
|
+
truncated > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5841
|
+
"... +",
|
|
5842
|
+
truncated,
|
|
5843
|
+
" lines"
|
|
5844
|
+
] })
|
|
5845
|
+
] });
|
|
5846
|
+
}
|
|
5847
|
+
if ((toolName.includes("shell_command") || toolName.includes("run_command")) && parsed) {
|
|
5848
|
+
const output = parsed.stdout || parsed.output || "";
|
|
5849
|
+
const stderr = parsed.stderr || "";
|
|
5850
|
+
const exitCode = parsed.exit_code ?? parsed.exitCode ?? 0;
|
|
5851
|
+
if (!output && !stderr) return null;
|
|
5852
|
+
const { lines, truncated } = truncateLines(output || stderr, MAX_LINES);
|
|
5853
|
+
const isError = exitCode !== 0 || stderr;
|
|
5854
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5855
|
+
lines.map((line, i) => /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: isError ? "red" : "gray", children: line.slice(0, 80) }, i)),
|
|
5856
|
+
truncated > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5857
|
+
"... +",
|
|
5858
|
+
truncated,
|
|
5859
|
+
" lines"
|
|
5860
|
+
] })
|
|
5861
|
+
] });
|
|
5862
|
+
}
|
|
5863
|
+
if ((toolName.includes("grep") || toolName.includes("find_by_name")) && parsed) {
|
|
5864
|
+
const matches = parsed.matches || parsed.results || parsed.files || [];
|
|
5865
|
+
if (matches.length === 0) {
|
|
5866
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "no matches" }) });
|
|
3802
5867
|
}
|
|
3803
|
-
|
|
5868
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5869
|
+
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5870
|
+
matches.length,
|
|
5871
|
+
" matches"
|
|
5872
|
+
] }),
|
|
5873
|
+
matches.slice(0, 5).map((m, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "gray", children: [
|
|
5874
|
+
m.file || m.path || m.name || m,
|
|
5875
|
+
m.line ? `:${m.line}` : ""
|
|
5876
|
+
] }, i)),
|
|
5877
|
+
matches.length > 5 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5878
|
+
"... +",
|
|
5879
|
+
matches.length - 5,
|
|
5880
|
+
" more"
|
|
5881
|
+
] })
|
|
5882
|
+
] });
|
|
5883
|
+
}
|
|
5884
|
+
if (toolName.includes("ls_tool") && parsed) {
|
|
5885
|
+
const entries = parsed.entries || parsed.files || [];
|
|
5886
|
+
if (entries.length === 0) return null;
|
|
5887
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5888
|
+
entries.slice(0, 6).map((e, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: e.isDirectory ? "blue" : "gray", children: [
|
|
5889
|
+
e.isDirectory ? "d" : "f",
|
|
5890
|
+
" ",
|
|
5891
|
+
e.name || e
|
|
5892
|
+
] }, i)),
|
|
5893
|
+
entries.length > 6 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5894
|
+
"... +",
|
|
5895
|
+
entries.length - 6,
|
|
5896
|
+
" more"
|
|
5897
|
+
] })
|
|
5898
|
+
] });
|
|
5899
|
+
}
|
|
5900
|
+
if (toolName.includes("todo")) {
|
|
3804
5901
|
return null;
|
|
3805
5902
|
}
|
|
5903
|
+
if (toolName.includes("view_file_outline") && parsed) {
|
|
5904
|
+
const symbols = parsed.symbols || parsed.outline || [];
|
|
5905
|
+
if (symbols.length === 0) return null;
|
|
5906
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
|
|
5907
|
+
symbols.slice(0, 5).map((s, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5908
|
+
s.kind || "sym",
|
|
5909
|
+
" ",
|
|
5910
|
+
s.name
|
|
5911
|
+
] }, i)),
|
|
5912
|
+
symbols.length > 5 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5913
|
+
"... +",
|
|
5914
|
+
symbols.length - 5,
|
|
5915
|
+
" more"
|
|
5916
|
+
] })
|
|
5917
|
+
] });
|
|
5918
|
+
}
|
|
3806
5919
|
return null;
|
|
3807
5920
|
};
|
|
3808
|
-
var ToolResultDisplay =
|
|
5921
|
+
var ToolResultDisplay = memo8(ToolResultDisplayComponent);
|
|
3809
5922
|
|
|
3810
5923
|
// src/app/ui/components/SlashCommands.tsx
|
|
3811
5924
|
import { Box as Box12, Text as Text11 } from "ink";
|
|
3812
|
-
import { Fragment as
|
|
5925
|
+
import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3813
5926
|
var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
3814
5927
|
const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
|
|
3815
5928
|
const outBox = (children) => /* @__PURE__ */ jsx12(
|
|
@@ -3831,9 +5944,9 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3831
5944
|
if (cmd === "help") {
|
|
3832
5945
|
const cmds = getSlashCommands();
|
|
3833
5946
|
return outBox(
|
|
3834
|
-
/* @__PURE__ */
|
|
5947
|
+
/* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3835
5948
|
/* @__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__ */
|
|
5949
|
+
/* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: cmds.map((c, i) => /* @__PURE__ */ jsxs11(Box12, { children: [
|
|
3837
5950
|
/* @__PURE__ */ jsx12(Text11, { color: "cyan", children: c.name.padEnd(12) }),
|
|
3838
5951
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: c.description })
|
|
3839
5952
|
] }, i)) })
|
|
@@ -3843,7 +5956,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3843
5956
|
if (cmd === "clear") {
|
|
3844
5957
|
setHistory((prev) => prev.filter((item) => item.id === 0 || item.id === 1));
|
|
3845
5958
|
return outBox(
|
|
3846
|
-
/* @__PURE__ */
|
|
5959
|
+
/* @__PURE__ */ jsxs11(Box12, { children: [
|
|
3847
5960
|
/* @__PURE__ */ jsx12(Text11, { color: "green", children: "\u2713" }),
|
|
3848
5961
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " History cleared" })
|
|
3849
5962
|
] })
|
|
@@ -3857,7 +5970,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3857
5970
|
setHistory((prev) => prev.concat({
|
|
3858
5971
|
id: Date.now(),
|
|
3859
5972
|
component: outBox(
|
|
3860
|
-
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */
|
|
5973
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
3861
5974
|
"\u2716 Failed to execute /init: ",
|
|
3862
5975
|
e?.message || String(e)
|
|
3863
5976
|
] }) })
|
|
@@ -3878,29 +5991,29 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3878
5991
|
const colType = 10;
|
|
3879
5992
|
const colSource = 18;
|
|
3880
5993
|
return outBox(
|
|
3881
|
-
/* @__PURE__ */
|
|
3882
|
-
/* @__PURE__ */
|
|
5994
|
+
/* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
5995
|
+
/* @__PURE__ */ jsxs11(Box12, { marginBottom: 1, children: [
|
|
3883
5996
|
/* @__PURE__ */ jsx12(Text11, { bold: true, color: "magenta", children: "MCP Tools" }),
|
|
3884
5997
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " \u2022 " }),
|
|
3885
|
-
/* @__PURE__ */
|
|
5998
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
3886
5999
|
tools.length,
|
|
3887
6000
|
" total"
|
|
3888
6001
|
] }),
|
|
3889
|
-
term && /* @__PURE__ */
|
|
6002
|
+
term && /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3890
6003
|
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " \u2022 filter: " }),
|
|
3891
|
-
/* @__PURE__ */
|
|
6004
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "cyan", children: [
|
|
3892
6005
|
'"',
|
|
3893
6006
|
term,
|
|
3894
6007
|
'"'
|
|
3895
6008
|
] }),
|
|
3896
|
-
/* @__PURE__ */
|
|
6009
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
3897
6010
|
" \u2022 showing: ",
|
|
3898
6011
|
filtered.length
|
|
3899
6012
|
] })
|
|
3900
6013
|
] })
|
|
3901
6014
|
] }),
|
|
3902
|
-
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools found" }) : /* @__PURE__ */
|
|
3903
|
-
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */
|
|
6015
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools found" }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
6016
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3904
6017
|
pad("Name", colName),
|
|
3905
6018
|
" \u2502 ",
|
|
3906
6019
|
pad("Type", colType),
|
|
@@ -3912,7 +6025,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3912
6025
|
const name = t.function?.name || t.name || "tool";
|
|
3913
6026
|
const type = t.function?.name ? "fn" : t.type || "tool";
|
|
3914
6027
|
const source = t.source || t.provider || "mcp";
|
|
3915
|
-
return /* @__PURE__ */
|
|
6028
|
+
return /* @__PURE__ */ jsxs11(Text11, { color: "white", children: [
|
|
3916
6029
|
pad(name, colName),
|
|
3917
6030
|
" \u2502 ",
|
|
3918
6031
|
pad(String(type), colType),
|
|
@@ -3935,22 +6048,22 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3935
6048
|
const colType = 10;
|
|
3936
6049
|
const colSource = 18;
|
|
3937
6050
|
return outBox(
|
|
3938
|
-
/* @__PURE__ */
|
|
6051
|
+
/* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3939
6052
|
/* @__PURE__ */ jsx12(Text11, { color: "magenta", bold: true, children: "Native Tools" }),
|
|
3940
|
-
/* @__PURE__ */
|
|
6053
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3941
6054
|
"Total Native: ",
|
|
3942
6055
|
tools.length,
|
|
3943
6056
|
term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
|
|
3944
6057
|
] }),
|
|
3945
|
-
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */
|
|
3946
|
-
/* @__PURE__ */
|
|
6058
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
6059
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3947
6060
|
pad("Name", colName),
|
|
3948
6061
|
" | ",
|
|
3949
6062
|
pad("Type", colType),
|
|
3950
6063
|
" | ",
|
|
3951
6064
|
pad("Source", colSource)
|
|
3952
6065
|
] }),
|
|
3953
|
-
/* @__PURE__ */
|
|
6066
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3954
6067
|
"".padEnd(colName, "-"),
|
|
3955
6068
|
"---",
|
|
3956
6069
|
"".padEnd(colType, "-"),
|
|
@@ -3961,7 +6074,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3961
6074
|
const name = t.function?.name || t.name || "tool";
|
|
3962
6075
|
const type = t.function?.name ? "fn" : t.type || "tool";
|
|
3963
6076
|
const source = t.source || "native";
|
|
3964
|
-
return /* @__PURE__ */
|
|
6077
|
+
return /* @__PURE__ */ jsxs11(Text11, { color: "white", children: [
|
|
3965
6078
|
pad(name, colName),
|
|
3966
6079
|
" | ",
|
|
3967
6080
|
pad(String(type), colType),
|
|
@@ -3973,34 +6086,36 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3973
6086
|
] })
|
|
3974
6087
|
);
|
|
3975
6088
|
}
|
|
3976
|
-
return outBox(/* @__PURE__ */
|
|
6089
|
+
return outBox(/* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
3977
6090
|
"Command not recognized: /",
|
|
3978
6091
|
cmd
|
|
3979
6092
|
] }));
|
|
3980
6093
|
};
|
|
3981
|
-
return /* @__PURE__ */ jsx12(
|
|
6094
|
+
return /* @__PURE__ */ jsx12(Fragment4, { children: render2() });
|
|
3982
6095
|
};
|
|
3983
6096
|
var SlashCommands_default = SlashCommands;
|
|
3984
6097
|
|
|
3985
6098
|
// src/app/agent/utils/update_check.ts
|
|
3986
6099
|
import updateNotifier from "update-notifier";
|
|
3987
|
-
import { readPackageUp } from "read-package-up";
|
|
3988
6100
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3989
|
-
import
|
|
3990
|
-
import
|
|
3991
|
-
|
|
6101
|
+
import path16 from "path";
|
|
6102
|
+
import fs12 from "fs";
|
|
6103
|
+
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
6104
|
+
function findBlumaPackageJson(startDir) {
|
|
3992
6105
|
let dir = startDir;
|
|
3993
|
-
for (let i = 0; i <
|
|
3994
|
-
const candidate =
|
|
3995
|
-
if (
|
|
6106
|
+
for (let i = 0; i < 10; i++) {
|
|
6107
|
+
const candidate = path16.join(dir, "package.json");
|
|
6108
|
+
if (fs12.existsSync(candidate)) {
|
|
3996
6109
|
try {
|
|
3997
|
-
const raw =
|
|
6110
|
+
const raw = fs12.readFileSync(candidate, "utf8");
|
|
3998
6111
|
const parsed = JSON.parse(raw);
|
|
3999
|
-
if (parsed?.name && parsed?.version)
|
|
6112
|
+
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
6113
|
+
return { name: parsed.name, version: parsed.version };
|
|
6114
|
+
}
|
|
4000
6115
|
} catch {
|
|
4001
6116
|
}
|
|
4002
6117
|
}
|
|
4003
|
-
const parent =
|
|
6118
|
+
const parent = path16.dirname(dir);
|
|
4004
6119
|
if (parent === dir) break;
|
|
4005
6120
|
dir = parent;
|
|
4006
6121
|
}
|
|
@@ -4012,20 +6127,18 @@ async function checkForUpdates() {
|
|
|
4012
6127
|
return String(process.env.BLUMA_FORCE_UPDATE_MSG);
|
|
4013
6128
|
}
|
|
4014
6129
|
const binPath = process.argv?.[1];
|
|
4015
|
-
let pkg;
|
|
4016
|
-
if (binPath &&
|
|
4017
|
-
|
|
4018
|
-
if (candidatePkg?.name && candidatePkg?.version) {
|
|
4019
|
-
pkg = candidatePkg;
|
|
4020
|
-
}
|
|
6130
|
+
let pkg = null;
|
|
6131
|
+
if (binPath && fs12.existsSync(binPath)) {
|
|
6132
|
+
pkg = findBlumaPackageJson(path16.dirname(binPath));
|
|
4021
6133
|
}
|
|
4022
6134
|
if (!pkg) {
|
|
4023
6135
|
const __filename = fileURLToPath3(import.meta.url);
|
|
4024
|
-
const __dirname =
|
|
4025
|
-
|
|
4026
|
-
|
|
6136
|
+
const __dirname = path16.dirname(__filename);
|
|
6137
|
+
pkg = findBlumaPackageJson(__dirname);
|
|
6138
|
+
}
|
|
6139
|
+
if (!pkg) {
|
|
6140
|
+
return null;
|
|
4027
6141
|
}
|
|
4028
|
-
if (!pkg?.name || !pkg?.version) return null;
|
|
4029
6142
|
const isCI = Boolean(process.env.CI);
|
|
4030
6143
|
const updateCheckInterval = 0;
|
|
4031
6144
|
const notifier = updateNotifier({
|
|
@@ -4037,20 +6150,19 @@ async function checkForUpdates() {
|
|
|
4037
6150
|
const cur = notifier.update.current;
|
|
4038
6151
|
const lat = notifier.update.latest;
|
|
4039
6152
|
if (cur && lat && cur !== lat) {
|
|
4040
|
-
return `Update available for
|
|
4041
|
-
|
|
6153
|
+
return `Update available for BluMa CLI! ${cur} \u2192 ${lat}
|
|
6154
|
+
Run: npm i -g ${BLUMA_PACKAGE_NAME} to update.`;
|
|
4042
6155
|
}
|
|
4043
6156
|
}
|
|
4044
6157
|
return null;
|
|
4045
6158
|
} catch (e) {
|
|
4046
|
-
console.warn("Update check failed:", e);
|
|
4047
6159
|
return null;
|
|
4048
6160
|
}
|
|
4049
6161
|
}
|
|
4050
6162
|
|
|
4051
6163
|
// src/app/ui/components/UpdateNotice.tsx
|
|
4052
6164
|
import { Box as Box13, Text as Text12 } from "ink";
|
|
4053
|
-
import { jsx as jsx13, jsxs as
|
|
6165
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4054
6166
|
function parseUpdateMessage(msg) {
|
|
4055
6167
|
const lines = msg.split(/\r?\n/).map((l) => l.trim());
|
|
4056
6168
|
const first = lines[0] || "";
|
|
@@ -4069,7 +6181,7 @@ function parseUpdateMessage(msg) {
|
|
|
4069
6181
|
}
|
|
4070
6182
|
var UpdateNotice = ({ message }) => {
|
|
4071
6183
|
const { name, current, latest, hint } = parseUpdateMessage(message);
|
|
4072
|
-
return /* @__PURE__ */
|
|
6184
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
4073
6185
|
/* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: "Update Available" }),
|
|
4074
6186
|
name && current && latest ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: `${name}: ${current} \u2192 ${latest}` }) : /* @__PURE__ */ jsx13(Text12, { color: "gray", children: message }),
|
|
4075
6187
|
hint ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: hint }) : null
|
|
@@ -4079,54 +6191,86 @@ var UpdateNotice_default = UpdateNotice;
|
|
|
4079
6191
|
|
|
4080
6192
|
// src/app/ui/components/ErrorMessage.tsx
|
|
4081
6193
|
import { Box as Box14, Text as Text13 } from "ink";
|
|
4082
|
-
import { jsx as jsx14, jsxs as
|
|
6194
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4083
6195
|
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
|
-
);
|
|
6196
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
6197
|
+
/* @__PURE__ */ jsxs13(Box14, { children: [
|
|
6198
|
+
/* @__PURE__ */ jsx14(Text13, { color: "red", children: "\u2717" }),
|
|
6199
|
+
/* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: " Error" })
|
|
6200
|
+
] }),
|
|
6201
|
+
/* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message }) }),
|
|
6202
|
+
details && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: details }) }),
|
|
6203
|
+
hint && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
|
|
6204
|
+
"hint: ",
|
|
6205
|
+
hint
|
|
6206
|
+
] }) })
|
|
6207
|
+
] });
|
|
4104
6208
|
};
|
|
4105
6209
|
var ErrorMessage_default = ErrorMessage;
|
|
4106
6210
|
|
|
6211
|
+
// src/app/ui/components/ReasoningDisplay.tsx
|
|
6212
|
+
import { memo as memo9, useState as useState5 } from "react";
|
|
6213
|
+
import { Box as Box15, Text as Text14 } from "ink";
|
|
6214
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
6215
|
+
var ReasoningDisplayComponent = ({
|
|
6216
|
+
reasoning,
|
|
6217
|
+
collapsed = false
|
|
6218
|
+
}) => {
|
|
6219
|
+
const [isExpanded, setIsExpanded] = useState5(!collapsed);
|
|
6220
|
+
if (!reasoning || reasoning.trim() === "") {
|
|
6221
|
+
return null;
|
|
6222
|
+
}
|
|
6223
|
+
const maxLines = 5;
|
|
6224
|
+
const lines = reasoning.split("\n");
|
|
6225
|
+
const displayText = isExpanded ? reasoning : lines.slice(0, maxLines).join("\n") + (lines.length > maxLines ? "..." : "");
|
|
6226
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, marginBottom: 1, marginTop: 1, children: [
|
|
6227
|
+
/* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text14, { color: "white", bold: true, children: "Thinking" }) }),
|
|
6228
|
+
/* @__PURE__ */ jsx15(Box15, { paddingLeft: 2, flexDirection: "column", children: displayText.split("\n").map((line, i) => /* @__PURE__ */ jsx15(Text14, { color: "gray", dimColor: true, children: line }, i)) })
|
|
6229
|
+
] });
|
|
6230
|
+
};
|
|
6231
|
+
var ReasoningDisplay = memo9(ReasoningDisplayComponent);
|
|
6232
|
+
|
|
4107
6233
|
// src/app/ui/App.tsx
|
|
4108
|
-
import { jsx as
|
|
6234
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
6235
|
+
var SAFE_AUTO_APPROVE_TOOLS = [
|
|
6236
|
+
// Comunicação/UI
|
|
6237
|
+
"message_notify_user",
|
|
6238
|
+
"agent_end_turn",
|
|
6239
|
+
"todo",
|
|
6240
|
+
"task_boundary",
|
|
6241
|
+
// Leitura de ficheiros (read-only)
|
|
6242
|
+
"ls_tool",
|
|
6243
|
+
"read_file_lines",
|
|
6244
|
+
"count_file_lines",
|
|
6245
|
+
"view_file_outline",
|
|
6246
|
+
"find_by_name",
|
|
6247
|
+
"grep_search",
|
|
6248
|
+
// Status de comandos (read-only)
|
|
6249
|
+
"command_status"
|
|
6250
|
+
];
|
|
4109
6251
|
var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
4110
6252
|
const agentInstance = useRef4(null);
|
|
4111
|
-
const [history, setHistory] =
|
|
4112
|
-
const [statusMessage, setStatusMessage] =
|
|
6253
|
+
const [history, setHistory] = useState6([]);
|
|
6254
|
+
const [statusMessage, setStatusMessage] = useState6(
|
|
4113
6255
|
"Initializing agent..."
|
|
4114
6256
|
);
|
|
4115
|
-
const [toolsCount, setToolsCount] =
|
|
4116
|
-
const [mcpStatus, setMcpStatus] =
|
|
6257
|
+
const [toolsCount, setToolsCount] = useState6(null);
|
|
6258
|
+
const [mcpStatus, setMcpStatus] = useState6(
|
|
4117
6259
|
"connecting"
|
|
4118
6260
|
);
|
|
4119
|
-
const [isProcessing, setIsProcessing] =
|
|
4120
|
-
const [pendingConfirmation, setPendingConfirmation] =
|
|
6261
|
+
const [isProcessing, setIsProcessing] = useState6(true);
|
|
6262
|
+
const [pendingConfirmation, setPendingConfirmation] = useState6(
|
|
4121
6263
|
null
|
|
4122
6264
|
);
|
|
4123
|
-
const [confirmationPreview, setConfirmationPreview] =
|
|
6265
|
+
const [confirmationPreview, setConfirmationPreview] = useState6(
|
|
4124
6266
|
null
|
|
4125
6267
|
);
|
|
4126
|
-
const [isInitAgentActive, setIsInitAgentActive] =
|
|
6268
|
+
const [isInitAgentActive, setIsInitAgentActive] = useState6(false);
|
|
4127
6269
|
const alwaysAcceptList = useRef4([]);
|
|
4128
6270
|
const workdir = process.cwd();
|
|
4129
6271
|
const updateCheckRan = useRef4(false);
|
|
6272
|
+
const sessionStartTime = useRef4(Date.now());
|
|
6273
|
+
const [toolCallCount, setToolCallCount] = useState6(0);
|
|
4130
6274
|
const handleInterrupt = useCallback2(() => {
|
|
4131
6275
|
if (!isProcessing) return;
|
|
4132
6276
|
eventBus2.emit("user_interrupt");
|
|
@@ -4135,7 +6279,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4135
6279
|
...prev,
|
|
4136
6280
|
{
|
|
4137
6281
|
id: prev.length,
|
|
4138
|
-
component: /* @__PURE__ */
|
|
6282
|
+
component: /* @__PURE__ */ jsx16(Text15, { color: "yellow", children: "-- Task cancelled by dev. --" })
|
|
4139
6283
|
}
|
|
4140
6284
|
]);
|
|
4141
6285
|
}, [isProcessing, eventBus2]);
|
|
@@ -4159,11 +6303,11 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4159
6303
|
...prev,
|
|
4160
6304
|
{
|
|
4161
6305
|
id: prev.length,
|
|
4162
|
-
component: /* @__PURE__ */
|
|
6306
|
+
component: /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { color: "white", dimColor: true, children: text }) })
|
|
4163
6307
|
},
|
|
4164
6308
|
{
|
|
4165
6309
|
id: prev.length + 1,
|
|
4166
|
-
component: /* @__PURE__ */
|
|
6310
|
+
component: /* @__PURE__ */ jsx16(
|
|
4167
6311
|
SlashCommands_default,
|
|
4168
6312
|
{
|
|
4169
6313
|
input: text,
|
|
@@ -4183,8 +6327,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4183
6327
|
id: prev.length,
|
|
4184
6328
|
component: (
|
|
4185
6329
|
// Uma única Box para o espaçamento
|
|
4186
|
-
/* @__PURE__ */
|
|
4187
|
-
/* @__PURE__ */
|
|
6330
|
+
/* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "white", dimColor: true, children: [
|
|
6331
|
+
/* @__PURE__ */ jsxs15(Text15, { color: "white", children: [
|
|
4188
6332
|
">",
|
|
4189
6333
|
" "
|
|
4190
6334
|
] }),
|
|
@@ -4219,7 +6363,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4219
6363
|
[]
|
|
4220
6364
|
);
|
|
4221
6365
|
useEffect5(() => {
|
|
4222
|
-
setHistory([{ id: 0, component: /* @__PURE__ */
|
|
6366
|
+
setHistory([{ id: 0, component: /* @__PURE__ */ jsx16(Header, { sessionId: sessionId2, workdir }) }]);
|
|
4223
6367
|
const initializeAgent = async () => {
|
|
4224
6368
|
try {
|
|
4225
6369
|
agentInstance.current = new Agent(sessionId2, eventBus2);
|
|
@@ -4248,6 +6392,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4248
6392
|
}
|
|
4249
6393
|
if (parsed.type === "confirmation_request") {
|
|
4250
6394
|
const toolToConfirm = parsed.tool_calls[0].function.name;
|
|
6395
|
+
if (SAFE_AUTO_APPROVE_TOOLS.includes(toolToConfirm)) {
|
|
6396
|
+
handleConfirmation("accept", parsed.tool_calls);
|
|
6397
|
+
return;
|
|
6398
|
+
}
|
|
4251
6399
|
if (alwaysAcceptList.current.includes(toolToConfirm)) {
|
|
4252
6400
|
handleConfirmation("accept", parsed.tool_calls);
|
|
4253
6401
|
return;
|
|
@@ -4281,7 +6429,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4281
6429
|
...prev,
|
|
4282
6430
|
{
|
|
4283
6431
|
id: prev.length,
|
|
4284
|
-
component: /* @__PURE__ */
|
|
6432
|
+
component: /* @__PURE__ */ jsx16(UpdateNotice_default, { message: msg })
|
|
4285
6433
|
}
|
|
4286
6434
|
]);
|
|
4287
6435
|
}
|
|
@@ -4295,10 +6443,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4295
6443
|
}
|
|
4296
6444
|
let newComponent = null;
|
|
4297
6445
|
if (parsed.type === "debug") {
|
|
4298
|
-
newComponent = /* @__PURE__ */
|
|
6446
|
+
newComponent = /* @__PURE__ */ jsx16(Text15, { color: "gray", children: parsed.message });
|
|
4299
6447
|
} else if (parsed.type === "protocol_violation") {
|
|
4300
|
-
newComponent = /* @__PURE__ */
|
|
4301
|
-
|
|
6448
|
+
newComponent = /* @__PURE__ */ jsxs15(
|
|
6449
|
+
Box16,
|
|
4302
6450
|
{
|
|
4303
6451
|
borderStyle: "round",
|
|
4304
6452
|
borderColor: "yellow",
|
|
@@ -4306,14 +6454,14 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4306
6454
|
marginBottom: 1,
|
|
4307
6455
|
paddingX: 1,
|
|
4308
6456
|
children: [
|
|
4309
|
-
/* @__PURE__ */
|
|
4310
|
-
/* @__PURE__ */
|
|
4311
|
-
/* @__PURE__ */
|
|
6457
|
+
/* @__PURE__ */ jsx16(Text15, { color: "yellow", bold: true, children: "Protocol Violation" }),
|
|
6458
|
+
/* @__PURE__ */ jsx16(Text15, { color: "gray", children: parsed.content }),
|
|
6459
|
+
/* @__PURE__ */ jsx16(Text15, { color: "yellow", children: parsed.message })
|
|
4312
6460
|
]
|
|
4313
6461
|
}
|
|
4314
6462
|
);
|
|
4315
6463
|
} else if (parsed.type === "error") {
|
|
4316
|
-
newComponent = /* @__PURE__ */
|
|
6464
|
+
newComponent = /* @__PURE__ */ jsx16(
|
|
4317
6465
|
ErrorMessage_default,
|
|
4318
6466
|
{
|
|
4319
6467
|
message: parsed.message,
|
|
@@ -4322,8 +6470,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4322
6470
|
}
|
|
4323
6471
|
);
|
|
4324
6472
|
} else if (parsed.type === "tool_call") {
|
|
4325
|
-
const
|
|
4326
|
-
newComponent = /* @__PURE__ */
|
|
6473
|
+
const nextId2 = history.length;
|
|
6474
|
+
newComponent = /* @__PURE__ */ jsx16(
|
|
4327
6475
|
ToolCallDisplay,
|
|
4328
6476
|
{
|
|
4329
6477
|
toolName: parsed.tool_name,
|
|
@@ -4332,7 +6480,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4332
6480
|
}
|
|
4333
6481
|
);
|
|
4334
6482
|
} else if (parsed.type === "tool_result") {
|
|
4335
|
-
newComponent = /* @__PURE__ */
|
|
6483
|
+
newComponent = /* @__PURE__ */ jsx16(
|
|
4336
6484
|
ToolResultDisplay,
|
|
4337
6485
|
{
|
|
4338
6486
|
toolName: parsed.tool_name,
|
|
@@ -4340,15 +6488,17 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4340
6488
|
}
|
|
4341
6489
|
);
|
|
4342
6490
|
} else if (parsed.type === "user_overlay") {
|
|
4343
|
-
newComponent = /* @__PURE__ */
|
|
4344
|
-
/* @__PURE__ */
|
|
6491
|
+
newComponent = /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
|
|
6492
|
+
/* @__PURE__ */ jsxs15(Text15, { color: "magenta", children: [
|
|
4345
6493
|
">",
|
|
4346
6494
|
" "
|
|
4347
6495
|
] }),
|
|
4348
6496
|
parsed.payload
|
|
4349
6497
|
] }) });
|
|
6498
|
+
} else if (parsed.type === "reasoning") {
|
|
6499
|
+
newComponent = /* @__PURE__ */ jsx16(ReasoningDisplay, { reasoning: parsed.content });
|
|
4350
6500
|
} else if (parsed.type === "log") {
|
|
4351
|
-
newComponent = /* @__PURE__ */
|
|
6501
|
+
newComponent = /* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
|
|
4352
6502
|
"\u2139\uFE0F ",
|
|
4353
6503
|
parsed.message,
|
|
4354
6504
|
parsed.payload ? `: ${parsed.payload}` : ""
|
|
@@ -4381,7 +6531,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4381
6531
|
return;
|
|
4382
6532
|
}
|
|
4383
6533
|
if (pendingConfirmation) {
|
|
4384
|
-
return /* @__PURE__ */
|
|
6534
|
+
return /* @__PURE__ */ jsx16(
|
|
4385
6535
|
ConfirmationPrompt,
|
|
4386
6536
|
{
|
|
4387
6537
|
toolCalls: pendingConfirmation,
|
|
@@ -4393,9 +6543,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4393
6543
|
}
|
|
4394
6544
|
);
|
|
4395
6545
|
}
|
|
4396
|
-
return /* @__PURE__ */
|
|
4397
|
-
isProcessing && !pendingConfirmation && /* @__PURE__ */
|
|
4398
|
-
/* @__PURE__ */
|
|
6546
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
6547
|
+
isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx16(WorkingTimer, {}),
|
|
6548
|
+
/* @__PURE__ */ jsx16(
|
|
4399
6549
|
InputPrompt,
|
|
4400
6550
|
{
|
|
4401
6551
|
onSubmit: handleSubmit,
|
|
@@ -4406,12 +6556,12 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
4406
6556
|
)
|
|
4407
6557
|
] });
|
|
4408
6558
|
};
|
|
4409
|
-
return /* @__PURE__ */
|
|
4410
|
-
/* @__PURE__ */
|
|
6559
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
6560
|
+
/* @__PURE__ */ jsx16(Static, { items: history, children: (item) => /* @__PURE__ */ jsx16(Box16, { children: item.component }, item.id) }),
|
|
4411
6561
|
renderInteractiveComponent()
|
|
4412
6562
|
] });
|
|
4413
6563
|
};
|
|
4414
|
-
var App =
|
|
6564
|
+
var App = memo10(AppComponent);
|
|
4415
6565
|
var App_default = App;
|
|
4416
6566
|
|
|
4417
6567
|
// src/app/ui/utils/terminalTitle.ts
|
|
@@ -4473,9 +6623,9 @@ function stopTitleKeeper() {
|
|
|
4473
6623
|
var BLUMA_TITLE = process.env.BLUMA_TITLE || "BluMa - NomadEngenuity";
|
|
4474
6624
|
startTitleKeeper(BLUMA_TITLE);
|
|
4475
6625
|
var eventBus = new EventEmitter2();
|
|
4476
|
-
var sessionId =
|
|
6626
|
+
var sessionId = uuidv43();
|
|
4477
6627
|
var props = {
|
|
4478
6628
|
eventBus,
|
|
4479
6629
|
sessionId
|
|
4480
6630
|
};
|
|
4481
|
-
render(
|
|
6631
|
+
render(React10.createElement(App_default, props));
|