@nomad-e/bluma-cli 0.0.103 → 0.0.104

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