@nomad-e/bluma-cli 0.0.32 → 0.0.33

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
@@ -6,7 +6,7 @@ import { EventEmitter as EventEmitter2 } from "events";
6
6
  import { v4 as uuidv42 } from "uuid";
7
7
 
8
8
  // src/app/ui/App.tsx
9
- import { useState as useState4, useEffect as useEffect3, useRef, useCallback, memo as memo4 } from "react";
9
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback, memo as memo4 } from "react";
10
10
  import { Box as Box15, Text as Text14, Static } from "ink";
11
11
 
12
12
  // src/app/ui/layout.tsx
@@ -115,7 +115,7 @@ function inputReducer(state, action, viewWidth) {
115
115
  };
116
116
  switch (action.type) {
117
117
  case "INPUT": {
118
- const cleanInput = action.payload.replace(/(\r\n|\n|\r)/gm, "");
118
+ const cleanInput = action.payload.replace(/(||)/gm, "");
119
119
  const newText = state.text.slice(0, state.cursorPosition) + cleanInput + state.text.slice(state.cursorPosition);
120
120
  const newCursorPosition = state.cursorPosition + cleanInput.length;
121
121
  const newViewStart = adjustView(newCursorPosition, state.viewStart);
@@ -140,6 +140,14 @@ function inputReducer(state, action, viewWidth) {
140
140
  case "SUBMIT": {
141
141
  return { text: "", cursorPosition: 0, viewStart: 0 };
142
142
  }
143
+ case "SET": {
144
+ const t = action.payload.text.replace(/(||)/gm, "");
145
+ const moveToEnd = action.payload.moveCursorToEnd ?? true;
146
+ const newText = t;
147
+ const newCursorPosition = moveToEnd ? newText.length : Math.min(state.cursorPosition, newText.length);
148
+ const newViewStart = adjustView(newCursorPosition, 0);
149
+ return { text: newText, cursorPosition: newCursorPosition, viewStart: newViewStart };
150
+ }
143
151
  default:
144
152
  return state;
145
153
  }
@@ -170,6 +178,13 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
170
178
  return dispatch({ type: "INPUT", payload: input });
171
179
  }
172
180
  if (key.return) {
181
+ if (globalThis.__BLUMA_AT_OPEN__) {
182
+ return;
183
+ }
184
+ if (globalThis.__BLUMA_SUPPRESS_SUBMIT__) {
185
+ globalThis.__BLUMA_SUPPRESS_SUBMIT__ = false;
186
+ return;
187
+ }
173
188
  if (state.text.trim().length > 0) {
174
189
  onSubmit(state.text);
175
190
  dispatch({ type: "SUBMIT" });
@@ -188,12 +203,20 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
188
203
  return {
189
204
  text: state.text,
190
205
  cursorPosition: state.cursorPosition,
191
- viewStart: state.viewStart
206
+ viewStart: state.viewStart,
207
+ setText: (t, pos) => {
208
+ if (typeof pos === "number") {
209
+ dispatch({ type: "SET", payload: { text: t, moveCursorToEnd: false, cursorPosition: pos } });
210
+ } else {
211
+ dispatch({ type: "SET", payload: { text: t, moveCursorToEnd: true } });
212
+ }
213
+ },
214
+ setCursor: (pos) => dispatch({ type: "SET_CURSOR", payload: pos })
192
215
  };
193
216
  };
194
217
 
195
218
  // src/app/ui/components/InputPrompt.tsx
196
- import { useEffect, useMemo, useState } from "react";
219
+ import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
197
220
  import { EventEmitter } from "events";
198
221
 
199
222
  // src/app/ui/utils/slashRegistry.ts
@@ -242,16 +265,160 @@ var filterSlashCommands = (query) => {
242
265
  return scored.map((s) => s.cmd);
243
266
  };
244
267
 
268
+ // src/app/ui/hooks/useAtCompletion.ts
269
+ import { useEffect, useRef, useState } from "react";
270
+ import fs from "fs";
271
+ import path from "path";
272
+ var MAX_RESULTS = 50;
273
+ var DEFAULT_RECURSIVE_DEPTH = 2;
274
+ function listPathSuggestions(baseDir, pattern) {
275
+ const raw = pattern || "";
276
+ const patternEndsWithSlash = raw.endsWith("/");
277
+ const relDir = raw.replace(/^\/+|\/+$/g, "");
278
+ const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
279
+ const listDir = path.resolve(baseDir, relDir || ".");
280
+ const results = [];
281
+ const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
282
+ const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
283
+ function isIgnoredName(name) {
284
+ if (!name) return false;
285
+ if (IGNORED_DIRS.includes(name)) return true;
286
+ if (name.startsWith(".")) return true;
287
+ return false;
288
+ }
289
+ function isIgnoredFile(name) {
290
+ if (!name) return false;
291
+ for (const e of IGNORED_EXTS) if (name.endsWith(e)) return true;
292
+ return false;
293
+ }
294
+ function pushEntry(entryPath, label, isDir) {
295
+ if (results.length >= MAX_RESULTS) return;
296
+ const clean = label.split(path.sep).join("/").replace(/[]+/g, "");
297
+ results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
298
+ }
299
+ try {
300
+ if (raw.length === 0 || patternEndsWithSlash) {
301
+ const queue = [{ dir: listDir, depth: 0, rel: relDir }];
302
+ while (queue.length && results.length < MAX_RESULTS) {
303
+ const node = queue.shift();
304
+ try {
305
+ const entries = fs.readdirSync(node.dir, { withFileTypes: true });
306
+ for (const entry of entries) {
307
+ if (isIgnoredName(entry.name)) continue;
308
+ const entryAbs = path.join(node.dir, entry.name);
309
+ const entryRel = node.rel ? path.posix.join(node.rel, entry.name) : entry.name;
310
+ if (entryRel.split("/").includes("node_modules")) continue;
311
+ if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
312
+ pushEntry(entryAbs, entryRel, entry.isDirectory());
313
+ if (entry.isDirectory() && node.depth < DEFAULT_RECURSIVE_DEPTH) {
314
+ queue.push({ dir: entryAbs, depth: node.depth + 1, rel: node.rel ? node.rel + "/" + entry.name : entry.name + "/" });
315
+ }
316
+ if (results.length >= MAX_RESULTS) break;
317
+ }
318
+ } catch (e) {
319
+ }
320
+ }
321
+ } else {
322
+ const entries = fs.readdirSync(listDir, { withFileTypes: true });
323
+ for (const entry of entries) {
324
+ if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
325
+ if (isIgnoredName(entry.name)) continue;
326
+ if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
327
+ const entryAbs = path.join(listDir, entry.name);
328
+ const label = relDir ? path.posix.join(relDir, entry.name) : entry.name;
329
+ pushEntry(entryAbs, label, entry.isDirectory());
330
+ if (results.length >= MAX_RESULTS) break;
331
+ }
332
+ }
333
+ } catch (e) {
334
+ }
335
+ return results.slice(0, MAX_RESULTS);
336
+ }
337
+ function useAtCompletion({
338
+ cwd,
339
+ text,
340
+ cursorPosition,
341
+ setText
342
+ }) {
343
+ const [open, setOpen] = useState(false);
344
+ const [selected, setSelected] = useState(0);
345
+ const [suggestions, setSuggestions] = useState([]);
346
+ const lastQuery = useRef("");
347
+ function scanForAt(text2, pos) {
348
+ const before = text2.slice(0, pos);
349
+ const m = before.match(/@([\w\/.\-_]*)$/);
350
+ if (!m) return { pattern: null, insertStart: -1 };
351
+ return { pattern: m[1] || "", insertStart: m.index + 1 };
352
+ }
353
+ function update(newText, newCursor) {
354
+ const res = scanForAt(newText, newCursor);
355
+ if (res.pattern !== null && res.pattern.length >= 0) {
356
+ setOpen(true);
357
+ globalThis.__BLUMA_AT_OPEN__ = true;
358
+ const suggs = listPathSuggestions(cwd, res.pattern);
359
+ setSuggestions(suggs);
360
+ setSelected(0);
361
+ lastQuery.current = res.pattern;
362
+ } else {
363
+ setOpen(false);
364
+ globalThis.__BLUMA_AT_OPEN__ = false;
365
+ setSuggestions([]);
366
+ }
367
+ }
368
+ useEffect(() => {
369
+ update(text, cursorPosition);
370
+ }, [text, cursorPosition, cwd]);
371
+ function insertAtSelection() {
372
+ if (!open || !suggestions[selected]) return;
373
+ const res = scanForAt(text, cursorPosition);
374
+ if (!res || res.insertStart < 0) return;
375
+ let chosen = suggestions[selected].label;
376
+ const isDir = suggestions[selected].isDir;
377
+ chosen = chosen.replace(/\\/g, "/").replace(/\|/g, "");
378
+ let insertVal = chosen;
379
+ if (insertVal.includes("/")) {
380
+ insertVal = insertVal.replace(/\/+$/g, "");
381
+ const parts = insertVal.split("/");
382
+ insertVal = parts[parts.length - 1];
383
+ }
384
+ if (isDir && !insertVal.endsWith("/")) insertVal = insertVal + "/";
385
+ const pattern = res.pattern || "";
386
+ const lastSlash = pattern.lastIndexOf("/");
387
+ const segmentOffset = lastSlash >= 0 ? lastSlash + 1 : 0;
388
+ const segmentStart = res.insertStart + segmentOffset;
389
+ const before = text.slice(0, segmentStart);
390
+ const after = text.slice(cursorPosition);
391
+ const newText = before + insertVal + after;
392
+ setText(newText + (isDir ? "" : " "), true);
393
+ if (isDir) {
394
+ setOpen(false);
395
+ setSuggestions([]);
396
+ setTimeout(() => {
397
+ setOpen(true);
398
+ update(newText, newText.length);
399
+ }, 0);
400
+ } else {
401
+ setOpen(false);
402
+ setSuggestions([]);
403
+ }
404
+ }
405
+ function close() {
406
+ setOpen(false);
407
+ setSuggestions([]);
408
+ }
409
+ return { open, suggestions, selected, setSelected, insertAtSelection, close, update };
410
+ }
411
+
245
412
  // src/app/ui/components/InputPrompt.tsx
246
413
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
247
414
  var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter();
248
415
  global.__bluma_ui_eventbus__ = uiEventBus;
249
416
  var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing = false }) => {
250
417
  const { stdout } = useStdout();
251
- const [viewWidth, setViewWidth] = useState(() => stdout.columns - 6);
252
- const [slashOpen, setSlashOpen] = useState(false);
253
- const [slashIndex, setSlashIndex] = useState(0);
254
- useEffect(() => {
418
+ const [viewWidth, setViewWidth] = useState2(() => stdout.columns - 6);
419
+ const [slashOpen, setSlashOpen] = useState2(false);
420
+ const [slashIndex, setSlashIndex] = useState2(0);
421
+ useEffect2(() => {
255
422
  const onResize = () => setViewWidth(stdout.columns - 6);
256
423
  stdout.on("resize", onResize);
257
424
  return () => {
@@ -263,7 +430,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
263
430
  if (isReadOnly) {
264
431
  if (trimmed.length > 0) {
265
432
  const payload = trimmed;
266
- uiEventBus.emit("dev_overlay", { kind: "message", payload, ts: Date.now() });
433
+ uiEventBus.emit("user_overlay", { kind: "message", payload, ts: Date.now() });
267
434
  return;
268
435
  }
269
436
  return;
@@ -271,9 +438,11 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
271
438
  onSubmit(value);
272
439
  };
273
440
  const effectiveReadOnly = isReadOnly;
274
- const { text, cursorPosition, viewStart } = useCustomInput({
441
+ const { text, cursorPosition, viewStart, setText } = useCustomInput({
442
+ // Sobrepõe a lógica padrão: nunca submete se autocomplete aberto
275
443
  onSubmit: (value) => {
276
444
  if (disableWhileProcessing && isReadOnly) return;
445
+ if (pathAutocomplete.open) return;
277
446
  permissiveOnSubmit(value);
278
447
  },
279
448
  viewWidth,
@@ -294,7 +463,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
294
463
  if (!slashQuery) return [];
295
464
  return filterSlashCommands(slashQuery);
296
465
  }, [slashQuery]);
297
- useEffect(() => {
466
+ useEffect2(() => {
298
467
  if (isReadOnly) {
299
468
  setSlashOpen(false);
300
469
  return;
@@ -317,12 +486,51 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
317
486
  if (choice) {
318
487
  const cmd = choice.name;
319
488
  setSlashOpen(false);
320
- permissiveOnSubmit(cmd);
489
+ try {
490
+ setText(`${cmd} `, true);
491
+ } catch (e) {
492
+ permissiveOnSubmit(`${cmd} `);
493
+ }
321
494
  }
322
495
  } else if (key.escape) {
323
496
  setSlashOpen(false);
324
497
  }
325
498
  }, { isActive: slashOpen });
499
+ const cwd = process.cwd();
500
+ const pathAutocomplete = useAtCompletion({ cwd, text, cursorPosition, setText });
501
+ useInput2((input, key) => {
502
+ if (pathAutocomplete.open) {
503
+ if (key.downArrow) {
504
+ pathAutocomplete.setSelected((i) => Math.min(i + 1, Math.max(0, pathAutocomplete.suggestions.length - 1)));
505
+ return;
506
+ } else if (key.upArrow) {
507
+ pathAutocomplete.setSelected((i) => Math.max(i - 1, 0));
508
+ return;
509
+ } else if (key.return || key.tab) {
510
+ const selected = pathAutocomplete.suggestions[pathAutocomplete.selected];
511
+ if (selected) {
512
+ const before = text.slice(0, cursorPosition);
513
+ const m = before.match(/@([\w\/.\-_]*)$/);
514
+ if (m) {
515
+ globalThis.__BLUMA_SUPPRESS_SUBMIT__ = true;
516
+ pathAutocomplete.insertAtSelection();
517
+ }
518
+ }
519
+ return;
520
+ } else if (key.escape) {
521
+ pathAutocomplete.close();
522
+ return;
523
+ }
524
+ return;
525
+ }
526
+ }, { isActive: true });
527
+ function canSubmitGivenCursor() {
528
+ if (visibleCursorPosition < visibleText.length) {
529
+ return visibleText[visibleCursorPosition] === " ";
530
+ } else {
531
+ return false;
532
+ }
533
+ }
326
534
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
327
535
  disableWhileProcessing ? (
328
536
  // Modo bloqueado visualmente, mantendo hooks estáveis
@@ -343,6 +551,22 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
343
551
  /* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorGlyph }),
344
552
  showPlaceholder ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(Text2, { children: textAfterCursor })
345
553
  ] }) }),
554
+ pathAutocomplete.open && pathAutocomplete.suggestions.length > 0 && (() => {
555
+ const VISIBLE = 7;
556
+ const total = pathAutocomplete.suggestions.length;
557
+ const sel = Math.max(0, Math.min(pathAutocomplete.selected, total - 1));
558
+ let start = Math.max(0, sel - Math.floor(VISIBLE / 2));
559
+ if (start + VISIBLE > total) start = Math.max(0, total - VISIBLE);
560
+ const windowItems = pathAutocomplete.suggestions.slice(start, start + VISIBLE);
561
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, height: Math.min(VISIBLE, total), overflowY: "auto", children: windowItems.map((s, idx) => {
562
+ const realIdx = start + idx;
563
+ const isSelected = realIdx === pathAutocomplete.selected;
564
+ return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
565
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "cyan" : "gray", children: isSelected ? "\u276F " : " " }),
566
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "cyan" : "white", bold: isSelected, dimColor: !isSelected, children: s.label })
567
+ ] }, s.fullPath);
568
+ }) });
569
+ })(),
346
570
  slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
347
571
  const isSelected = idx === slashIndex;
348
572
  return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
@@ -369,7 +593,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
369
593
  import { Box as Box6, Text as Text6 } from "ink";
370
594
 
371
595
  // src/app/ui/InteractiveMenu.tsx
372
- import { useState as useState2, memo } from "react";
596
+ import { useState as useState3, memo } from "react";
373
597
  import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
374
598
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
375
599
  var InteractiveMenuComponent = ({ onDecision }) => {
@@ -378,7 +602,7 @@ var InteractiveMenuComponent = ({ onDecision }) => {
378
602
  { label: "2. No, cancel this command", value: "decline" },
379
603
  { label: "3. Always allow this type of command", value: "accept_always" }
380
604
  ];
381
- const [selectedOption, setSelectedOption] = useState2(0);
605
+ const [selectedOption, setSelectedOption] = useState3(0);
382
606
  useInput3((input, key) => {
383
607
  if (key.upArrow) {
384
608
  setSelectedOption((prev) => prev > 0 ? prev - 1 : options.length - 1);
@@ -419,7 +643,7 @@ var InteractiveMenu = memo(InteractiveMenuComponent);
419
643
 
420
644
  // src/app/ui/components/promptRenderers.tsx
421
645
  import { Box as Box5, Text as Text5 } from "ink";
422
- import path from "path";
646
+ import path2 from "path";
423
647
 
424
648
  // src/app/ui/components/SimpleDiff.tsx
425
649
  import { Box as Box4, Text as Text4 } from "ink";
@@ -458,7 +682,7 @@ var SimpleDiff = ({ text, maxHeight }) => {
458
682
  // src/app/ui/components/promptRenderers.tsx
459
683
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
460
684
  var getBasePath = (filePath) => {
461
- return path.basename(filePath);
685
+ return path2.basename(filePath);
462
686
  };
463
687
  var renderShellCommand = ({
464
688
  toolCall
@@ -626,12 +850,12 @@ var ConfirmationPrompt = ({ toolCalls, preview, onDecision }) => {
626
850
  // src/app/agent/agent.ts
627
851
  import OpenAI from "openai";
628
852
  import * as dotenv from "dotenv";
629
- import path8 from "path";
853
+ import path9 from "path";
630
854
  import os6 from "os";
631
855
 
632
856
  // src/app/agent/tool_invoker.ts
633
- import { promises as fs5 } from "fs";
634
- import path4 from "path";
857
+ import { promises as fs6 } from "fs";
858
+ import path5 from "path";
635
859
  import { fileURLToPath } from "url";
636
860
 
637
861
  // src/app/agent/tools/natives/shell_command.ts
@@ -699,8 +923,8 @@ ${stderr.trim()}`.trim();
699
923
  }
700
924
 
701
925
  // src/app/agent/tools/natives/edit.ts
702
- import path2 from "path";
703
- import { promises as fs } from "fs";
926
+ import path3 from "path";
927
+ import { promises as fs2 } from "fs";
704
928
  import { diffLines } from "diff";
705
929
  function unescapeLlmString(inputString) {
706
930
  return inputString.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
@@ -736,7 +960,7 @@ async function calculateEdit(filePath, oldString, newString, expectedReplacement
736
960
  let finalOldString = oldString.replace(/\r\n/g, "\n");
737
961
  let occurrences = 0;
738
962
  try {
739
- currentContent = await fs.readFile(filePath, "utf-8");
963
+ currentContent = await fs2.readFile(filePath, "utf-8");
740
964
  currentContent = currentContent.replace(/\r\n/g, "\n");
741
965
  } catch (e) {
742
966
  if (e.code !== "ENOENT") {
@@ -792,7 +1016,7 @@ function createDiff(filename, oldContent, newContent) {
792
1016
  }
793
1017
  async function editTool(args) {
794
1018
  const { file_path, old_string, new_string, expected_replacements = 1 } = args;
795
- if (!path2.isAbsolute(file_path)) {
1019
+ if (!path3.isAbsolute(file_path)) {
796
1020
  return { success: false, error: `Invalid parameters: file_path must be absolute.`, file_path };
797
1021
  }
798
1022
  if (file_path.includes("..")) {
@@ -808,10 +1032,10 @@ async function editTool(args) {
808
1032
  file_path
809
1033
  };
810
1034
  }
811
- await fs.mkdir(path2.dirname(file_path), { recursive: true });
812
- await fs.writeFile(file_path, editData.newContent, "utf-8");
813
- const relativePath = path2.relative(process.cwd(), file_path);
814
- const filename = path2.basename(file_path);
1035
+ await fs2.mkdir(path3.dirname(file_path), { recursive: true });
1036
+ await fs2.writeFile(file_path, editData.newContent, "utf-8");
1037
+ const relativePath = path3.relative(process.cwd(), file_path);
1038
+ const filename = path3.basename(file_path);
815
1039
  if (editData.isNewFile) {
816
1040
  return {
817
1041
  success: true,
@@ -846,10 +1070,10 @@ ${finalDiff}`,
846
1070
 
847
1071
  // src/app/agent/tools/natives/message.ts
848
1072
  import { v4 as uuidv4 } from "uuid";
849
- function messageNotifyDev(args) {
1073
+ function messageNotifyuser(args) {
850
1074
  const { text_markdown } = args;
851
1075
  const notification = {
852
- type: "message_notify_dev",
1076
+ type: "message_notify_user",
853
1077
  id: `notify_${uuidv4()}`,
854
1078
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
855
1079
  content: {
@@ -863,8 +1087,8 @@ function messageNotifyDev(args) {
863
1087
  }
864
1088
 
865
1089
  // src/app/agent/tools/natives/ls.ts
866
- import { promises as fs2 } from "fs";
867
- import path3 from "path";
1090
+ import { promises as fs3 } from "fs";
1091
+ import path4 from "path";
868
1092
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
869
1093
  ".git",
870
1094
  ".gitignore",
@@ -892,8 +1116,8 @@ async function ls(args) {
892
1116
  max_depth
893
1117
  } = args;
894
1118
  try {
895
- const basePath = path3.resolve(directory_path);
896
- if (!(await fs2.stat(basePath)).isDirectory()) {
1119
+ const basePath = path4.resolve(directory_path);
1120
+ if (!(await fs3.stat(basePath)).isDirectory()) {
897
1121
  throw new Error(`Directory '${directory_path}' not found.`);
898
1122
  }
899
1123
  const allIgnorePatterns = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore_patterns]);
@@ -902,11 +1126,11 @@ async function ls(args) {
902
1126
  const allDirs = [];
903
1127
  const walk = async (currentDir, currentDepth) => {
904
1128
  if (max_depth !== void 0 && currentDepth > max_depth) return;
905
- const entries = await fs2.readdir(currentDir, { withFileTypes: true });
1129
+ const entries = await fs3.readdir(currentDir, { withFileTypes: true });
906
1130
  for (const entry of entries) {
907
1131
  const entryName = entry.name;
908
- const fullPath = path3.join(currentDir, entryName);
909
- const posixPath = fullPath.split(path3.sep).join("/");
1132
+ const fullPath = path4.join(currentDir, entryName);
1133
+ const posixPath = fullPath.split(path4.sep).join("/");
910
1134
  const isHidden = entryName.startsWith(".");
911
1135
  if (allIgnorePatterns.has(entryName) || isHidden && !show_hidden) {
912
1136
  continue;
@@ -917,7 +1141,7 @@ async function ls(args) {
917
1141
  await walk(fullPath, currentDepth + 1);
918
1142
  }
919
1143
  } else if (entry.isFile()) {
920
- if (!normalizedExtensions || normalizedExtensions.includes(path3.extname(entryName).toLowerCase())) {
1144
+ if (!normalizedExtensions || normalizedExtensions.includes(path4.extname(entryName).toLowerCase())) {
921
1145
  allFiles.push(posixPath);
922
1146
  }
923
1147
  }
@@ -928,7 +1152,7 @@ async function ls(args) {
928
1152
  allDirs.sort();
929
1153
  return {
930
1154
  success: true,
931
- path: basePath.split(path3.sep).join("/"),
1155
+ path: basePath.split(path4.sep).join("/"),
932
1156
  recursive,
933
1157
  total_files: allFiles.length,
934
1158
  total_directories: allDirs.length,
@@ -944,17 +1168,17 @@ async function ls(args) {
944
1168
  }
945
1169
 
946
1170
  // src/app/agent/tools/natives/readLines.ts
947
- import { promises as fs3 } from "fs";
1171
+ import { promises as fs4 } from "fs";
948
1172
  async function readLines(args) {
949
1173
  const { filepath, start_line, end_line } = args;
950
1174
  try {
951
- if (!(await fs3.stat(filepath)).isFile()) {
1175
+ if (!(await fs4.stat(filepath)).isFile()) {
952
1176
  throw new Error(`File '${filepath}' not found or is not a file.`);
953
1177
  }
954
1178
  if (start_line < 1 || end_line < start_line) {
955
1179
  throw new Error("Invalid line range. start_line must be >= 1 and end_line must be >= start_line.");
956
1180
  }
957
- const fileContent = await fs3.readFile(filepath, "utf-8");
1181
+ const fileContent = await fs4.readFile(filepath, "utf-8");
958
1182
  const lines = fileContent.split("\n");
959
1183
  const total_lines = lines.length;
960
1184
  const startIndex = start_line - 1;
@@ -982,12 +1206,12 @@ async function readLines(args) {
982
1206
 
983
1207
  // src/app/agent/tools/natives/count_lines.ts
984
1208
  import { createReadStream } from "fs";
985
- import { promises as fs4 } from "fs";
1209
+ import { promises as fs5 } from "fs";
986
1210
  import readline from "readline";
987
1211
  async function countLines(args) {
988
1212
  const { filepath } = args;
989
1213
  try {
990
- if (!(await fs4.stat(filepath)).isFile()) {
1214
+ if (!(await fs5.stat(filepath)).isFile()) {
991
1215
  throw new Error(`File '${filepath}' not found or is not a file.`);
992
1216
  }
993
1217
  const fileStream = createReadStream(filepath);
@@ -1022,9 +1246,9 @@ var ToolInvoker = class {
1022
1246
  async initialize() {
1023
1247
  try {
1024
1248
  const __filename = fileURLToPath(import.meta.url);
1025
- const __dirname = path4.dirname(__filename);
1026
- const configPath = path4.resolve(__dirname, "config", "native_tools.json");
1027
- const fileContent = await fs5.readFile(configPath, "utf-8");
1249
+ const __dirname = path5.dirname(__filename);
1250
+ const configPath = path5.resolve(__dirname, "config", "native_tools.json");
1251
+ const fileContent = await fs6.readFile(configPath, "utf-8");
1028
1252
  const config2 = JSON.parse(fileContent);
1029
1253
  this.toolDefinitions = config2.nativeTools;
1030
1254
  } catch (error) {
@@ -1039,7 +1263,7 @@ var ToolInvoker = class {
1039
1263
  registerTools() {
1040
1264
  this.toolImplementations.set("shell_command", shellCommand);
1041
1265
  this.toolImplementations.set("edit_tool", editTool);
1042
- this.toolImplementations.set("message_notify_dev", messageNotifyDev);
1266
+ this.toolImplementations.set("message_notify_user", messageNotifyuser);
1043
1267
  this.toolImplementations.set("ls_tool", ls);
1044
1268
  this.toolImplementations.set("count_file_lines", countLines);
1045
1269
  this.toolImplementations.set("read_file_lines", readLines);
@@ -1073,8 +1297,8 @@ var ToolInvoker = class {
1073
1297
  };
1074
1298
 
1075
1299
  // src/app/agent/tools/mcp/mcp_client.ts
1076
- import { promises as fs6 } from "fs";
1077
- import path5 from "path";
1300
+ import { promises as fs7 } from "fs";
1301
+ import path6 from "path";
1078
1302
  import os2 from "os";
1079
1303
  import { fileURLToPath as fileURLToPath2 } from "url";
1080
1304
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -1102,9 +1326,9 @@ var MCPClient = class {
1102
1326
  });
1103
1327
  }
1104
1328
  const __filename = fileURLToPath2(import.meta.url);
1105
- const __dirname = path5.dirname(__filename);
1106
- const defaultConfigPath = path5.resolve(__dirname, "config", "bluma-mcp.json");
1107
- const userConfigPath = path5.join(os2.homedir(), ".bluma-cli", "bluma-mcp.json");
1329
+ const __dirname = path6.dirname(__filename);
1330
+ const defaultConfigPath = path6.resolve(__dirname, "config", "bluma-mcp.json");
1331
+ const userConfigPath = path6.join(os2.homedir(), ".bluma-cli", "bluma-mcp.json");
1108
1332
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
1109
1333
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
1110
1334
  const mergedConfig = {
@@ -1138,7 +1362,7 @@ var MCPClient = class {
1138
1362
  }
1139
1363
  async loadMcpConfig(configPath, configType) {
1140
1364
  try {
1141
- const fileContent = await fs6.readFile(configPath, "utf-8");
1365
+ const fileContent = await fs7.readFile(configPath, "utf-8");
1142
1366
  const processedContent = this.replaceEnvPlaceholders(fileContent);
1143
1367
  return JSON.parse(processedContent);
1144
1368
  } catch (error) {
@@ -1273,12 +1497,12 @@ var AdvancedFeedbackSystem = class {
1273
1497
  };
1274
1498
 
1275
1499
  // src/app/agent/bluma/core/bluma.ts
1276
- import path7 from "path";
1500
+ import path8 from "path";
1277
1501
 
1278
1502
  // src/app/agent/session_manger/session_manager.ts
1279
- import path6 from "path";
1503
+ import path7 from "path";
1280
1504
  import os3 from "os";
1281
- import { promises as fs7 } from "fs";
1505
+ import { promises as fs8 } from "fs";
1282
1506
  var fileLocks = /* @__PURE__ */ new Map();
1283
1507
  async function withFileLock(file, fn) {
1284
1508
  const prev = fileLocks.get(file) || Promise.resolve();
@@ -1296,13 +1520,13 @@ async function withFileLock(file, fn) {
1296
1520
  function expandHome(p) {
1297
1521
  if (!p) return p;
1298
1522
  if (p.startsWith("~")) {
1299
- return path6.join(os3.homedir(), p.slice(1));
1523
+ return path7.join(os3.homedir(), p.slice(1));
1300
1524
  }
1301
1525
  return p;
1302
1526
  }
1303
1527
  function getPreferredAppDir() {
1304
- const fixed = path6.join(os3.homedir(), ".bluma-cli");
1305
- return path6.resolve(expandHome(fixed));
1528
+ const fixed = path7.join(os3.homedir(), ".bluma-cli");
1529
+ return path7.resolve(expandHome(fixed));
1306
1530
  }
1307
1531
  async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1308
1532
  let attempt = 0;
@@ -1310,7 +1534,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1310
1534
  const isWin = process.platform === "win32";
1311
1535
  while (attempt <= maxRetries) {
1312
1536
  try {
1313
- await fs7.rename(src, dest);
1537
+ await fs8.rename(src, dest);
1314
1538
  return;
1315
1539
  } catch (e) {
1316
1540
  lastErr = e;
@@ -1323,9 +1547,9 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1323
1547
  }
1324
1548
  }
1325
1549
  try {
1326
- const data = await fs7.readFile(src);
1327
- await fs7.writeFile(dest, data);
1328
- await fs7.unlink(src).catch(() => {
1550
+ const data = await fs8.readFile(src);
1551
+ await fs8.writeFile(dest, data);
1552
+ await fs8.unlink(src).catch(() => {
1329
1553
  });
1330
1554
  return;
1331
1555
  } catch (fallbackErr) {
@@ -1334,16 +1558,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1334
1558
  }
1335
1559
  async function ensureSessionDir() {
1336
1560
  const appDir = getPreferredAppDir();
1337
- const sessionDir = path6.join(appDir, "sessions");
1338
- await fs7.mkdir(sessionDir, { recursive: true });
1561
+ const sessionDir = path7.join(appDir, "sessions");
1562
+ await fs8.mkdir(sessionDir, { recursive: true });
1339
1563
  return sessionDir;
1340
1564
  }
1341
1565
  async function loadOrcreateSession(sessionId2) {
1342
1566
  const sessionDir = await ensureSessionDir();
1343
- const sessionFile = path6.join(sessionDir, `${sessionId2}.json`);
1567
+ const sessionFile = path7.join(sessionDir, `${sessionId2}.json`);
1344
1568
  try {
1345
- await fs7.access(sessionFile);
1346
- const fileContent = await fs7.readFile(sessionFile, "utf-8");
1569
+ await fs8.access(sessionFile);
1570
+ const fileContent = await fs8.readFile(sessionFile, "utf-8");
1347
1571
  const sessionData = JSON.parse(fileContent);
1348
1572
  return [sessionFile, sessionData.conversation_history || []];
1349
1573
  } catch (error) {
@@ -1352,7 +1576,7 @@ async function loadOrcreateSession(sessionId2) {
1352
1576
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
1353
1577
  conversation_history: []
1354
1578
  };
1355
- await fs7.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
1579
+ await fs8.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
1356
1580
  return [sessionFile, []];
1357
1581
  }
1358
1582
  }
@@ -1360,12 +1584,12 @@ async function saveSessionHistory(sessionFile, history) {
1360
1584
  await withFileLock(sessionFile, async () => {
1361
1585
  let sessionData;
1362
1586
  try {
1363
- const dir = path6.dirname(sessionFile);
1364
- await fs7.mkdir(dir, { recursive: true });
1587
+ const dir = path7.dirname(sessionFile);
1588
+ await fs8.mkdir(dir, { recursive: true });
1365
1589
  } catch {
1366
1590
  }
1367
1591
  try {
1368
- const fileContent = await fs7.readFile(sessionFile, "utf-8");
1592
+ const fileContent = await fs8.readFile(sessionFile, "utf-8");
1369
1593
  sessionData = JSON.parse(fileContent);
1370
1594
  } catch (error) {
1371
1595
  const code = error && error.code;
@@ -1376,14 +1600,14 @@ async function saveSessionHistory(sessionFile, history) {
1376
1600
  console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
1377
1601
  }
1378
1602
  }
1379
- const sessionId2 = path6.basename(sessionFile, ".json");
1603
+ const sessionId2 = path7.basename(sessionFile, ".json");
1380
1604
  sessionData = {
1381
1605
  session_id: sessionId2,
1382
1606
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
1383
1607
  conversation_history: []
1384
1608
  };
1385
1609
  try {
1386
- await fs7.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1610
+ await fs8.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1387
1611
  } catch {
1388
1612
  }
1389
1613
  }
@@ -1391,7 +1615,7 @@ async function saveSessionHistory(sessionFile, history) {
1391
1615
  sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
1392
1616
  const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
1393
1617
  try {
1394
- await fs7.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1618
+ await fs8.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1395
1619
  await safeRenameWithRetry(tempSessionFile, sessionFile);
1396
1620
  } catch (writeError) {
1397
1621
  if (writeError instanceof Error) {
@@ -1400,7 +1624,7 @@ async function saveSessionHistory(sessionFile, history) {
1400
1624
  console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
1401
1625
  }
1402
1626
  try {
1403
- await fs7.unlink(tempSessionFile);
1627
+ await fs8.unlink(tempSessionFile);
1404
1628
  } catch {
1405
1629
  }
1406
1630
  }
@@ -1411,320 +1635,73 @@ async function saveSessionHistory(sessionFile, history) {
1411
1635
  import os4 from "os";
1412
1636
  var SYSTEM_PROMPT = `
1413
1637
 
1414
- ### YOU ARE BluMa CLI \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
1415
- You use a proprietary Large Language Model (LLM) fine-tuned by the NomadEngenuity team.
1416
-
1638
+ ### IDENTITY AND OBJECTIVE
1639
+ You are BluMa, an autonomous AI Software Engineer developed by the specialists at NomadEngenuity.
1640
+ You leverage a proprietary Large Language Model, fine-tuned specifically for complex software engineering and code generation tasks.
1641
+ Your objective is to analyze user requests, formulate a precise plan, and execute that plan flawlessly using your available tools.
1642
+ You operate with the highest standards of professionalism, precision, and safety.
1417
1643
  ---
1418
1644
 
1419
- ## BEHAVIORAL RULES
1420
-
1421
- - **Identity:**
1422
- You are BluMa (NomadEngenuity). Maintain professionalism and technical language.
1423
-
1424
- - **Communication:**
1425
- ALL messages must be sent via 'message_notify_dev'.
1426
- **No direct text replies to the developer.**
1645
+ ### CORE DIRECTIVES
1427
1646
 
1428
- - **Task Completion:**
1429
- When a task is completed, immediately invoke 'agent_end_task' without dev permissions.
1647
+ 1. **THINK FIRST, ACT SECOND:** Your first action in any turn is to formulate an internal plan. Use the mandatory **"Reasoning Process"** format detailed below.
1648
+ 2. **TOOL-BASED OPERATION:** All actions and communications MUST be performed through a tool call. NEVER respond with free-form text.
1649
+ 3. **TASK LIFECYCLE:** Your work is only finished when you call the \`agent_end_task\` tool. Each tool call is a step within your current turn. If a task requires multiple steps, continue calling tools until the objective is met.
1650
+ 4. **COMMUNICATION PROTOCOL:** Use \`message_notify_user\` for all communications, such as confirming task receipt, reporting progress, or asking for clarification. Be concise.
1651
+ 5. **ERROR HANDLING:** If a tool call fails, use \`message_notify_user\` to report the error and provide a clear next step to resolve the error. Always try to recover from errors.
1430
1652
 
1431
- - **Tool Rules:**
1432
- Never make parallel tool calls.
1433
- Always use only the defined tools with their exact names.
1434
-
1435
- - **Autonomy:**
1436
- Act 100% autonomously.
1437
- Do not ask for formatting preferences.
1438
- Use the notebook for internal reasoning.
1653
+ ---
1439
1654
 
1655
+ ### TOOL USAGE GUIDELINES
1440
1656
 
1441
- ### CRITICAL COMMUNICATION PROTOCOL
1442
- - Only tool_calls are allowed for assistant replies. Never include a "content" field.
1443
- - Always use tools to respond, retrieve data, compute or transform. Await a valid tool response before any final message.
1444
- - Zero tolerance for protocol violations.
1657
+ - **File modification:** Always use the \`edit_tool\` tool to create or modify files in a structured manner. Strictly follow the tool's layout to provide the necessary context.
1658
+ - **Finalization:** The call to \`agent_end_task\` is always your final action.
1659
+ ---
1445
1660
 
1661
+ ### CURRENT ENVIRONMENT CONTEXT
1446
1662
  <current_system_environment>
1447
1663
  - Operating System: {os_type} ({os_version})
1448
1664
  - Architecture: {architecture}
1449
- - Current Working Directory: {workdir}
1665
+ - Current Directory: {workdir}
1450
1666
  - Shell: {shell_type}
1451
- - Username: {username}
1667
+ - User: {username}
1452
1668
  - Current Date: {current_date}
1453
- - Timezone: {timezone}
1454
- - Locale: {locale}
1455
1669
  </current_system_environment>
1456
1670
 
1671
+ ---
1457
1672
 
1458
- <mermaid_diagrams>
1459
- # MERMAID DIAGRAM CREATION - PERFECT SYNTAX REQUIRED!
1460
- ## CRITICAL: ALL DIAGRAMS MUST RENDER WITHOUT ERRORS
1461
-
1462
- ### MANDATORY MERMAID SYNTAX RULES
1463
- 1. **ALWAYS wrap ALL labels in double quotes**: "label text"
1464
- 2. **NEVER use unescaped special characters**: /, (), [], {}, +, *, ?, ^, $, |, 3. **Use line breaks (<br/>) for multi-line labels**: "Line 1<br/>Line 2"
1465
- 4. **NO custom colors or ::: syntax**: Stick to standard themes
1466
- 5. **NO beta features**: Use only stable Mermaid syntax
1467
- 6. **NO remote images**: Never embed external images
1468
-
1469
- ### SAFE LABEL FORMATTING
1470
-
1471
- CORRECT:
1472
- - "dev Authentication"
1473
- - "API Gateway (REST)"
1474
- - "Database Connection<br/>MySQL 8.0"
1475
- - "Process Data<br/>Transform & Validate"
1476
-
1477
- INCORRECT:
1478
- - dev Authentication (missing quotes)
1479
- - API Gateway (REST) (parentheses without quotes)
1480
- - Database/MySQL (slash without quotes)
1481
- - Process & Transform (ampersand without quotes)
1482
-
1483
-
1484
- ### DIAGRAM TYPE BEST PRACTICES
1485
-
1486
- IMPORTANT
1487
- The Notion API rejects rich text of type "code" and only accepts "text" for code blocks \u2013
1488
- a limitation of their own JSON (even for the language: "mermaid"). Therefore, only the code/text block
1489
- type is viable.
1490
-
1491
- You should insert the pretty diagram without any delimiters or headers, in the code block with
1492
- proper indentation and double quotes, only in the text field, to facilitate as little manual rework as possible.
1493
- It's ready to copy into the native Mermaid block.
1494
-
1495
- #### FLOWCHART
1496
-
1497
- flowchart TD
1498
- A["Start Process"] --> B["Validate Input"]
1499
- B --> C{"Is Valid?"}
1500
- C -->|"Yes"| D["Process Data"]
1501
- C -->|"No"| E["Return Error"]
1502
- D --> F["Save to Database"]
1503
- F --> G["Send Response"]
1504
-
1505
-
1506
- #### SEQUENCE DIAGRAM
1507
-
1508
- sequenceDiagram
1509
- participant U as "dev"
1510
- participant A as "API Gateway"
1511
- participant D as "Database"
1512
-
1513
- U->>A: "Submit Request"
1514
- A->>D: "Query Data"
1515
- D-->>A: "Return Results"
1516
- A-->>U: "Response Data"
1517
-
1518
- #### CLASS DIAGRAM
1519
-
1520
- classDiagram
1521
- class dev {
1522
- +String name
1523
- +String email
1524
- +authenticate()
1525
- +updateProfile()
1526
- }
1527
-
1528
- class Database {
1529
- +connect()
1530
- +query()
1531
- +close()
1532
- }
1533
-
1534
- dev --> Database : "uses"
1535
-
1536
-
1537
- ### VALIDATION CHECKLIST
1538
- Before creating any diagram, ensure:
1539
- - [ ] All labels are wrapped in double quotes
1540
- - [ ] No unescaped special characters (/, (), etc.)
1541
- - [ ] Line breaks use <br/> syntax
1542
- - [ ] No custom colors or styling
1543
- - [ ] No beta features or experimental syntax
1544
- - [ ] All connections use proper arrow syntax
1545
- - [ ] Node IDs are simple alphanumeric
1546
-
1547
- ### ERROR PREVENTION
1548
- - Always test diagram syntax mentally before generating
1549
- - Use simple, descriptive labels without special formatting
1550
- - Prefer clarity over visual complexity
1551
- - Keep diagrams focused and readable
1552
- - Use standard Mermaid themes only
1553
-
1554
- ## ZERO TOLERANCE FOR SYNTAX ERRORS
1555
- Every diagram MUST render perfectly on first try. No exceptions.
1556
- </mermaid_diagrams>
1557
-
1673
+ ### COMMUNICATION PROTOCOL
1558
1674
  <message_rules>
1559
- - Communicate with dev's via message tools instead of direct text responses
1675
+ - Communicate with user's via message tools instead of direct text responses
1560
1676
  - Reply immediately to new user messages before other operations
1561
1677
  - First reply must be brief, only confirming receipt without specific solutions
1562
- - Notify dev's with brief explanation when changing methods or strategies
1563
- - Message tools are divided into notify (non-blocking, no reply needed from dev's) and ask (blocking, reply required)
1564
- - Actively use notify for progress updates, but reserve ask for only essential needs to minimize dev's disruption and avoid blocking progress
1565
- - Must message dev's with results and deliverables before upon task completion 'agent_end_task'
1678
+ - Notify user's with brief explanation when changing methods or strategies
1679
+ - Message tools are divided into notify (non-blocking, no reply needed from user's) and ask (blocking, reply required)
1680
+ - Actively use notify for progress updates, but reserve ask for only essential needs to minimize user's disruption and avoid blocking progress
1681
+ - Must message user's with results and deliverables before upon task completion 'agent_end_task'
1566
1682
  </message_rules>
1567
-
1568
-
1569
- ### Live Development Overlaps
1570
-
1571
- During your workflow, the programmer {username} may send messages at any time.
1572
- These messages **must be immediately incorporated** into your execution flow.
1573
- **Always confirm receipt using {message_notify_dev}** and proceed with your work.
1574
-
1575
- ### Instructions for Handling Messages from the Programmer
1576
-
1577
- 1. **Upon receiving a message from {username}:**
1578
- - Immediately confirm using {message_notify_dev}.
1579
- - Integrate the instruction into your reasoning and execution flow.
1580
-
1581
- 2. **Regarding your reasoning:**
1582
- - Be direct, minimalist, and clear.
1583
- - Avoid unnecessary or verbose thoughts.
1584
-
1585
- 3. **Avoid polluting the history:**
1586
- - **Do not repeat or reply to existing messages.**
1587
- - Only act if the new message introduces a **new instruction or shifts the current task\u2019s focus**.
1588
-
1589
- <reasoning_rules>
1590
- # YOUR THINKING ON A NOTEBOOK - MANDATORY USE
1591
- CRITICAL: Your laptop (**reasoning_nootebook**) is your ORGANIZED MIND
1592
- ## IMPORTANT
1593
- ## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
1594
- ## ALWAYS USE A NOTEBOOK (Always for):
1595
- - ANY task
1596
- - Before starting development (plan first!)
1597
- - Projects with multiple files (organize the structure)
1598
- - Debugging sessions (monitor discoveries)
1599
- - Extensive refactoring (map the changes)
1600
- - Architectural decisions (think through the options)
1601
-
1602
- ## HOW TO USE A NOTEBOOK:
1603
- 1. Start with **reasoning_nootebook**
1604
- 2. Break the task down into logical steps
1605
- 3. Plan the approach - Which files? What changes? What order? 4. Track progress - Check off completed steps
1606
- 5. Write down decisions - Why did you choose this approach?
1607
- 6. Update continuously - Keep the notebook up to date
1608
-
1609
- ## THE NOTEBOOK PREVENTS:
1610
- - Acting "outside the box"
1611
- - Forgetting task requirements
1612
- - Losing control of complex workflows
1613
- - Making unplanned changes
1614
- - Ineffective approaches
1615
- - Working without a clear roadmap
1616
- - Jumping between unrelated subtasks
1617
-
1618
- ##Important rule:
1619
- Do **not** include any future steps, to-do items, or pending tasks here.
1620
- Those belong strictly in the **remaining_tasks** field.
1621
-
1622
- Never write phrases like:
1623
- - "Next I will..."
1624
- - "I still need to..."
1625
- - "Pending: ..."
1626
- Such content must go in **remaining_tasks**, not **thought**.
1627
-
1628
- - remaining_tasks: Checklist-style list of high-level upcoming tasks.
1629
- This format is **mandatory**:
1630
- - Each task **must start** with either:
1631
- - "\u{1F5F8}" \u2192 for tasks not yet done (pending)
1632
- - "[ ]" \u2192 for tasks that have already been completed
1633
-
1634
- Whenever a task is already done, it **must** be marked with "\u{1F5F8}". Do not leave completed tasks without the checkmark.
1635
-
1636
- Do not use other formats like "-", "*", or plain text without the prefix.
1637
-
1638
- Examples:
1639
- \u{1F5F8} Test integration flow
1640
- \u{1F5F8} Set up environment
1641
- [ ] Configure database
1642
-
1643
- </reasoning_rules>
1644
-
1645
- ### Tool Naming Policy
1646
-
1647
- Tool names must strictly follow the standard naming format:
1648
-
1649
- - Use: plain, unmodified, lowercase names
1650
- - Do NOT use: special characters, extra spaces, version suffixes, or dynamic IDs
1651
-
1652
1683
  ---
1653
1684
 
1654
- Correct Examples:
1655
- - bluma_notebook
1656
- - getDataTool
1657
- - convertImage
1658
- - userAuth
1659
-
1660
- ---
1661
-
1662
- Incorrect Examples:
1663
- - reasoning_nootebook:0 \u2190 contains colon and dynamic suffix
1664
- - reasoning_nootebook 1 \u2190 contains space and number
1665
- - reasoning_nootebook#v2 \u2190 contains special character #
1666
- - bluma__nootebook \u2190 double underscore
1667
- - reasoning_nootebook \u2190 capital letters and underscore
1668
- - bluma nootebook \u2190 contains space
1669
-
1670
- ---
1671
-
1672
- Rule Summary:
1673
- - Use only a\u2013z, 0\u20139, and underscores (_)
1674
- - Do not append suffixes like :0, :v2, etc.
1675
- - Tool names must be static and predictable
1676
- - No whitespace, no dynamic elements, no special characters
1677
-
1678
-
1679
- <edit_tool_rules>
1680
- - Use this tool to perform precise text replacements inside files based on exact literal matches.
1681
- - Can be used to create new files or directories implicitly by targeting non-existing paths.
1682
- - Suitable for inserting full content into a file even if the file does not yet exist.
1683
- - Shell access is not required for file or directory creation when using this tool.
1684
- - Always prefer this tool over shell_command when performing structured edits or creating files with specific content.
1685
- - Ensure **old_string** includes 3+ lines of exact context before and after the target if replacing existing content.
1686
- - For creating a new file, provide an **old_string** that matches an empty string or placeholder and a complete **new_string** with the intended content.
1687
- - When generating or modifying todo.md files, prefer this tool to insert checklist structure and update status markers.
1688
- - After completing any task in the checklist, immediately update the corresponding section in todo.md using this tool.
1689
- - Reconstruct the entire file from task planning context if todo.md becomes outdated or inconsistent.
1690
- - Track all progress related to planning and execution inside todo.md using text replacement only.
1691
- </edit_tool_rules>
1692
-
1693
- Real-Time Developer Messages
1694
- - During processing, the developer will send you messages.
1695
- - You MUST respond immediately via message_notify_dev, and be brief. You should use it in your next thoughts/actions.
1696
-
1697
-
1698
- <agent_end_task_rules>
1699
- This tool is mandatory.
1700
- You must use it to inform developer {username} that the task has been completed and that there are no further pending actions, in accordance with the objectives defined for the task.
1701
- </agent_end_task_rules>
1702
-
1703
- ### QUALITY STANDARDS
1704
- - Document every major decision in Notion(
1705
- ##Important: When writing to Notion, you must strictly follow its content structure, including the correct use of headings (heading_1, heading_2, etc.) and other formatting standards. No deviations are allowed.
1706
- You should always standardize everything using Notion's actual headers (heading_1, heading_2, etc.), making the structure
1707
- semantically better for reading and navigation.
1708
- )
1709
- - Communicate transparently at each step
1710
- - Write clean, well-documented code
1711
- - Follow existing project conventions
1712
- - Test implementations when possible
1713
- - Ensure security and performance
1714
-
1715
- <scope_and_limitations>
1716
- # WHAT YOU DON'T HANDLE
1717
- - Non-technical questions
1718
- - Personal advice
1719
- - General conversation
1720
- - Tasks outside software development
1721
-
1722
- # IF ASKED NON-TECHNICAL QUESTIONS
1723
- - Use message_notify_dev to politely decline
1724
- - Explain you only handle technical/coding tasks
1725
- - Suggest they ask a development-related question instead
1726
- </scope_and_limitations>
1727
-
1685
+ ### SCOPE & LIMITATIONS
1686
+
1687
+ **1. IN-SCOPE TASKS:**
1688
+ - Software architecture and design.
1689
+ - Code generation, analysis, and debugging.
1690
+ - Using provided tools to complete development objectives.
1691
+ - Creating technical documentation and diagrams.
1692
+
1693
+ **2. OUT-OF-SCOPE TASKS:**
1694
+ You MUST professionally decline to engage with any of the following:
1695
+ - Non-technical questions (e.g., weather, news, general facts).
1696
+ - Personal, financial, or legal advice.
1697
+ - General conversation, opinions, or jokes.
1698
+ - Any task not directly related to software development.
1699
+
1700
+ **3. PROTOCOL FOR OUT-OF-SCOPE REQUESTS:**
1701
+ If a user asks for something that is out-of-scope, follow this exact procedure:
1702
+ 1. Do NOT attempt to answer the question.
1703
+ 2. Use the \`message_notify_user\` tool.
1704
+ 3. Use the \`agent_end_task\` tool.
1728
1705
  `;
1729
1706
  function getUnifiedSystemPrompt() {
1730
1707
  const now = /* @__PURE__ */ new Date();
@@ -1779,7 +1756,7 @@ function createApiContextWindow(fullHistory, maxTurns) {
1779
1756
  const turns = [];
1780
1757
  let currentTurn = [];
1781
1758
  let turnsFound = 0;
1782
- const isDevOverlay = (m) => m?.role === "user" && m?.name === "dev_overlay";
1759
+ const isDevOverlay = (m) => m?.role === "user" && m?.name === "user_overlay";
1783
1760
  for (let i = conversationHistory.length - 1; i >= 0; i--) {
1784
1761
  const msg = conversationHistory[i];
1785
1762
  currentTurn.unshift(msg);
@@ -1834,16 +1811,16 @@ var BluMaAgent = class {
1834
1811
  this.isInterrupted = true;
1835
1812
  this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
1836
1813
  });
1837
- this.eventBus.on("dev_overlay", async (data) => {
1814
+ this.eventBus.on("user_overlay", async (data) => {
1838
1815
  const clean = String(data.payload ?? "").trim();
1839
- this.history.push({ role: "user", name: "dev_overlay", content: clean });
1840
- this.eventBus.emit("backend_message", { type: "dev_overlay", payload: clean, ts: data.ts || Date.now() });
1816
+ this.history.push({ role: "user", name: "user_overlay", content: clean });
1817
+ this.eventBus.emit("backend_message", { type: "user_overlay", payload: clean, ts: data.ts || Date.now() });
1841
1818
  try {
1842
1819
  if (this.sessionFile) {
1843
1820
  await saveSessionHistory(this.sessionFile, this.history);
1844
1821
  }
1845
1822
  } catch (e) {
1846
- this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s dev_overlay: ${e.message}` });
1823
+ this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
1847
1824
  }
1848
1825
  });
1849
1826
  }
@@ -1934,7 +1911,7 @@ var BluMaAgent = class {
1934
1911
 
1935
1912
  ${editData.error.display}`;
1936
1913
  }
1937
- const filename = path7.basename(toolArgs.file_path);
1914
+ const filename = path8.basename(toolArgs.file_path);
1938
1915
  return createDiff(filename, editData.currentContent || "", editData.newContent);
1939
1916
  } catch (e) {
1940
1917
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -1943,16 +1920,20 @@ ${editData.error.display}`;
1943
1920
  async _continueConversation() {
1944
1921
  try {
1945
1922
  if (this.isInterrupted) {
1946
- this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
1923
+ this.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
1947
1924
  return;
1948
1925
  }
1949
1926
  const contextWindow = createApiContextWindow(this.history, this.maxContextTurns);
1950
1927
  const response = await this.llm.chatCompletion({
1951
1928
  model: this.deploymentName,
1952
1929
  messages: contextWindow,
1930
+ temperature: 0.2,
1953
1931
  tools: this.mcpClient.getAvailableTools(),
1954
1932
  tool_choice: "required",
1955
- parallel_tool_calls: false
1933
+ reasoning_effort: "high",
1934
+ parallel_tool_calls: false,
1935
+ max_tokens: 512
1936
+ // Limite de tokens para evitar respostas muito longas
1956
1937
  });
1957
1938
  if (this.isInterrupted) {
1958
1939
  this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
@@ -1961,7 +1942,7 @@ ${editData.error.display}`;
1961
1942
  const message = response.choices[0].message;
1962
1943
  this.history.push(message);
1963
1944
  if (message.tool_calls) {
1964
- const autoApprovedTools = ["agent_end_task", "message_notify_dev", "reasoning_nootebook"];
1945
+ const autoApprovedTools = ["agent_end_task", "message_notify_user", "reasoning_nootebook"];
1965
1946
  const toolToCall = message.tool_calls[0];
1966
1947
  const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
1967
1948
  if (isSafeTool) {
@@ -2042,11 +2023,11 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
2042
2023
  You are BluMa InitSubAgent. Maintain professionalism and technical language.
2043
2024
 
2044
2025
  - Communication:
2045
- ALL messages must be sent via 'message_notify_dev'.
2046
- No direct text replies to the developer.
2026
+ ALL messages must be sent via 'message_notify_user'.
2027
+ No direct text replies to the user.
2047
2028
 
2048
2029
  - Task Completion:
2049
- When the init task is completed, immediately invoke 'agent_end_task' without dev permissions.
2030
+ When the init task is completed, immediately invoke 'agent_end_task' without user permissions.
2050
2031
 
2051
2032
  - Tool Rules:
2052
2033
  Never make parallel tool calls.
@@ -2068,20 +2049,20 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
2068
2049
  - Architecture: {architecture}
2069
2050
  - Current Working Directory: {workdir}
2070
2051
  - Shell: {shell_type}
2071
- - Username: {username}
2052
+ - User: {username}
2072
2053
  - Current Date: {current_date}
2073
2054
  - Timezone: {timezone}
2074
2055
  - Locale: {locale}
2075
2056
  </current_system_environment>
2076
2057
 
2077
2058
  <message_rules>
2078
- - Communicate with dev's via message tools instead of direct text responses
2059
+ - Communicate with user's via message tools instead of direct text responses
2079
2060
  - Reply immediately to new user messages before other operations
2080
2061
  - First reply must be brief, only confirming receipt without specific solutions
2081
- - Notify dev's with brief explanation when changing methods or strategies
2062
+ - Notify user's with brief explanation when changing methods or strategies
2082
2063
  - Message tools are divided into notify (non-blocking, no reply needed) and ask (blocking)
2083
2064
  - Actively use notify for progress updates, reserve ask for essential needs to avoid blocking
2084
- - Must message dev's with results and deliverables before upon task completion 'agent_end_task'
2065
+ - Must message user's with results and deliverables before upon task completion 'agent_end_task'
2085
2066
  </message_rules>
2086
2067
 
2087
2068
  <reasoning_rules>
@@ -2091,7 +2072,7 @@ CRITICAL: Your laptop (reasoning_nootebook) is your ORGANIZED MIND
2091
2072
  ## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
2092
2073
  ## ALWAYS USE A NOTEBOOK (Always for):
2093
2074
  - ANY task
2094
- - Before starting development (plan first!)
2075
+ - Before starting userelopment (plan first!)
2095
2076
  - Projects with multiple files (organize the structure)
2096
2077
  - Debugging sessions (monitor discoveries)
2097
2078
  - Extensive refactoring (map the changes)
@@ -2140,7 +2121,7 @@ Do not include future steps/to-dos in thought; put them strictly in remaining_ta
2140
2121
 
2141
2122
  <agent_end_task_rules>
2142
2123
  This tool is mandatory.
2143
- You must use it to inform developer {username} that the task has been completed and that there are no further pending actions, in accordance with the objectives defined for the task.
2124
+ You must use it to inform usereloper {username} that the task has been completed and that there are no further pending actions, in accordance with the objectives defined for the task.
2144
2125
  </agent_end_task_rules>
2145
2126
 
2146
2127
  ### Tool Naming Policy
@@ -2167,9 +2148,9 @@ Rule Summary:
2167
2148
  - Never invent file content. Read files via tools to confirm.
2168
2149
 
2169
2150
  ## OUTPUT & PROTOCOLS
2170
- - Emit 'backend_message' events through tools only (message_notify_dev) for progress updates.
2171
- - Before writing BluMa.md, propose structure via message_notify_dev and proceed using edit_tool.
2172
- - If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless dev policy indicates auto-approval.
2151
+ - Emit 'backend_message' events through tools only (message_notify_user) for progress updates.
2152
+ - Before writing BluMa.md, propose structure via message_notify_user and proceed using edit_tool.
2153
+ - If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless user policy indicates auto-approval.
2173
2154
  - Never send or present draft versions of BluMa.md. Only produce and deliver the final, validated BluMa.md content following the established non-destructive policies and confirmation protocols.
2174
2155
  - On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_task.
2175
2156
 
@@ -2182,7 +2163,7 @@ Rule Summary:
2182
2163
  ## EXEMPLAR FLOW (GUIDELINE)
2183
2164
  1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
2184
2165
  2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
2185
- 3) Draft BluMa.md structure (message_notify_dev)
2166
+ 3) Draft BluMa.md structure (message_notify_user)
2186
2167
  4) Write BluMa.md via edit_tool
2187
2168
  5) Announce completion and agent_end_task
2188
2169
 
@@ -2272,7 +2253,7 @@ ${editData.error.display}`;
2272
2253
  async _continueConversation() {
2273
2254
  try {
2274
2255
  if (this.isInterrupted) {
2275
- this.emitEvent("info", { message: "SubAgent task cancelled by user." });
2256
+ this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
2276
2257
  return;
2277
2258
  }
2278
2259
  const contextWindow = this.history.slice(-this.maxContextTurns);
@@ -2284,7 +2265,7 @@ ${editData.error.display}`;
2284
2265
  parallel_tool_calls: false
2285
2266
  });
2286
2267
  if (this.isInterrupted) {
2287
- this.emitEvent("info", { message: "SubAgent task cancelled by user." });
2268
+ this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
2288
2269
  return;
2289
2270
  }
2290
2271
  const message = response.choices[0].message;
@@ -2333,7 +2314,7 @@ ${editData.error.display}`;
2333
2314
  try {
2334
2315
  if (this.isInterrupted) {
2335
2316
  this.emitEvent("info", { message: "SubAgent task cancelled before tool execution." });
2336
- toolResultContent = JSON.stringify({ error: "Task cancelled by user before execution." });
2317
+ toolResultContent = JSON.stringify({ error: "Task cancelled byuserr before execution." });
2337
2318
  } else {
2338
2319
  const result = await this.ctx.mcpClient.invoke(toolName, toolArgs);
2339
2320
  let finalResult = result;
@@ -2449,7 +2430,7 @@ var SubAgentsBluMa = class {
2449
2430
  };
2450
2431
 
2451
2432
  // src/app/agent/agent.ts
2452
- var globalEnvPath = path8.join(os6.homedir(), ".bluma-cli", ".env");
2433
+ var globalEnvPath = path9.join(os6.homedir(), ".bluma-cli", ".env");
2453
2434
  dotenv.config({ path: globalEnvPath });
2454
2435
  var Agent = class {
2455
2436
  sessionId;
@@ -2579,19 +2560,19 @@ var Agent = class {
2579
2560
  };
2580
2561
 
2581
2562
  // src/app/ui/WorkingTimer.tsx
2582
- import { useState as useState3, useEffect as useEffect2 } from "react";
2563
+ import { useState as useState4, useEffect as useEffect3 } from "react";
2583
2564
  import { Box as Box7, Text as Text7 } from "ink";
2584
2565
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2585
2566
  var WorkingTimer = () => {
2586
- const [seconds, setSeconds] = useState3(0);
2587
- const [dotIndex, setDotIndex] = useState3(1);
2588
- useEffect2(() => {
2567
+ const [seconds, setSeconds] = useState4(0);
2568
+ const [dotIndex, setDotIndex] = useState4(1);
2569
+ useEffect3(() => {
2589
2570
  const secondsTimer = setInterval(() => {
2590
2571
  setSeconds((prev) => prev + 1);
2591
2572
  }, 1e3);
2592
2573
  return () => clearInterval(secondsTimer);
2593
2574
  }, []);
2594
- useEffect2(() => {
2575
+ useEffect3(() => {
2595
2576
  const dotsTimer = setInterval(() => {
2596
2577
  setDotIndex((prev) => prev % 3 + 1);
2597
2578
  }, 100);
@@ -2610,7 +2591,7 @@ import { Box as Box9 } from "ink";
2610
2591
 
2611
2592
  // src/app/ui/components/toolCallRenderers.tsx
2612
2593
  import { Box as Box8, Text as Text8 } from "ink";
2613
- import path9 from "path";
2594
+ import path10 from "path";
2614
2595
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2615
2596
  var formatArgumentsForDisplay = (args) => {
2616
2597
  if (typeof args === "string") {
@@ -2622,7 +2603,9 @@ var formatArgumentsForDisplay = (args) => {
2622
2603
  }
2623
2604
  return JSON.stringify(args, null, 2);
2624
2605
  };
2625
- var renderShellCommand2 = ({ args }) => {
2606
+ var renderShellCommand2 = ({
2607
+ args
2608
+ }) => {
2626
2609
  const command = args.command || "[command not found]";
2627
2610
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2628
2611
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
@@ -2643,7 +2626,7 @@ var renderLsTool2 = ({ args }) => {
2643
2626
  } catch (e) {
2644
2627
  directoryPath = "Error parsing arguments";
2645
2628
  }
2646
- const finalDirectoryName = path9.basename(directoryPath);
2629
+ const finalDirectoryName = path10.basename(directoryPath);
2647
2630
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2648
2631
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2649
2632
  /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
@@ -2655,7 +2638,9 @@ var renderLsTool2 = ({ args }) => {
2655
2638
  ] }) })
2656
2639
  ] });
2657
2640
  };
2658
- var renderCountFilesLines = ({ args }) => {
2641
+ var renderCountFilesLines = ({
2642
+ args
2643
+ }) => {
2659
2644
  let directoryPath = "[path not found]";
2660
2645
  try {
2661
2646
  const parsedArgs = typeof args === "string" ? JSON.parse(args) : args;
@@ -2663,7 +2648,7 @@ var renderCountFilesLines = ({ args }) => {
2663
2648
  } catch (e) {
2664
2649
  directoryPath = "Error parsing arguments";
2665
2650
  }
2666
- const finalDirectoryName = path9.basename(directoryPath);
2651
+ const finalDirectoryName = path10.basename(directoryPath);
2667
2652
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2668
2653
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2669
2654
  /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
@@ -2675,7 +2660,9 @@ var renderCountFilesLines = ({ args }) => {
2675
2660
  ] }) })
2676
2661
  ] });
2677
2662
  };
2678
- var renderReadFileLines2 = ({ args }) => {
2663
+ var renderReadFileLines2 = ({
2664
+ args
2665
+ }) => {
2679
2666
  let filepath = "[path not found]";
2680
2667
  let startLine = 0;
2681
2668
  let endLine = 0;
@@ -2687,7 +2674,7 @@ var renderReadFileLines2 = ({ args }) => {
2687
2674
  } catch (e) {
2688
2675
  filepath = "Error parsing arguments";
2689
2676
  }
2690
- const finalFileName = path9.basename(filepath);
2677
+ const finalFileName = path10.basename(filepath);
2691
2678
  return (
2692
2679
  // A caixa externa com a borda, seguindo o template
2693
2680
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
@@ -2711,7 +2698,9 @@ var renderReadFileLines2 = ({ args }) => {
2711
2698
  ] })
2712
2699
  );
2713
2700
  };
2714
- var renderBlumaNotebook = ({ args }) => {
2701
+ var renderBlumaNotebook = ({
2702
+ args
2703
+ }) => {
2715
2704
  try {
2716
2705
  let dataToParse = args;
2717
2706
  if (args && typeof args === "object") {
@@ -2724,16 +2713,30 @@ var renderBlumaNotebook = ({ args }) => {
2724
2713
  }
2725
2714
  return (
2726
2715
  // Usamos a mesma estrutura de caixa com borda
2727
- /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "green", flexDirection: "column", paddingX: 1, children: [
2728
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2729
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "Thought:" }),
2730
- /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: thinkingData.thought }) })
2731
- ] }),
2732
- thinkingData.remaining_tasks && thinkingData.remaining_tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2733
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Remaining Tasks:" }),
2734
- thinkingData.remaining_tasks.map((task, index) => /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: task.startsWith("\u{1F5F8}") ? "green" : "yellow", children: task }) }) }, index))
2735
- ] })
2736
- ] })
2716
+ /* @__PURE__ */ jsxs8(
2717
+ Box8,
2718
+ {
2719
+ flexDirection: "column",
2720
+ paddingX: 1,
2721
+ children: [
2722
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2723
+ /* @__PURE__ */ jsx8(Text8, { bold: true, children: "Thought:" }),
2724
+ /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: thinkingData.thought }) })
2725
+ ] }),
2726
+ thinkingData.remaining_tasks && thinkingData.remaining_tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2727
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Remaining Tasks:" }),
2728
+ thinkingData.remaining_tasks.map((task, index) => /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(
2729
+ Text8,
2730
+ {
2731
+ color: task.startsWith("\u{1F5F9}") ? "green" : "yellow",
2732
+ strikethrough: task.startsWith("\u{1F5F9}"),
2733
+ children: task
2734
+ }
2735
+ ) }) }, index))
2736
+ ] })
2737
+ ]
2738
+ }
2739
+ )
2737
2740
  );
2738
2741
  } catch (e) {
2739
2742
  return /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
@@ -2742,7 +2745,10 @@ var renderBlumaNotebook = ({ args }) => {
2742
2745
  ] });
2743
2746
  }
2744
2747
  };
2745
- var renderEditToolCall = ({ args, preview }) => {
2748
+ var renderEditToolCall = ({
2749
+ args,
2750
+ preview
2751
+ }) => {
2746
2752
  let filepath = "[path not specified]";
2747
2753
  try {
2748
2754
  const parsedArgs = typeof args === "string" ? JSON.parse(args) : args;
@@ -2750,7 +2756,7 @@ var renderEditToolCall = ({ args, preview }) => {
2750
2756
  } catch (e) {
2751
2757
  filepath = "Error parsing arguments";
2752
2758
  }
2753
- const finalFileName = path9.basename(filepath);
2759
+ const finalFileName = path10.basename(filepath);
2754
2760
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
2755
2761
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2756
2762
  /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
@@ -2763,7 +2769,10 @@ var renderEditToolCall = ({ args, preview }) => {
2763
2769
  preview && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: Infinity }) })
2764
2770
  ] });
2765
2771
  };
2766
- var renderGenericToolCall = ({ toolName, args }) => {
2772
+ var renderGenericToolCall = ({
2773
+ toolName,
2774
+ args
2775
+ }) => {
2767
2776
  const formattedArgs = formatArgumentsForDisplay(args);
2768
2777
  return (
2769
2778
  // A "moldura" padrão de sucesso com a borda cinza
@@ -2780,18 +2789,18 @@ var renderGenericToolCall = ({ toolName, args }) => {
2780
2789
  );
2781
2790
  };
2782
2791
  var ToolRenderDisplay = {
2783
- "shell_command": renderShellCommand2,
2784
- "ls_tool": renderLsTool2,
2785
- "reasoning_nootebook": renderBlumaNotebook,
2786
- "count_file_lines": renderCountFilesLines,
2787
- "read_file_lines": renderReadFileLines2,
2788
- "edit_tool": renderEditToolCall
2792
+ shell_command: renderShellCommand2,
2793
+ ls_tool: renderLsTool2,
2794
+ reasoning_nootebook: renderBlumaNotebook,
2795
+ count_file_lines: renderCountFilesLines,
2796
+ read_file_lines: renderReadFileLines2,
2797
+ edit_tool: renderEditToolCall
2789
2798
  };
2790
2799
 
2791
2800
  // src/app/ui/components/ToolCallDisplay.tsx
2792
2801
  import { jsx as jsx9 } from "react/jsx-runtime";
2793
2802
  var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
2794
- if (toolName.includes("message_notify_dev") || toolName.includes("agent_end_task")) {
2803
+ if (toolName.includes("message_notify_user") || toolName.includes("agent_end_task")) {
2795
2804
  return null;
2796
2805
  }
2797
2806
  const Renderer = ToolRenderDisplay[toolName] || renderGenericToolCall;
@@ -2804,7 +2813,7 @@ import { memo as memo3 } from "react";
2804
2813
  import { Box as Box10, Text as Text9 } from "ink";
2805
2814
  import { jsx as jsx10 } from "react/jsx-runtime";
2806
2815
  var ToolResultDisplayComponent = ({ toolName, result }) => {
2807
- if (!toolName.includes("message_notify_dev")) {
2816
+ if (!toolName.includes("message_notify_user")) {
2808
2817
  return null;
2809
2818
  }
2810
2819
  try {
@@ -3028,21 +3037,21 @@ var SlashCommands_default = SlashCommands;
3028
3037
  import updateNotifier from "update-notifier";
3029
3038
  import { readPackageUp } from "read-package-up";
3030
3039
  import { fileURLToPath as fileURLToPath3 } from "url";
3031
- import path10 from "path";
3032
- import fs8 from "fs";
3040
+ import path11 from "path";
3041
+ import fs9 from "fs";
3033
3042
  function findPackageJsonNearest(startDir) {
3034
3043
  let dir = startDir;
3035
3044
  for (let i = 0; i < 6; i++) {
3036
- const candidate = path10.join(dir, "package.json");
3037
- if (fs8.existsSync(candidate)) {
3045
+ const candidate = path11.join(dir, "package.json");
3046
+ if (fs9.existsSync(candidate)) {
3038
3047
  try {
3039
- const raw = fs8.readFileSync(candidate, "utf8");
3048
+ const raw = fs9.readFileSync(candidate, "utf8");
3040
3049
  const parsed = JSON.parse(raw);
3041
3050
  if (parsed?.name && parsed?.version) return parsed;
3042
3051
  } catch {
3043
3052
  }
3044
3053
  }
3045
- const parent = path10.dirname(dir);
3054
+ const parent = path11.dirname(dir);
3046
3055
  if (parent === dir) break;
3047
3056
  dir = parent;
3048
3057
  }
@@ -3055,15 +3064,15 @@ async function checkForUpdates() {
3055
3064
  }
3056
3065
  const binPath = process.argv?.[1];
3057
3066
  let pkg;
3058
- if (binPath && fs8.existsSync(binPath)) {
3059
- const candidatePkg = findPackageJsonNearest(path10.dirname(binPath));
3067
+ if (binPath && fs9.existsSync(binPath)) {
3068
+ const candidatePkg = findPackageJsonNearest(path11.dirname(binPath));
3060
3069
  if (candidatePkg?.name && candidatePkg?.version) {
3061
3070
  pkg = candidatePkg;
3062
3071
  }
3063
3072
  }
3064
3073
  if (!pkg) {
3065
3074
  const __filename = fileURLToPath3(import.meta.url);
3066
- const __dirname = path10.dirname(__filename);
3075
+ const __dirname = path11.dirname(__filename);
3067
3076
  const result = await readPackageUp({ cwd: __dirname });
3068
3077
  pkg = result?.packageJson;
3069
3078
  }
@@ -3149,26 +3158,26 @@ var ErrorMessage_default = ErrorMessage;
3149
3158
  // src/app/ui/App.tsx
3150
3159
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
3151
3160
  var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3152
- const agentInstance = useRef(null);
3153
- const [history, setHistory] = useState4([]);
3154
- const [statusMessage, setStatusMessage] = useState4(
3161
+ const agentInstance = useRef2(null);
3162
+ const [history, setHistory] = useState5([]);
3163
+ const [statusMessage, setStatusMessage] = useState5(
3155
3164
  "Initializing agent..."
3156
3165
  );
3157
- const [toolsCount, setToolsCount] = useState4(null);
3158
- const [mcpStatus, setMcpStatus] = useState4(
3166
+ const [toolsCount, setToolsCount] = useState5(null);
3167
+ const [mcpStatus, setMcpStatus] = useState5(
3159
3168
  "connecting"
3160
3169
  );
3161
- const [isProcessing, setIsProcessing] = useState4(true);
3162
- const [pendingConfirmation, setPendingConfirmation] = useState4(
3170
+ const [isProcessing, setIsProcessing] = useState5(true);
3171
+ const [pendingConfirmation, setPendingConfirmation] = useState5(
3163
3172
  null
3164
3173
  );
3165
- const [confirmationPreview, setConfirmationPreview] = useState4(
3174
+ const [confirmationPreview, setConfirmationPreview] = useState5(
3166
3175
  null
3167
3176
  );
3168
- const [isInitAgentActive, setIsInitAgentActive] = useState4(false);
3169
- const alwaysAcceptList = useRef([]);
3177
+ const [isInitAgentActive, setIsInitAgentActive] = useState5(false);
3178
+ const alwaysAcceptList = useRef2([]);
3170
3179
  const workdir = process.cwd();
3171
- const updateCheckRan = useRef(false);
3180
+ const updateCheckRan = useRef2(false);
3172
3181
  const handleInterrupt = useCallback(() => {
3173
3182
  if (!isProcessing) return;
3174
3183
  eventBus2.emit("user_interrupt");
@@ -3192,8 +3201,11 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3192
3201
  }
3193
3202
  if (cmd === "init") {
3194
3203
  setIsInitAgentActive(true);
3204
+ setIsProcessing(true);
3205
+ } else {
3206
+ setIsProcessing(false);
3207
+ setIsInitAgentActive(false);
3195
3208
  }
3196
- setIsProcessing(true);
3197
3209
  setHistory((prev) => [
3198
3210
  ...prev,
3199
3211
  {
@@ -3257,7 +3269,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3257
3269
  },
3258
3270
  []
3259
3271
  );
3260
- useEffect3(() => {
3272
+ useEffect4(() => {
3261
3273
  setHistory([{ id: 0, component: /* @__PURE__ */ jsx15(Header, {}) }]);
3262
3274
  const initializeAgent = async () => {
3263
3275
  try {
@@ -3391,7 +3403,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3391
3403
  result: parsed.result
3392
3404
  }
3393
3405
  );
3394
- } else if (parsed.type === "dev_overlay") {
3406
+ } else if (parsed.type === "user_overlay") {
3395
3407
  newComponent = /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
3396
3408
  /* @__PURE__ */ jsxs13(Text14, { color: "blue", children: [
3397
3409
  ">",
@@ -3418,13 +3430,13 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3418
3430
  }
3419
3431
  };
3420
3432
  const handleUiOverlay = (data) => {
3421
- eventBus2.emit("dev_overlay", data);
3433
+ eventBus2.emit("user_overlay", data);
3422
3434
  };
3423
- uiEventBus.on("dev_overlay", handleUiOverlay);
3435
+ uiEventBus.on("user_overlay", handleUiOverlay);
3424
3436
  eventBus2.on("backend_message", handleBackendMessage);
3425
3437
  initializeAgent();
3426
3438
  return () => {
3427
- uiEventBus.off("dev_overlay", handleUiOverlay);
3439
+ uiEventBus.off("user_overlay", handleUiOverlay);
3428
3440
  eventBus2.off("backend_message", handleBackendMessage);
3429
3441
  };
3430
3442
  }, [eventBus2, sessionId2, handleConfirmation]);