@nomad-e/bluma-cli 0.0.103 → 0.0.105

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