@nomad-e/bluma-cli 0.0.31 → 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,8 +6,8 @@ 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";
10
- import { Box as Box14, Text as Text13, Static } from "ink";
9
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback, memo as memo4 } from "react";
10
+ import { Box as Box15, Text as Text14, Static } from "ink";
11
11
 
12
12
  // src/app/ui/layout.tsx
13
13
  import { Box, Text } from "ink";
@@ -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,14 +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
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
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
- useEffect(() => {
418
+ const [viewWidth, setViewWidth] = useState2(() => stdout.columns - 6);
419
+ const [slashOpen, setSlashOpen] = useState2(false);
420
+ const [slashIndex, setSlashIndex] = useState2(0);
421
+ useEffect2(() => {
253
422
  const onResize = () => setViewWidth(stdout.columns - 6);
254
423
  stdout.on("resize", onResize);
255
424
  return () => {
@@ -261,43 +430,40 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
261
430
  if (isReadOnly) {
262
431
  if (trimmed.length > 0) {
263
432
  const payload = trimmed;
264
- uiEventBus.emit("dev_overlay", { kind: "message", payload, ts: Date.now() });
433
+ uiEventBus.emit("user_overlay", { kind: "message", payload, ts: Date.now() });
265
434
  return;
266
435
  }
267
436
  return;
268
437
  }
269
438
  onSubmit(value);
270
439
  };
271
- const effectiveReadOnly = disableWhileProcessing ? false : isReadOnly;
272
- const { text, cursorPosition, viewStart } = useCustomInput({
440
+ const effectiveReadOnly = isReadOnly;
441
+ const { text, cursorPosition, viewStart, setText } = useCustomInput({
442
+ // Sobrepõe a lógica padrão: nunca submete se autocomplete aberto
273
443
  onSubmit: (value) => {
274
444
  if (disableWhileProcessing && isReadOnly) return;
445
+ if (pathAutocomplete.open) return;
275
446
  permissiveOnSubmit(value);
276
447
  },
277
448
  viewWidth,
278
449
  isReadOnly: effectiveReadOnly,
279
450
  onInterrupt
280
451
  });
281
- const [slashOpen, setSlashOpen] = useState(false);
282
- const [slashIndex, setSlashIndex] = useState(0);
283
452
  const visibleText = text.slice(viewStart, viewStart + viewWidth);
284
453
  const visibleCursorPosition = cursorPosition - viewStart;
285
454
  const textBeforeCursor = visibleText.slice(0, visibleCursorPosition);
286
- const charAtCursor = visibleText.slice(
287
- visibleCursorPosition,
288
- visibleCursorPosition + 1
289
- );
455
+ const charAtCursor = visibleText.slice(visibleCursorPosition, visibleCursorPosition + 1);
290
456
  const textAfterCursor = visibleText.slice(visibleCursorPosition + 1);
291
457
  const cursorGlyph = charAtCursor && charAtCursor.length > 0 ? charAtCursor : " ";
292
458
  const borderColor = isReadOnly ? "gray" : "gray";
293
- const placeholder = isReadOnly ? " press esc to cancel | type a message while agent processes" : "";
459
+ const placeholder = isReadOnly ? " Press Esc to cancel | Enter message while agent runs" : "";
294
460
  const showPlaceholder = text.length === 0 && isReadOnly;
295
461
  const slashQuery = useMemo(() => text.startsWith("/") ? text : "", [text]);
296
462
  const slashSuggestions = useMemo(() => {
297
463
  if (!slashQuery) return [];
298
464
  return filterSlashCommands(slashQuery);
299
465
  }, [slashQuery]);
300
- useEffect(() => {
466
+ useEffect2(() => {
301
467
  if (isReadOnly) {
302
468
  setSlashOpen(false);
303
469
  return;
@@ -320,36 +486,102 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
320
486
  if (choice) {
321
487
  const cmd = choice.name;
322
488
  setSlashOpen(false);
323
- permissiveOnSubmit(cmd);
489
+ try {
490
+ setText(`${cmd} `, true);
491
+ } catch (e) {
492
+ permissiveOnSubmit(`${cmd} `);
493
+ }
324
494
  }
325
495
  } else if (key.escape) {
326
496
  setSlashOpen(false);
327
497
  }
328
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
+ }
329
534
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
330
- /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
331
- /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
332
- ">",
333
- " "
334
- ] }),
335
- /* @__PURE__ */ jsx2(Text2, { children: textBeforeCursor }),
336
- /* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorGlyph }),
337
- showPlaceholder ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(Text2, { children: textAfterCursor })
338
- ] }) }),
339
- slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
340
- const isSelected = idx === slashIndex;
341
- return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
342
- /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "blue" : "gray", children: isSelected ? "\u276F " : " " }),
343
- /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "blue" : "white", bold: isSelected, dimColor: !isSelected, children: [
344
- s.name,
345
- " ",
346
- /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
347
- "- ",
348
- s.description
535
+ disableWhileProcessing ? (
536
+ // Modo bloqueado visualmente, mantendo hooks estáveis
537
+ /* @__PURE__ */ jsx2(Fragment, { children: /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", borderDimColor: true, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
538
+ /* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
539
+ ">",
540
+ " "
541
+ ] }),
542
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "ctrl+c to exit" })
543
+ ] }) }) })
544
+ ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
545
+ /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
546
+ /* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
547
+ ">",
548
+ " "
549
+ ] }),
550
+ /* @__PURE__ */ jsx2(Text2, { children: textBeforeCursor }),
551
+ /* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorGlyph }),
552
+ showPlaceholder ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(Text2, { children: textAfterCursor })
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
+ })(),
570
+ slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
571
+ const isSelected = idx === slashIndex;
572
+ return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
573
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "blue" : "gray", children: isSelected ? "\u276F " : " " }),
574
+ /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "blue" : "white", bold: isSelected, dimColor: !isSelected, children: [
575
+ s.name,
576
+ " ",
577
+ /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
578
+ "- ",
579
+ s.description
580
+ ] })
349
581
  ] })
350
- ] })
351
- ] }, s.name);
352
- }) }),
582
+ ] }, s.name);
583
+ }) })
584
+ ] }),
353
585
  /* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", dimColor: true, children: [
354
586
  "ctrl+c to exit | /help to explore commands | esc to interrupt | ",
355
587
  isReadOnly ? "Read-only mode (message passthrough)" : "Editable mode"
@@ -361,7 +593,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
361
593
  import { Box as Box6, Text as Text6 } from "ink";
362
594
 
363
595
  // src/app/ui/InteractiveMenu.tsx
364
- import { useState as useState2, memo } from "react";
596
+ import { useState as useState3, memo } from "react";
365
597
  import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
366
598
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
367
599
  var InteractiveMenuComponent = ({ onDecision }) => {
@@ -370,7 +602,7 @@ var InteractiveMenuComponent = ({ onDecision }) => {
370
602
  { label: "2. No, cancel this command", value: "decline" },
371
603
  { label: "3. Always allow this type of command", value: "accept_always" }
372
604
  ];
373
- const [selectedOption, setSelectedOption] = useState2(0);
605
+ const [selectedOption, setSelectedOption] = useState3(0);
374
606
  useInput3((input, key) => {
375
607
  if (key.upArrow) {
376
608
  setSelectedOption((prev) => prev > 0 ? prev - 1 : options.length - 1);
@@ -411,7 +643,7 @@ var InteractiveMenu = memo(InteractiveMenuComponent);
411
643
 
412
644
  // src/app/ui/components/promptRenderers.tsx
413
645
  import { Box as Box5, Text as Text5 } from "ink";
414
- import path from "path";
646
+ import path2 from "path";
415
647
 
416
648
  // src/app/ui/components/SimpleDiff.tsx
417
649
  import { Box as Box4, Text as Text4 } from "ink";
@@ -450,7 +682,7 @@ var SimpleDiff = ({ text, maxHeight }) => {
450
682
  // src/app/ui/components/promptRenderers.tsx
451
683
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
452
684
  var getBasePath = (filePath) => {
453
- return path.basename(filePath);
685
+ return path2.basename(filePath);
454
686
  };
455
687
  var renderShellCommand = ({
456
688
  toolCall
@@ -618,12 +850,12 @@ var ConfirmationPrompt = ({ toolCalls, preview, onDecision }) => {
618
850
  // src/app/agent/agent.ts
619
851
  import OpenAI from "openai";
620
852
  import * as dotenv from "dotenv";
621
- import path8 from "path";
853
+ import path9 from "path";
622
854
  import os6 from "os";
623
855
 
624
856
  // src/app/agent/tool_invoker.ts
625
- import { promises as fs5 } from "fs";
626
- import path4 from "path";
857
+ import { promises as fs6 } from "fs";
858
+ import path5 from "path";
627
859
  import { fileURLToPath } from "url";
628
860
 
629
861
  // src/app/agent/tools/natives/shell_command.ts
@@ -691,8 +923,8 @@ ${stderr.trim()}`.trim();
691
923
  }
692
924
 
693
925
  // src/app/agent/tools/natives/edit.ts
694
- import path2 from "path";
695
- import { promises as fs } from "fs";
926
+ import path3 from "path";
927
+ import { promises as fs2 } from "fs";
696
928
  import { diffLines } from "diff";
697
929
  function unescapeLlmString(inputString) {
698
930
  return inputString.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
@@ -728,7 +960,7 @@ async function calculateEdit(filePath, oldString, newString, expectedReplacement
728
960
  let finalOldString = oldString.replace(/\r\n/g, "\n");
729
961
  let occurrences = 0;
730
962
  try {
731
- currentContent = await fs.readFile(filePath, "utf-8");
963
+ currentContent = await fs2.readFile(filePath, "utf-8");
732
964
  currentContent = currentContent.replace(/\r\n/g, "\n");
733
965
  } catch (e) {
734
966
  if (e.code !== "ENOENT") {
@@ -784,7 +1016,7 @@ function createDiff(filename, oldContent, newContent) {
784
1016
  }
785
1017
  async function editTool(args) {
786
1018
  const { file_path, old_string, new_string, expected_replacements = 1 } = args;
787
- if (!path2.isAbsolute(file_path)) {
1019
+ if (!path3.isAbsolute(file_path)) {
788
1020
  return { success: false, error: `Invalid parameters: file_path must be absolute.`, file_path };
789
1021
  }
790
1022
  if (file_path.includes("..")) {
@@ -800,10 +1032,10 @@ async function editTool(args) {
800
1032
  file_path
801
1033
  };
802
1034
  }
803
- await fs.mkdir(path2.dirname(file_path), { recursive: true });
804
- await fs.writeFile(file_path, editData.newContent, "utf-8");
805
- const relativePath = path2.relative(process.cwd(), file_path);
806
- 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);
807
1039
  if (editData.isNewFile) {
808
1040
  return {
809
1041
  success: true,
@@ -838,10 +1070,10 @@ ${finalDiff}`,
838
1070
 
839
1071
  // src/app/agent/tools/natives/message.ts
840
1072
  import { v4 as uuidv4 } from "uuid";
841
- function messageNotifyDev(args) {
1073
+ function messageNotifyuser(args) {
842
1074
  const { text_markdown } = args;
843
1075
  const notification = {
844
- type: "message_notify_dev",
1076
+ type: "message_notify_user",
845
1077
  id: `notify_${uuidv4()}`,
846
1078
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
847
1079
  content: {
@@ -855,8 +1087,8 @@ function messageNotifyDev(args) {
855
1087
  }
856
1088
 
857
1089
  // src/app/agent/tools/natives/ls.ts
858
- import { promises as fs2 } from "fs";
859
- import path3 from "path";
1090
+ import { promises as fs3 } from "fs";
1091
+ import path4 from "path";
860
1092
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
861
1093
  ".git",
862
1094
  ".gitignore",
@@ -884,8 +1116,8 @@ async function ls(args) {
884
1116
  max_depth
885
1117
  } = args;
886
1118
  try {
887
- const basePath = path3.resolve(directory_path);
888
- if (!(await fs2.stat(basePath)).isDirectory()) {
1119
+ const basePath = path4.resolve(directory_path);
1120
+ if (!(await fs3.stat(basePath)).isDirectory()) {
889
1121
  throw new Error(`Directory '${directory_path}' not found.`);
890
1122
  }
891
1123
  const allIgnorePatterns = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore_patterns]);
@@ -894,11 +1126,11 @@ async function ls(args) {
894
1126
  const allDirs = [];
895
1127
  const walk = async (currentDir, currentDepth) => {
896
1128
  if (max_depth !== void 0 && currentDepth > max_depth) return;
897
- const entries = await fs2.readdir(currentDir, { withFileTypes: true });
1129
+ const entries = await fs3.readdir(currentDir, { withFileTypes: true });
898
1130
  for (const entry of entries) {
899
1131
  const entryName = entry.name;
900
- const fullPath = path3.join(currentDir, entryName);
901
- const posixPath = fullPath.split(path3.sep).join("/");
1132
+ const fullPath = path4.join(currentDir, entryName);
1133
+ const posixPath = fullPath.split(path4.sep).join("/");
902
1134
  const isHidden = entryName.startsWith(".");
903
1135
  if (allIgnorePatterns.has(entryName) || isHidden && !show_hidden) {
904
1136
  continue;
@@ -909,7 +1141,7 @@ async function ls(args) {
909
1141
  await walk(fullPath, currentDepth + 1);
910
1142
  }
911
1143
  } else if (entry.isFile()) {
912
- if (!normalizedExtensions || normalizedExtensions.includes(path3.extname(entryName).toLowerCase())) {
1144
+ if (!normalizedExtensions || normalizedExtensions.includes(path4.extname(entryName).toLowerCase())) {
913
1145
  allFiles.push(posixPath);
914
1146
  }
915
1147
  }
@@ -920,7 +1152,7 @@ async function ls(args) {
920
1152
  allDirs.sort();
921
1153
  return {
922
1154
  success: true,
923
- path: basePath.split(path3.sep).join("/"),
1155
+ path: basePath.split(path4.sep).join("/"),
924
1156
  recursive,
925
1157
  total_files: allFiles.length,
926
1158
  total_directories: allDirs.length,
@@ -936,17 +1168,17 @@ async function ls(args) {
936
1168
  }
937
1169
 
938
1170
  // src/app/agent/tools/natives/readLines.ts
939
- import { promises as fs3 } from "fs";
1171
+ import { promises as fs4 } from "fs";
940
1172
  async function readLines(args) {
941
1173
  const { filepath, start_line, end_line } = args;
942
1174
  try {
943
- if (!(await fs3.stat(filepath)).isFile()) {
1175
+ if (!(await fs4.stat(filepath)).isFile()) {
944
1176
  throw new Error(`File '${filepath}' not found or is not a file.`);
945
1177
  }
946
1178
  if (start_line < 1 || end_line < start_line) {
947
1179
  throw new Error("Invalid line range. start_line must be >= 1 and end_line must be >= start_line.");
948
1180
  }
949
- const fileContent = await fs3.readFile(filepath, "utf-8");
1181
+ const fileContent = await fs4.readFile(filepath, "utf-8");
950
1182
  const lines = fileContent.split("\n");
951
1183
  const total_lines = lines.length;
952
1184
  const startIndex = start_line - 1;
@@ -974,12 +1206,12 @@ async function readLines(args) {
974
1206
 
975
1207
  // src/app/agent/tools/natives/count_lines.ts
976
1208
  import { createReadStream } from "fs";
977
- import { promises as fs4 } from "fs";
1209
+ import { promises as fs5 } from "fs";
978
1210
  import readline from "readline";
979
1211
  async function countLines(args) {
980
1212
  const { filepath } = args;
981
1213
  try {
982
- if (!(await fs4.stat(filepath)).isFile()) {
1214
+ if (!(await fs5.stat(filepath)).isFile()) {
983
1215
  throw new Error(`File '${filepath}' not found or is not a file.`);
984
1216
  }
985
1217
  const fileStream = createReadStream(filepath);
@@ -1014,9 +1246,9 @@ var ToolInvoker = class {
1014
1246
  async initialize() {
1015
1247
  try {
1016
1248
  const __filename = fileURLToPath(import.meta.url);
1017
- const __dirname = path4.dirname(__filename);
1018
- const configPath = path4.resolve(__dirname, "config", "native_tools.json");
1019
- 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");
1020
1252
  const config2 = JSON.parse(fileContent);
1021
1253
  this.toolDefinitions = config2.nativeTools;
1022
1254
  } catch (error) {
@@ -1031,7 +1263,7 @@ var ToolInvoker = class {
1031
1263
  registerTools() {
1032
1264
  this.toolImplementations.set("shell_command", shellCommand);
1033
1265
  this.toolImplementations.set("edit_tool", editTool);
1034
- this.toolImplementations.set("message_notify_dev", messageNotifyDev);
1266
+ this.toolImplementations.set("message_notify_user", messageNotifyuser);
1035
1267
  this.toolImplementations.set("ls_tool", ls);
1036
1268
  this.toolImplementations.set("count_file_lines", countLines);
1037
1269
  this.toolImplementations.set("read_file_lines", readLines);
@@ -1065,8 +1297,8 @@ var ToolInvoker = class {
1065
1297
  };
1066
1298
 
1067
1299
  // src/app/agent/tools/mcp/mcp_client.ts
1068
- import { promises as fs6 } from "fs";
1069
- import path5 from "path";
1300
+ import { promises as fs7 } from "fs";
1301
+ import path6 from "path";
1070
1302
  import os2 from "os";
1071
1303
  import { fileURLToPath as fileURLToPath2 } from "url";
1072
1304
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -1094,9 +1326,9 @@ var MCPClient = class {
1094
1326
  });
1095
1327
  }
1096
1328
  const __filename = fileURLToPath2(import.meta.url);
1097
- const __dirname = path5.dirname(__filename);
1098
- const defaultConfigPath = path5.resolve(__dirname, "config", "bluma-mcp.json");
1099
- 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");
1100
1332
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
1101
1333
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
1102
1334
  const mergedConfig = {
@@ -1130,7 +1362,7 @@ var MCPClient = class {
1130
1362
  }
1131
1363
  async loadMcpConfig(configPath, configType) {
1132
1364
  try {
1133
- const fileContent = await fs6.readFile(configPath, "utf-8");
1365
+ const fileContent = await fs7.readFile(configPath, "utf-8");
1134
1366
  const processedContent = this.replaceEnvPlaceholders(fileContent);
1135
1367
  return JSON.parse(processedContent);
1136
1368
  } catch (error) {
@@ -1189,14 +1421,14 @@ var MCPClient = class {
1189
1421
  async invoke(toolName, args) {
1190
1422
  const route = this.toolToServerMap.get(toolName);
1191
1423
  if (!route) {
1192
- return { error: `Ferramenta '${toolName}' n\xE3o encontrada ou registrada.` };
1424
+ return { error: `This tool '${toolName}'is not found or registered you must use the correct name of this tool.` };
1193
1425
  }
1194
1426
  if (route.server === "native") {
1195
1427
  return this.nativeToolInvoker.invoke(route.originalName, args);
1196
1428
  } else {
1197
1429
  const session = this.sessions.get(route.server);
1198
1430
  if (!session) {
1199
- return { error: `Sess\xE3o para o servidor '${route.server}' n\xE3o encontrada.` };
1431
+ return { error: `Session to the server '${route.server}' n\xE3o encontrada.` };
1200
1432
  }
1201
1433
  const result = await session.callTool({ name: route.originalName, arguments: args });
1202
1434
  return result.content;
@@ -1223,7 +1455,7 @@ var MCPClient = class {
1223
1455
  try {
1224
1456
  await session.close();
1225
1457
  } catch (error) {
1226
- console.error(`[MCPClient] Erro ao encerrar conex\xE3o com '${name}':`, error);
1458
+ console.error(`[MCPClient]Error closing connection to'${name}':`, error);
1227
1459
  }
1228
1460
  }
1229
1461
  }
@@ -1248,12 +1480,12 @@ var AdvancedFeedbackSystem = class {
1248
1480
  this.cumulativeScore += penalty;
1249
1481
  return {
1250
1482
  score: penalty,
1251
- message: "Direct text response is a protocol violation. All communication must be done via the 'message_notify_dev' tool.",
1483
+ message: "You are attempting a direct message without a tool_call. All replies must contain tool_call.",
1252
1484
  correction: `
1253
- ## PROTOCOL VIOLATION \u2014 SEVERE
1254
- You sent a direct text response, which is strictly prohibited.
1255
- PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
1256
- You MUST use tools for all actions and communication.
1485
+ ## PROTOCOL VIOLATION \u2014 SERIOUS
1486
+ You are sending a direct response without tool_call, which is strictly prohibited.
1487
+ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
1488
+ You MUST always use tool_call without exception.
1257
1489
  `.trim()
1258
1490
  };
1259
1491
  }
@@ -1265,12 +1497,12 @@ var AdvancedFeedbackSystem = class {
1265
1497
  };
1266
1498
 
1267
1499
  // src/app/agent/bluma/core/bluma.ts
1268
- import path7 from "path";
1500
+ import path8 from "path";
1269
1501
 
1270
1502
  // src/app/agent/session_manger/session_manager.ts
1271
- import path6 from "path";
1503
+ import path7 from "path";
1272
1504
  import os3 from "os";
1273
- import { promises as fs7 } from "fs";
1505
+ import { promises as fs8 } from "fs";
1274
1506
  var fileLocks = /* @__PURE__ */ new Map();
1275
1507
  async function withFileLock(file, fn) {
1276
1508
  const prev = fileLocks.get(file) || Promise.resolve();
@@ -1288,13 +1520,13 @@ async function withFileLock(file, fn) {
1288
1520
  function expandHome(p) {
1289
1521
  if (!p) return p;
1290
1522
  if (p.startsWith("~")) {
1291
- return path6.join(os3.homedir(), p.slice(1));
1523
+ return path7.join(os3.homedir(), p.slice(1));
1292
1524
  }
1293
1525
  return p;
1294
1526
  }
1295
1527
  function getPreferredAppDir() {
1296
- const fixed = path6.join(os3.homedir(), ".bluma-cli");
1297
- return path6.resolve(expandHome(fixed));
1528
+ const fixed = path7.join(os3.homedir(), ".bluma-cli");
1529
+ return path7.resolve(expandHome(fixed));
1298
1530
  }
1299
1531
  async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1300
1532
  let attempt = 0;
@@ -1302,7 +1534,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1302
1534
  const isWin = process.platform === "win32";
1303
1535
  while (attempt <= maxRetries) {
1304
1536
  try {
1305
- await fs7.rename(src, dest);
1537
+ await fs8.rename(src, dest);
1306
1538
  return;
1307
1539
  } catch (e) {
1308
1540
  lastErr = e;
@@ -1315,9 +1547,9 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1315
1547
  }
1316
1548
  }
1317
1549
  try {
1318
- const data = await fs7.readFile(src);
1319
- await fs7.writeFile(dest, data);
1320
- await fs7.unlink(src).catch(() => {
1550
+ const data = await fs8.readFile(src);
1551
+ await fs8.writeFile(dest, data);
1552
+ await fs8.unlink(src).catch(() => {
1321
1553
  });
1322
1554
  return;
1323
1555
  } catch (fallbackErr) {
@@ -1326,16 +1558,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
1326
1558
  }
1327
1559
  async function ensureSessionDir() {
1328
1560
  const appDir = getPreferredAppDir();
1329
- const sessionDir = path6.join(appDir, "sessions");
1330
- await fs7.mkdir(sessionDir, { recursive: true });
1561
+ const sessionDir = path7.join(appDir, "sessions");
1562
+ await fs8.mkdir(sessionDir, { recursive: true });
1331
1563
  return sessionDir;
1332
1564
  }
1333
1565
  async function loadOrcreateSession(sessionId2) {
1334
1566
  const sessionDir = await ensureSessionDir();
1335
- const sessionFile = path6.join(sessionDir, `${sessionId2}.json`);
1567
+ const sessionFile = path7.join(sessionDir, `${sessionId2}.json`);
1336
1568
  try {
1337
- await fs7.access(sessionFile);
1338
- const fileContent = await fs7.readFile(sessionFile, "utf-8");
1569
+ await fs8.access(sessionFile);
1570
+ const fileContent = await fs8.readFile(sessionFile, "utf-8");
1339
1571
  const sessionData = JSON.parse(fileContent);
1340
1572
  return [sessionFile, sessionData.conversation_history || []];
1341
1573
  } catch (error) {
@@ -1344,7 +1576,7 @@ async function loadOrcreateSession(sessionId2) {
1344
1576
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
1345
1577
  conversation_history: []
1346
1578
  };
1347
- await fs7.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
1579
+ await fs8.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
1348
1580
  return [sessionFile, []];
1349
1581
  }
1350
1582
  }
@@ -1352,12 +1584,12 @@ async function saveSessionHistory(sessionFile, history) {
1352
1584
  await withFileLock(sessionFile, async () => {
1353
1585
  let sessionData;
1354
1586
  try {
1355
- const dir = path6.dirname(sessionFile);
1356
- await fs7.mkdir(dir, { recursive: true });
1587
+ const dir = path7.dirname(sessionFile);
1588
+ await fs8.mkdir(dir, { recursive: true });
1357
1589
  } catch {
1358
1590
  }
1359
1591
  try {
1360
- const fileContent = await fs7.readFile(sessionFile, "utf-8");
1592
+ const fileContent = await fs8.readFile(sessionFile, "utf-8");
1361
1593
  sessionData = JSON.parse(fileContent);
1362
1594
  } catch (error) {
1363
1595
  const code = error && error.code;
@@ -1368,14 +1600,14 @@ async function saveSessionHistory(sessionFile, history) {
1368
1600
  console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
1369
1601
  }
1370
1602
  }
1371
- const sessionId2 = path6.basename(sessionFile, ".json");
1603
+ const sessionId2 = path7.basename(sessionFile, ".json");
1372
1604
  sessionData = {
1373
1605
  session_id: sessionId2,
1374
1606
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
1375
1607
  conversation_history: []
1376
1608
  };
1377
1609
  try {
1378
- await fs7.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1610
+ await fs8.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1379
1611
  } catch {
1380
1612
  }
1381
1613
  }
@@ -1383,7 +1615,7 @@ async function saveSessionHistory(sessionFile, history) {
1383
1615
  sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
1384
1616
  const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
1385
1617
  try {
1386
- await fs7.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1618
+ await fs8.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
1387
1619
  await safeRenameWithRetry(tempSessionFile, sessionFile);
1388
1620
  } catch (writeError) {
1389
1621
  if (writeError instanceof Error) {
@@ -1392,7 +1624,7 @@ async function saveSessionHistory(sessionFile, history) {
1392
1624
  console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
1393
1625
  }
1394
1626
  try {
1395
- await fs7.unlink(tempSessionFile);
1627
+ await fs8.unlink(tempSessionFile);
1396
1628
  } catch {
1397
1629
  }
1398
1630
  }
@@ -1403,320 +1635,73 @@ async function saveSessionHistory(sessionFile, history) {
1403
1635
  import os4 from "os";
1404
1636
  var SYSTEM_PROMPT = `
1405
1637
 
1406
- ### YOU ARE BluMa CLI \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
1407
- You use a proprietary Large Language Model (LLM) fine-tuned by the NomadEngenuity team.
1408
-
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.
1409
1643
  ---
1410
1644
 
1411
- ## BEHAVIORAL RULES
1412
-
1413
- - **Identity:**
1414
- You are BluMa (NomadEngenuity). Maintain professionalism and technical language.
1415
-
1416
- - **Communication:**
1417
- ALL messages must be sent via 'message_notify_dev'.
1418
- **No direct text replies to the developer.**
1419
-
1420
- - **Task Completion:**
1421
- When a task is completed, immediately invoke 'agent_end_task' without dev permissions.
1645
+ ### CORE DIRECTIVES
1422
1646
 
1423
- - **Tool Rules:**
1424
- Never make parallel tool calls.
1425
- Always use only the defined tools with their exact names.
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.
1426
1652
 
1427
- - **Autonomy:**
1428
- Act 100% autonomously.
1429
- Do not ask for formatting preferences.
1430
- Use the notebook for internal reasoning.
1653
+ ---
1431
1654
 
1655
+ ### TOOL USAGE GUIDELINES
1432
1656
 
1433
- ### CRITICAL COMMUNICATION PROTOCOL
1434
- - Only tool_calls are allowed for assistant replies. Never include a "content" field.
1435
- - Always use tools to respond, retrieve data, compute or transform. Await a valid tool response before any final message.
1436
- - 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
+ ---
1437
1660
 
1661
+ ### CURRENT ENVIRONMENT CONTEXT
1438
1662
  <current_system_environment>
1439
1663
  - Operating System: {os_type} ({os_version})
1440
1664
  - Architecture: {architecture}
1441
- - Current Working Directory: {workdir}
1665
+ - Current Directory: {workdir}
1442
1666
  - Shell: {shell_type}
1443
- - Username: {username}
1667
+ - User: {username}
1444
1668
  - Current Date: {current_date}
1445
- - Timezone: {timezone}
1446
- - Locale: {locale}
1447
1669
  </current_system_environment>
1448
1670
 
1671
+ ---
1449
1672
 
1450
- <mermaid_diagrams>
1451
- # MERMAID DIAGRAM CREATION - PERFECT SYNTAX REQUIRED!
1452
- ## CRITICAL: ALL DIAGRAMS MUST RENDER WITHOUT ERRORS
1453
-
1454
- ### MANDATORY MERMAID SYNTAX RULES
1455
- 1. **ALWAYS wrap ALL labels in double quotes**: "label text"
1456
- 2. **NEVER use unescaped special characters**: /, (), [], {}, +, *, ?, ^, $, |, 3. **Use line breaks (<br/>) for multi-line labels**: "Line 1<br/>Line 2"
1457
- 4. **NO custom colors or ::: syntax**: Stick to standard themes
1458
- 5. **NO beta features**: Use only stable Mermaid syntax
1459
- 6. **NO remote images**: Never embed external images
1460
-
1461
- ### SAFE LABEL FORMATTING
1462
-
1463
- CORRECT:
1464
- - "dev Authentication"
1465
- - "API Gateway (REST)"
1466
- - "Database Connection<br/>MySQL 8.0"
1467
- - "Process Data<br/>Transform & Validate"
1468
-
1469
- INCORRECT:
1470
- - dev Authentication (missing quotes)
1471
- - API Gateway (REST) (parentheses without quotes)
1472
- - Database/MySQL (slash without quotes)
1473
- - Process & Transform (ampersand without quotes)
1474
-
1475
-
1476
- ### DIAGRAM TYPE BEST PRACTICES
1477
-
1478
- IMPORTANT
1479
- The Notion API rejects rich text of type "code" and only accepts "text" for code blocks \u2013
1480
- a limitation of their own JSON (even for the language: "mermaid"). Therefore, only the code/text block
1481
- type is viable.
1482
-
1483
- You should insert the pretty diagram without any delimiters or headers, in the code block with
1484
- proper indentation and double quotes, only in the text field, to facilitate as little manual rework as possible.
1485
- It's ready to copy into the native Mermaid block.
1486
-
1487
- #### FLOWCHART
1488
-
1489
- flowchart TD
1490
- A["Start Process"] --> B["Validate Input"]
1491
- B --> C{"Is Valid?"}
1492
- C -->|"Yes"| D["Process Data"]
1493
- C -->|"No"| E["Return Error"]
1494
- D --> F["Save to Database"]
1495
- F --> G["Send Response"]
1496
-
1497
-
1498
- #### SEQUENCE DIAGRAM
1499
-
1500
- sequenceDiagram
1501
- participant U as "dev"
1502
- participant A as "API Gateway"
1503
- participant D as "Database"
1504
-
1505
- U->>A: "Submit Request"
1506
- A->>D: "Query Data"
1507
- D-->>A: "Return Results"
1508
- A-->>U: "Response Data"
1509
-
1510
- #### CLASS DIAGRAM
1511
-
1512
- classDiagram
1513
- class dev {
1514
- +String name
1515
- +String email
1516
- +authenticate()
1517
- +updateProfile()
1518
- }
1519
-
1520
- class Database {
1521
- +connect()
1522
- +query()
1523
- +close()
1524
- }
1525
-
1526
- dev --> Database : "uses"
1527
-
1528
-
1529
- ### VALIDATION CHECKLIST
1530
- Before creating any diagram, ensure:
1531
- - [ ] All labels are wrapped in double quotes
1532
- - [ ] No unescaped special characters (/, (), etc.)
1533
- - [ ] Line breaks use <br/> syntax
1534
- - [ ] No custom colors or styling
1535
- - [ ] No beta features or experimental syntax
1536
- - [ ] All connections use proper arrow syntax
1537
- - [ ] Node IDs are simple alphanumeric
1538
-
1539
- ### ERROR PREVENTION
1540
- - Always test diagram syntax mentally before generating
1541
- - Use simple, descriptive labels without special formatting
1542
- - Prefer clarity over visual complexity
1543
- - Keep diagrams focused and readable
1544
- - Use standard Mermaid themes only
1545
-
1546
- ## ZERO TOLERANCE FOR SYNTAX ERRORS
1547
- Every diagram MUST render perfectly on first try. No exceptions.
1548
- </mermaid_diagrams>
1549
-
1673
+ ### COMMUNICATION PROTOCOL
1550
1674
  <message_rules>
1551
- - 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
1552
1676
  - Reply immediately to new user messages before other operations
1553
1677
  - First reply must be brief, only confirming receipt without specific solutions
1554
- - Notify dev's with brief explanation when changing methods or strategies
1555
- - Message tools are divided into notify (non-blocking, no reply needed from dev's) and ask (blocking, reply required)
1556
- - Actively use notify for progress updates, but reserve ask for only essential needs to minimize dev's disruption and avoid blocking progress
1557
- - 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'
1558
1682
  </message_rules>
1559
-
1560
-
1561
- ### Live Development Overlaps
1562
-
1563
- During your workflow, the programmer {username} may send messages at any time.
1564
- These messages **must be immediately incorporated** into your execution flow.
1565
- **Always confirm receipt using {message_notify_dev}** and proceed with your work.
1566
-
1567
- ### Instructions for Handling Messages from the Programmer
1568
-
1569
- 1. **Upon receiving a message from {username}:**
1570
- - Immediately confirm using {message_notify_dev}.
1571
- - Integrate the instruction into your reasoning and execution flow.
1572
-
1573
- 2. **Regarding your reasoning:**
1574
- - Be direct, minimalist, and clear.
1575
- - Avoid unnecessary or verbose thoughts.
1576
-
1577
- 3. **Avoid polluting the history:**
1578
- - **Do not repeat or reply to existing messages.**
1579
- - Only act if the new message introduces a **new instruction or shifts the current task\u2019s focus**.
1580
-
1581
- <reasoning_rules>
1582
- # YOUR THINKING ON A NOTEBOOK - MANDATORY USE
1583
- CRITICAL: Your laptop (**reasoning_nootebook**) is your ORGANIZED MIND
1584
- ## IMPORTANT
1585
- ## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
1586
- ## ALWAYS USE A NOTEBOOK (Always for):
1587
- - ANY task
1588
- - Before starting development (plan first!)
1589
- - Projects with multiple files (organize the structure)
1590
- - Debugging sessions (monitor discoveries)
1591
- - Extensive refactoring (map the changes)
1592
- - Architectural decisions (think through the options)
1593
-
1594
- ## HOW TO USE A NOTEBOOK:
1595
- 1. Start with **reasoning_nootebook**
1596
- 2. Break the task down into logical steps
1597
- 3. Plan the approach - Which files? What changes? What order? 4. Track progress - Check off completed steps
1598
- 5. Write down decisions - Why did you choose this approach?
1599
- 6. Update continuously - Keep the notebook up to date
1600
-
1601
- ## THE NOTEBOOK PREVENTS:
1602
- - Acting "outside the box"
1603
- - Forgetting task requirements
1604
- - Losing control of complex workflows
1605
- - Making unplanned changes
1606
- - Ineffective approaches
1607
- - Working without a clear roadmap
1608
- - Jumping between unrelated subtasks
1609
-
1610
- ##Important rule:
1611
- Do **not** include any future steps, to-do items, or pending tasks here.
1612
- Those belong strictly in the **remaining_tasks** field.
1613
-
1614
- Never write phrases like:
1615
- - "Next I will..."
1616
- - "I still need to..."
1617
- - "Pending: ..."
1618
- Such content must go in **remaining_tasks**, not **thought**.
1619
-
1620
- - remaining_tasks: Checklist-style list of high-level upcoming tasks.
1621
- This format is **mandatory**:
1622
- - Each task **must start** with either:
1623
- - "\u{1F5F8}" \u2192 for tasks not yet done (pending)
1624
- - "[ ]" \u2192 for tasks that have already been completed
1625
-
1626
- Whenever a task is already done, it **must** be marked with "\u{1F5F8}". Do not leave completed tasks without the checkmark.
1627
-
1628
- Do not use other formats like "-", "*", or plain text without the prefix.
1629
-
1630
- Examples:
1631
- \u{1F5F8} Test integration flow
1632
- \u{1F5F8} Set up environment
1633
- [ ] Configure database
1634
-
1635
- </reasoning_rules>
1636
-
1637
- ### Tool Naming Policy
1638
-
1639
- Tool names must strictly follow the standard naming format:
1640
-
1641
- - Use: plain, unmodified, lowercase names
1642
- - Do NOT use: special characters, extra spaces, version suffixes, or dynamic IDs
1643
-
1644
- ---
1645
-
1646
- Correct Examples:
1647
- - bluma_notebook
1648
- - getDataTool
1649
- - convertImage
1650
- - userAuth
1651
-
1652
1683
  ---
1653
1684
 
1654
- Incorrect Examples:
1655
- - reasoning_nootebook:0 \u2190 contains colon and dynamic suffix
1656
- - reasoning_nootebook 1 \u2190 contains space and number
1657
- - reasoning_nootebook#v2 \u2190 contains special character #
1658
- - bluma__nootebook \u2190 double underscore
1659
- - reasoning_nootebook \u2190 capital letters and underscore
1660
- - bluma nootebook \u2190 contains space
1661
-
1662
- ---
1663
-
1664
- Rule Summary:
1665
- - Use only a\u2013z, 0\u20139, and underscores (_)
1666
- - Do not append suffixes like :0, :v2, etc.
1667
- - Tool names must be static and predictable
1668
- - No whitespace, no dynamic elements, no special characters
1669
-
1670
-
1671
- <edit_tool_rules>
1672
- - Use this tool to perform precise text replacements inside files based on exact literal matches.
1673
- - Can be used to create new files or directories implicitly by targeting non-existing paths.
1674
- - Suitable for inserting full content into a file even if the file does not yet exist.
1675
- - Shell access is not required for file or directory creation when using this tool.
1676
- - Always prefer this tool over shell_command when performing structured edits or creating files with specific content.
1677
- - Ensure **old_string** includes 3+ lines of exact context before and after the target if replacing existing content.
1678
- - 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.
1679
- - When generating or modifying todo.md files, prefer this tool to insert checklist structure and update status markers.
1680
- - After completing any task in the checklist, immediately update the corresponding section in todo.md using this tool.
1681
- - Reconstruct the entire file from task planning context if todo.md becomes outdated or inconsistent.
1682
- - Track all progress related to planning and execution inside todo.md using text replacement only.
1683
- </edit_tool_rules>
1684
-
1685
- Real-Time Developer Messages
1686
- - During processing, the developer will send you messages.
1687
- - You MUST respond immediately via message_notify_dev, and be brief. You should use it in your next thoughts/actions.
1688
-
1689
-
1690
- <agent_end_task_rules>
1691
- This tool is mandatory.
1692
- 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.
1693
- </agent_end_task_rules>
1694
-
1695
- ### QUALITY STANDARDS
1696
- - Document every major decision in Notion(
1697
- ##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.
1698
- You should always standardize everything using Notion's actual headers (heading_1, heading_2, etc.), making the structure
1699
- semantically better for reading and navigation.
1700
- )
1701
- - Communicate transparently at each step
1702
- - Write clean, well-documented code
1703
- - Follow existing project conventions
1704
- - Test implementations when possible
1705
- - Ensure security and performance
1706
-
1707
- <scope_and_limitations>
1708
- # WHAT YOU DON'T HANDLE
1709
- - Non-technical questions
1710
- - Personal advice
1711
- - General conversation
1712
- - Tasks outside software development
1713
-
1714
- # IF ASKED NON-TECHNICAL QUESTIONS
1715
- - Use message_notify_dev to politely decline
1716
- - Explain you only handle technical/coding tasks
1717
- - Suggest they ask a development-related question instead
1718
- </scope_and_limitations>
1719
-
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.
1720
1705
  `;
1721
1706
  function getUnifiedSystemPrompt() {
1722
1707
  const now = /* @__PURE__ */ new Date();
@@ -1771,7 +1756,7 @@ function createApiContextWindow(fullHistory, maxTurns) {
1771
1756
  const turns = [];
1772
1757
  let currentTurn = [];
1773
1758
  let turnsFound = 0;
1774
- const isDevOverlay = (m) => m?.role === "user" && m?.name === "dev_overlay";
1759
+ const isDevOverlay = (m) => m?.role === "user" && m?.name === "user_overlay";
1775
1760
  for (let i = conversationHistory.length - 1; i >= 0; i--) {
1776
1761
  const msg = conversationHistory[i];
1777
1762
  currentTurn.unshift(msg);
@@ -1824,17 +1809,18 @@ var BluMaAgent = class {
1824
1809
  this.feedbackSystem = feedbackSystem;
1825
1810
  this.eventBus.on("user_interrupt", () => {
1826
1811
  this.isInterrupted = true;
1812
+ this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
1827
1813
  });
1828
- this.eventBus.on("dev_overlay", async (data) => {
1814
+ this.eventBus.on("user_overlay", async (data) => {
1829
1815
  const clean = String(data.payload ?? "").trim();
1830
- this.history.push({ role: "user", name: "dev_overlay", content: clean });
1831
- 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() });
1832
1818
  try {
1833
1819
  if (this.sessionFile) {
1834
1820
  await saveSessionHistory(this.sessionFile, this.history);
1835
1821
  }
1836
1822
  } catch (e) {
1837
- 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}` });
1838
1824
  }
1839
1825
  });
1840
1826
  }
@@ -1925,7 +1911,7 @@ var BluMaAgent = class {
1925
1911
 
1926
1912
  ${editData.error.display}`;
1927
1913
  }
1928
- const filename = path7.basename(toolArgs.file_path);
1914
+ const filename = path8.basename(toolArgs.file_path);
1929
1915
  return createDiff(filename, editData.currentContent || "", editData.newContent);
1930
1916
  } catch (e) {
1931
1917
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -1934,16 +1920,20 @@ ${editData.error.display}`;
1934
1920
  async _continueConversation() {
1935
1921
  try {
1936
1922
  if (this.isInterrupted) {
1937
- this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
1923
+ this.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
1938
1924
  return;
1939
1925
  }
1940
1926
  const contextWindow = createApiContextWindow(this.history, this.maxContextTurns);
1941
1927
  const response = await this.llm.chatCompletion({
1942
1928
  model: this.deploymentName,
1943
1929
  messages: contextWindow,
1930
+ temperature: 0.2,
1944
1931
  tools: this.mcpClient.getAvailableTools(),
1945
1932
  tool_choice: "required",
1946
- 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
1947
1937
  });
1948
1938
  if (this.isInterrupted) {
1949
1939
  this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
@@ -1952,7 +1942,7 @@ ${editData.error.display}`;
1952
1942
  const message = response.choices[0].message;
1953
1943
  this.history.push(message);
1954
1944
  if (message.tool_calls) {
1955
- const autoApprovedTools = ["agent_end_task", "message_notify_dev", "reasoning_nootebook"];
1945
+ const autoApprovedTools = ["agent_end_task", "message_notify_user", "reasoning_nootebook"];
1956
1946
  const toolToCall = message.tool_calls[0];
1957
1947
  const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
1958
1948
  if (isSafeTool) {
@@ -2033,11 +2023,11 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
2033
2023
  You are BluMa InitSubAgent. Maintain professionalism and technical language.
2034
2024
 
2035
2025
  - Communication:
2036
- ALL messages must be sent via 'message_notify_dev'.
2037
- 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.
2038
2028
 
2039
2029
  - Task Completion:
2040
- 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.
2041
2031
 
2042
2032
  - Tool Rules:
2043
2033
  Never make parallel tool calls.
@@ -2059,20 +2049,20 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
2059
2049
  - Architecture: {architecture}
2060
2050
  - Current Working Directory: {workdir}
2061
2051
  - Shell: {shell_type}
2062
- - Username: {username}
2052
+ - User: {username}
2063
2053
  - Current Date: {current_date}
2064
2054
  - Timezone: {timezone}
2065
2055
  - Locale: {locale}
2066
2056
  </current_system_environment>
2067
2057
 
2068
2058
  <message_rules>
2069
- - 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
2070
2060
  - Reply immediately to new user messages before other operations
2071
2061
  - First reply must be brief, only confirming receipt without specific solutions
2072
- - Notify dev's with brief explanation when changing methods or strategies
2062
+ - Notify user's with brief explanation when changing methods or strategies
2073
2063
  - Message tools are divided into notify (non-blocking, no reply needed) and ask (blocking)
2074
2064
  - Actively use notify for progress updates, reserve ask for essential needs to avoid blocking
2075
- - 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'
2076
2066
  </message_rules>
2077
2067
 
2078
2068
  <reasoning_rules>
@@ -2082,7 +2072,7 @@ CRITICAL: Your laptop (reasoning_nootebook) is your ORGANIZED MIND
2082
2072
  ## NEVER PUT CHECKLISTS OR STEPS IN THE THOUGHT TEXT
2083
2073
  ## ALWAYS USE A NOTEBOOK (Always for):
2084
2074
  - ANY task
2085
- - Before starting development (plan first!)
2075
+ - Before starting userelopment (plan first!)
2086
2076
  - Projects with multiple files (organize the structure)
2087
2077
  - Debugging sessions (monitor discoveries)
2088
2078
  - Extensive refactoring (map the changes)
@@ -2129,6 +2119,11 @@ Do not include future steps/to-dos in thought; put them strictly in remaining_ta
2129
2119
  </edit_tool_rules>
2130
2120
 
2131
2121
 
2122
+ <agent_end_task_rules>
2123
+ This tool is mandatory.
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.
2125
+ </agent_end_task_rules>
2126
+
2132
2127
  ### Tool Naming Policy
2133
2128
  - Use plain, unmodified, lowercase tool names
2134
2129
  - No special characters, spaces, or version suffixes
@@ -2153,9 +2148,9 @@ Rule Summary:
2153
2148
  - Never invent file content. Read files via tools to confirm.
2154
2149
 
2155
2150
  ## OUTPUT & PROTOCOLS
2156
- - Emit 'backend_message' events through tools only (message_notify_dev) for progress updates.
2157
- - Before writing BluMa.md, propose structure via message_notify_dev and proceed using edit_tool.
2158
- - 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.
2159
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.
2160
2155
  - On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_task.
2161
2156
 
@@ -2168,7 +2163,7 @@ Rule Summary:
2168
2163
  ## EXEMPLAR FLOW (GUIDELINE)
2169
2164
  1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
2170
2165
  2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
2171
- 3) Draft BluMa.md structure (message_notify_dev)
2166
+ 3) Draft BluMa.md structure (message_notify_user)
2172
2167
  4) Write BluMa.md via edit_tool
2173
2168
  5) Announce completion and agent_end_task
2174
2169
 
@@ -2258,7 +2253,7 @@ ${editData.error.display}`;
2258
2253
  async _continueConversation() {
2259
2254
  try {
2260
2255
  if (this.isInterrupted) {
2261
- this.emitEvent("info", { message: "SubAgent task cancelled by user." });
2256
+ this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
2262
2257
  return;
2263
2258
  }
2264
2259
  const contextWindow = this.history.slice(-this.maxContextTurns);
@@ -2270,7 +2265,7 @@ ${editData.error.display}`;
2270
2265
  parallel_tool_calls: false
2271
2266
  });
2272
2267
  if (this.isInterrupted) {
2273
- this.emitEvent("info", { message: "SubAgent task cancelled by user." });
2268
+ this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
2274
2269
  return;
2275
2270
  }
2276
2271
  const message = response.choices[0].message;
@@ -2319,7 +2314,7 @@ ${editData.error.display}`;
2319
2314
  try {
2320
2315
  if (this.isInterrupted) {
2321
2316
  this.emitEvent("info", { message: "SubAgent task cancelled before tool execution." });
2322
- toolResultContent = JSON.stringify({ error: "Task cancelled by user before execution." });
2317
+ toolResultContent = JSON.stringify({ error: "Task cancelled byuserr before execution." });
2323
2318
  } else {
2324
2319
  const result = await this.ctx.mcpClient.invoke(toolName, toolArgs);
2325
2320
  let finalResult = result;
@@ -2435,7 +2430,7 @@ var SubAgentsBluMa = class {
2435
2430
  };
2436
2431
 
2437
2432
  // src/app/agent/agent.ts
2438
- var globalEnvPath = path8.join(os6.homedir(), ".bluma-cli", ".env");
2433
+ var globalEnvPath = path9.join(os6.homedir(), ".bluma-cli", ".env");
2439
2434
  dotenv.config({ path: globalEnvPath });
2440
2435
  var Agent = class {
2441
2436
  sessionId;
@@ -2461,9 +2456,52 @@ var Agent = class {
2461
2456
  const apiKey = process.env.AZURE_OPENAI_API_KEY;
2462
2457
  const apiVersion = process.env.AZURE_OPENAI_API_VERSION;
2463
2458
  this.deploymentName = process.env.AZURE_OPENAI_DEPLOYMENT || "";
2464
- if (!endpoint || !apiKey || !apiVersion || !this.deploymentName) {
2465
- const errorMessage = `Uma ou mais vari\xE1veis de ambiente Azure OpenAI n\xE3o foram encontradas. Verifique em: ${globalEnvPath} ou nas vari\xE1veis de sistema.`;
2466
- throw new Error(errorMessage);
2459
+ const missing = [];
2460
+ if (!endpoint) missing.push("AZURE_OPENAI_ENDPOINT");
2461
+ if (!apiKey) missing.push("AZURE_OPENAI_API_KEY");
2462
+ if (!apiVersion) missing.push("AZURE_OPENAI_API_VERSION");
2463
+ if (!this.deploymentName) missing.push("AZURE_OPENAI_DEPLOYMENT");
2464
+ if (missing.length > 0) {
2465
+ const platform = process.platform;
2466
+ const varList = missing.join(", ");
2467
+ let guidance = "";
2468
+ if (platform === "win32") {
2469
+ guidance = [
2470
+ "Windows (PowerShell):",
2471
+ ` $env:${missing[0]}="<value>" # sess\xE3o atual`,
2472
+ ...missing.slice(1).map((v) => ` $env:${v}="<value>"`),
2473
+ " # Persistir para o utilizador:",
2474
+ ...missing.map((v) => ` [System.Environment]::SetEnvironmentVariable("${v}", "<value>", "User")`),
2475
+ "",
2476
+ "Windows (cmd.exe):",
2477
+ ...missing.map((v) => ` setx ${v} "<value>"`)
2478
+ ].join("");
2479
+ } else if (platform === "darwin" || platform === "linux") {
2480
+ guidance = [
2481
+ "macOS/Linux (bash/zsh):",
2482
+ ...missing.map((v) => ` echo 'export ${v}="<value>"' >> ~/.bashrc # ou ~/.zshrc`),
2483
+ " source ~/.bashrc # ou: source ~/.zshrc"
2484
+ ].join("");
2485
+ } else {
2486
+ guidance = [
2487
+ "Generic POSIX:",
2488
+ ...missing.map((v) => ` export ${v}="<value>"`)
2489
+ ].join("");
2490
+ }
2491
+ const message = [
2492
+ `Missing required environment variables: ${varList}.`,
2493
+ `Configure them globally using the commands below (based on your OS), or set them in the config file: ${globalEnvPath}.`,
2494
+ "",
2495
+ guidance
2496
+ ].join("");
2497
+ this.eventBus.emit("backend_message", {
2498
+ type: "error",
2499
+ code: "missing_env",
2500
+ missing,
2501
+ path: globalEnvPath,
2502
+ message
2503
+ });
2504
+ throw new Error(message);
2467
2505
  }
2468
2506
  const openai = new OpenAI({
2469
2507
  // Configuração do cliente OpenAI hospedado no Azure
@@ -2522,19 +2560,19 @@ var Agent = class {
2522
2560
  };
2523
2561
 
2524
2562
  // src/app/ui/WorkingTimer.tsx
2525
- import { useState as useState3, useEffect as useEffect2 } from "react";
2563
+ import { useState as useState4, useEffect as useEffect3 } from "react";
2526
2564
  import { Box as Box7, Text as Text7 } from "ink";
2527
2565
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2528
2566
  var WorkingTimer = () => {
2529
- const [seconds, setSeconds] = useState3(0);
2530
- const [dotIndex, setDotIndex] = useState3(1);
2531
- useEffect2(() => {
2567
+ const [seconds, setSeconds] = useState4(0);
2568
+ const [dotIndex, setDotIndex] = useState4(1);
2569
+ useEffect3(() => {
2532
2570
  const secondsTimer = setInterval(() => {
2533
2571
  setSeconds((prev) => prev + 1);
2534
2572
  }, 1e3);
2535
2573
  return () => clearInterval(secondsTimer);
2536
2574
  }, []);
2537
- useEffect2(() => {
2575
+ useEffect3(() => {
2538
2576
  const dotsTimer = setInterval(() => {
2539
2577
  setDotIndex((prev) => prev % 3 + 1);
2540
2578
  }, 100);
@@ -2553,7 +2591,7 @@ import { Box as Box9 } from "ink";
2553
2591
 
2554
2592
  // src/app/ui/components/toolCallRenderers.tsx
2555
2593
  import { Box as Box8, Text as Text8 } from "ink";
2556
- import path9 from "path";
2594
+ import path10 from "path";
2557
2595
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2558
2596
  var formatArgumentsForDisplay = (args) => {
2559
2597
  if (typeof args === "string") {
@@ -2565,7 +2603,9 @@ var formatArgumentsForDisplay = (args) => {
2565
2603
  }
2566
2604
  return JSON.stringify(args, null, 2);
2567
2605
  };
2568
- var renderShellCommand2 = ({ args }) => {
2606
+ var renderShellCommand2 = ({
2607
+ args
2608
+ }) => {
2569
2609
  const command = args.command || "[command not found]";
2570
2610
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2571
2611
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
@@ -2586,7 +2626,7 @@ var renderLsTool2 = ({ args }) => {
2586
2626
  } catch (e) {
2587
2627
  directoryPath = "Error parsing arguments";
2588
2628
  }
2589
- const finalDirectoryName = path9.basename(directoryPath);
2629
+ const finalDirectoryName = path10.basename(directoryPath);
2590
2630
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2591
2631
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2592
2632
  /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
@@ -2598,7 +2638,9 @@ var renderLsTool2 = ({ args }) => {
2598
2638
  ] }) })
2599
2639
  ] });
2600
2640
  };
2601
- var renderCountFilesLines = ({ args }) => {
2641
+ var renderCountFilesLines = ({
2642
+ args
2643
+ }) => {
2602
2644
  let directoryPath = "[path not found]";
2603
2645
  try {
2604
2646
  const parsedArgs = typeof args === "string" ? JSON.parse(args) : args;
@@ -2606,7 +2648,7 @@ var renderCountFilesLines = ({ args }) => {
2606
2648
  } catch (e) {
2607
2649
  directoryPath = "Error parsing arguments";
2608
2650
  }
2609
- const finalDirectoryName = path9.basename(directoryPath);
2651
+ const finalDirectoryName = path10.basename(directoryPath);
2610
2652
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2611
2653
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2612
2654
  /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
@@ -2618,7 +2660,9 @@ var renderCountFilesLines = ({ args }) => {
2618
2660
  ] }) })
2619
2661
  ] });
2620
2662
  };
2621
- var renderReadFileLines2 = ({ args }) => {
2663
+ var renderReadFileLines2 = ({
2664
+ args
2665
+ }) => {
2622
2666
  let filepath = "[path not found]";
2623
2667
  let startLine = 0;
2624
2668
  let endLine = 0;
@@ -2630,7 +2674,7 @@ var renderReadFileLines2 = ({ args }) => {
2630
2674
  } catch (e) {
2631
2675
  filepath = "Error parsing arguments";
2632
2676
  }
2633
- const finalFileName = path9.basename(filepath);
2677
+ const finalFileName = path10.basename(filepath);
2634
2678
  return (
2635
2679
  // A caixa externa com a borda, seguindo o template
2636
2680
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
@@ -2654,7 +2698,9 @@ var renderReadFileLines2 = ({ args }) => {
2654
2698
  ] })
2655
2699
  );
2656
2700
  };
2657
- var renderBlumaNotebook = ({ args }) => {
2701
+ var renderBlumaNotebook = ({
2702
+ args
2703
+ }) => {
2658
2704
  try {
2659
2705
  let dataToParse = args;
2660
2706
  if (args && typeof args === "object") {
@@ -2667,16 +2713,30 @@ var renderBlumaNotebook = ({ args }) => {
2667
2713
  }
2668
2714
  return (
2669
2715
  // Usamos a mesma estrutura de caixa com borda
2670
- /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "green", flexDirection: "column", paddingX: 1, children: [
2671
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2672
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "Thought:" }),
2673
- /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: thinkingData.thought }) })
2674
- ] }),
2675
- thinkingData.remaining_tasks && thinkingData.remaining_tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2676
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Remaining Tasks:" }),
2677
- 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))
2678
- ] })
2679
- ] })
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
+ )
2680
2740
  );
2681
2741
  } catch (e) {
2682
2742
  return /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
@@ -2685,7 +2745,10 @@ var renderBlumaNotebook = ({ args }) => {
2685
2745
  ] });
2686
2746
  }
2687
2747
  };
2688
- var renderEditToolCall = ({ args, preview }) => {
2748
+ var renderEditToolCall = ({
2749
+ args,
2750
+ preview
2751
+ }) => {
2689
2752
  let filepath = "[path not specified]";
2690
2753
  try {
2691
2754
  const parsedArgs = typeof args === "string" ? JSON.parse(args) : args;
@@ -2693,7 +2756,7 @@ var renderEditToolCall = ({ args, preview }) => {
2693
2756
  } catch (e) {
2694
2757
  filepath = "Error parsing arguments";
2695
2758
  }
2696
- const finalFileName = path9.basename(filepath);
2759
+ const finalFileName = path10.basename(filepath);
2697
2760
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
2698
2761
  /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2699
2762
  /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
@@ -2706,7 +2769,10 @@ var renderEditToolCall = ({ args, preview }) => {
2706
2769
  preview && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: Infinity }) })
2707
2770
  ] });
2708
2771
  };
2709
- var renderGenericToolCall = ({ toolName, args }) => {
2772
+ var renderGenericToolCall = ({
2773
+ toolName,
2774
+ args
2775
+ }) => {
2710
2776
  const formattedArgs = formatArgumentsForDisplay(args);
2711
2777
  return (
2712
2778
  // A "moldura" padrão de sucesso com a borda cinza
@@ -2723,18 +2789,18 @@ var renderGenericToolCall = ({ toolName, args }) => {
2723
2789
  );
2724
2790
  };
2725
2791
  var ToolRenderDisplay = {
2726
- "shell_command": renderShellCommand2,
2727
- "ls_tool": renderLsTool2,
2728
- "reasoning_nootebook": renderBlumaNotebook,
2729
- "count_file_lines": renderCountFilesLines,
2730
- "read_file_lines": renderReadFileLines2,
2731
- "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
2732
2798
  };
2733
2799
 
2734
2800
  // src/app/ui/components/ToolCallDisplay.tsx
2735
2801
  import { jsx as jsx9 } from "react/jsx-runtime";
2736
2802
  var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
2737
- if (toolName.includes("message_notify_dev") || toolName.includes("agent_end_task")) {
2803
+ if (toolName.includes("message_notify_user") || toolName.includes("agent_end_task")) {
2738
2804
  return null;
2739
2805
  }
2740
2806
  const Renderer = ToolRenderDisplay[toolName] || renderGenericToolCall;
@@ -2747,7 +2813,7 @@ import { memo as memo3 } from "react";
2747
2813
  import { Box as Box10, Text as Text9 } from "ink";
2748
2814
  import { jsx as jsx10 } from "react/jsx-runtime";
2749
2815
  var ToolResultDisplayComponent = ({ toolName, result }) => {
2750
- if (!toolName.includes("message_notify_dev")) {
2816
+ if (!toolName.includes("message_notify_user")) {
2751
2817
  return null;
2752
2818
  }
2753
2819
  try {
@@ -2819,7 +2885,7 @@ var SessionInfoConnectingMCP_default = SessionInfoConnectingMCP;
2819
2885
 
2820
2886
  // src/app/ui/components/SlashCommands.tsx
2821
2887
  import { Box as Box12, Text as Text11 } from "ink";
2822
- import { Fragment, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2888
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2823
2889
  var SlashCommands = ({ input, setHistory, agentRef }) => {
2824
2890
  const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
2825
2891
  const outBox = (children) => /* @__PURE__ */ jsx12(Box12, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginBottom: 1, flexDirection: "column", children });
@@ -2830,7 +2896,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
2830
2896
  if (cmd === "help") {
2831
2897
  const cmds = getSlashCommands();
2832
2898
  return outBox(
2833
- /* @__PURE__ */ jsxs10(Fragment, { children: [
2899
+ /* @__PURE__ */ jsxs10(Fragment2, { children: [
2834
2900
  /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Available commands" }),
2835
2901
  cmds.map((c, i) => /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2836
2902
  c.name,
@@ -2871,7 +2937,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
2871
2937
  const colType = 10;
2872
2938
  const colSource = 18;
2873
2939
  return outBox(
2874
- /* @__PURE__ */ jsxs10(Fragment, { children: [
2940
+ /* @__PURE__ */ jsxs10(Fragment2, { children: [
2875
2941
  /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "MCP Tools" }),
2876
2942
  /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2877
2943
  "Total MCP: ",
@@ -2920,7 +2986,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
2920
2986
  const colType = 10;
2921
2987
  const colSource = 18;
2922
2988
  return outBox(
2923
- /* @__PURE__ */ jsxs10(Fragment, { children: [
2989
+ /* @__PURE__ */ jsxs10(Fragment2, { children: [
2924
2990
  /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Native Tools" }),
2925
2991
  /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2926
2992
  "Total Native: ",
@@ -2963,7 +3029,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
2963
3029
  cmd
2964
3030
  ] }));
2965
3031
  };
2966
- return /* @__PURE__ */ jsx12(Fragment, { children: render2() });
3032
+ return /* @__PURE__ */ jsx12(Fragment2, { children: render2() });
2967
3033
  };
2968
3034
  var SlashCommands_default = SlashCommands;
2969
3035
 
@@ -2971,21 +3037,21 @@ var SlashCommands_default = SlashCommands;
2971
3037
  import updateNotifier from "update-notifier";
2972
3038
  import { readPackageUp } from "read-package-up";
2973
3039
  import { fileURLToPath as fileURLToPath3 } from "url";
2974
- import path10 from "path";
2975
- import fs8 from "fs";
3040
+ import path11 from "path";
3041
+ import fs9 from "fs";
2976
3042
  function findPackageJsonNearest(startDir) {
2977
3043
  let dir = startDir;
2978
3044
  for (let i = 0; i < 6; i++) {
2979
- const candidate = path10.join(dir, "package.json");
2980
- if (fs8.existsSync(candidate)) {
3045
+ const candidate = path11.join(dir, "package.json");
3046
+ if (fs9.existsSync(candidate)) {
2981
3047
  try {
2982
- const raw = fs8.readFileSync(candidate, "utf8");
3048
+ const raw = fs9.readFileSync(candidate, "utf8");
2983
3049
  const parsed = JSON.parse(raw);
2984
3050
  if (parsed?.name && parsed?.version) return parsed;
2985
3051
  } catch {
2986
3052
  }
2987
3053
  }
2988
- const parent = path10.dirname(dir);
3054
+ const parent = path11.dirname(dir);
2989
3055
  if (parent === dir) break;
2990
3056
  dir = parent;
2991
3057
  }
@@ -2998,15 +3064,15 @@ async function checkForUpdates() {
2998
3064
  }
2999
3065
  const binPath = process.argv?.[1];
3000
3066
  let pkg;
3001
- if (binPath && fs8.existsSync(binPath)) {
3002
- const candidatePkg = findPackageJsonNearest(path10.dirname(binPath));
3067
+ if (binPath && fs9.existsSync(binPath)) {
3068
+ const candidatePkg = findPackageJsonNearest(path11.dirname(binPath));
3003
3069
  if (candidatePkg?.name && candidatePkg?.version) {
3004
3070
  pkg = candidatePkg;
3005
3071
  }
3006
3072
  }
3007
3073
  if (!pkg) {
3008
3074
  const __filename = fileURLToPath3(import.meta.url);
3009
- const __dirname = path10.dirname(__filename);
3075
+ const __dirname = path11.dirname(__filename);
3010
3076
  const result = await readPackageUp({ cwd: __dirname });
3011
3077
  pkg = result?.packageJson;
3012
3078
  }
@@ -3062,28 +3128,56 @@ var UpdateNotice = ({ message }) => {
3062
3128
  };
3063
3129
  var UpdateNotice_default = UpdateNotice;
3064
3130
 
3065
- // src/app/ui/App.tsx
3131
+ // src/app/ui/components/ErrorMessage.tsx
3132
+ import { Box as Box14, Text as Text13 } from "ink";
3066
3133
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3134
+ var ErrorMessage = ({ message, details, hint }) => {
3135
+ return /* @__PURE__ */ jsxs12(
3136
+ Box14,
3137
+ {
3138
+ borderStyle: "round",
3139
+ borderColor: "red",
3140
+ paddingX: 1,
3141
+ paddingY: 0,
3142
+ flexDirection: "column",
3143
+ marginBottom: 1,
3144
+ children: [
3145
+ /* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: "Error" }),
3146
+ /* @__PURE__ */ jsx14(Text13, { color: "red", children: message }),
3147
+ details ? /* @__PURE__ */ jsx14(Text13, { color: "red", dimColor: true, children: details }) : null,
3148
+ hint ? /* @__PURE__ */ jsxs12(Text13, { color: "gray", children: [
3149
+ "Hint: ",
3150
+ hint
3151
+ ] }) : null
3152
+ ]
3153
+ }
3154
+ );
3155
+ };
3156
+ var ErrorMessage_default = ErrorMessage;
3157
+
3158
+ // src/app/ui/App.tsx
3159
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
3067
3160
  var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3068
- const agentInstance = useRef(null);
3069
- const [history, setHistory] = useState4([]);
3070
- const [statusMessage, setStatusMessage] = useState4(
3161
+ const agentInstance = useRef2(null);
3162
+ const [history, setHistory] = useState5([]);
3163
+ const [statusMessage, setStatusMessage] = useState5(
3071
3164
  "Initializing agent..."
3072
3165
  );
3073
- const [toolsCount, setToolsCount] = useState4(null);
3074
- const [mcpStatus, setMcpStatus] = useState4(
3166
+ const [toolsCount, setToolsCount] = useState5(null);
3167
+ const [mcpStatus, setMcpStatus] = useState5(
3075
3168
  "connecting"
3076
3169
  );
3077
- const [isProcessing, setIsProcessing] = useState4(true);
3078
- const [pendingConfirmation, setPendingConfirmation] = useState4(
3170
+ const [isProcessing, setIsProcessing] = useState5(true);
3171
+ const [pendingConfirmation, setPendingConfirmation] = useState5(
3079
3172
  null
3080
3173
  );
3081
- const [confirmationPreview, setConfirmationPreview] = useState4(
3174
+ const [confirmationPreview, setConfirmationPreview] = useState5(
3082
3175
  null
3083
3176
  );
3084
- const alwaysAcceptList = useRef([]);
3177
+ const [isInitAgentActive, setIsInitAgentActive] = useState5(false);
3178
+ const alwaysAcceptList = useRef2([]);
3085
3179
  const workdir = process.cwd();
3086
- const updateCheckRan = useRef(false);
3180
+ const updateCheckRan = useRef2(false);
3087
3181
  const handleInterrupt = useCallback(() => {
3088
3182
  if (!isProcessing) return;
3089
3183
  eventBus2.emit("user_interrupt");
@@ -3092,7 +3186,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3092
3186
  ...prev,
3093
3187
  {
3094
3188
  id: prev.length,
3095
- component: /* @__PURE__ */ jsx14(Text13, { color: "yellow", children: "-- Task cancelled by dev. --" })
3189
+ component: /* @__PURE__ */ jsx15(Text14, { color: "yellow", children: "-- Task cancelled by dev. --" })
3096
3190
  }
3097
3191
  ]);
3098
3192
  }, [isProcessing, eventBus2]);
@@ -3105,16 +3199,22 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3105
3199
  setIsProcessing(false);
3106
3200
  return;
3107
3201
  }
3108
- setIsProcessing(true);
3202
+ if (cmd === "init") {
3203
+ setIsInitAgentActive(true);
3204
+ setIsProcessing(true);
3205
+ } else {
3206
+ setIsProcessing(false);
3207
+ setIsInitAgentActive(false);
3208
+ }
3109
3209
  setHistory((prev) => [
3110
3210
  ...prev,
3111
3211
  {
3112
3212
  id: prev.length,
3113
- component: /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text13, { color: "white", dimColor: true, children: text }) })
3213
+ component: /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { color: "white", dimColor: true, children: text }) })
3114
3214
  },
3115
3215
  {
3116
3216
  id: prev.length + 1,
3117
- component: /* @__PURE__ */ jsx14(
3217
+ component: /* @__PURE__ */ jsx15(
3118
3218
  SlashCommands_default,
3119
3219
  {
3120
3220
  input: text,
@@ -3134,8 +3234,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3134
3234
  id: prev.length,
3135
3235
  component: (
3136
3236
  // Uma única Box para o espaçamento
3137
- /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: "white", dimColor: true, children: [
3138
- /* @__PURE__ */ jsxs12(Text13, { color: "white", children: [
3237
+ /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "white", dimColor: true, children: [
3238
+ /* @__PURE__ */ jsxs13(Text14, { color: "white", children: [
3139
3239
  ">",
3140
3240
  " "
3141
3241
  ] }),
@@ -3169,8 +3269,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3169
3269
  },
3170
3270
  []
3171
3271
  );
3172
- useEffect3(() => {
3173
- setHistory([{ id: 0, component: /* @__PURE__ */ jsx14(Header, {}) }]);
3272
+ useEffect4(() => {
3273
+ setHistory([{ id: 0, component: /* @__PURE__ */ jsx15(Header, {}) }]);
3174
3274
  const initializeAgent = async () => {
3175
3275
  try {
3176
3276
  agentInstance.current = new Agent(sessionId2, eventBus2);
@@ -3190,6 +3290,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3190
3290
  };
3191
3291
  const handleBackendMessage = (parsed) => {
3192
3292
  try {
3293
+ if (parsed.type === "done" || parsed.type === "error") {
3294
+ setIsInitAgentActive(false);
3295
+ }
3193
3296
  if (parsed.type === "connection_status") {
3194
3297
  setStatusMessage(parsed.message);
3195
3298
  return;
@@ -3222,7 +3325,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3222
3325
  if (prev.length < 2) {
3223
3326
  newHistory.push({
3224
3327
  id: 1,
3225
- component: /* @__PURE__ */ jsx14(
3328
+ component: /* @__PURE__ */ jsx15(
3226
3329
  SessionInfo,
3227
3330
  {
3228
3331
  sessionId: sessionId2,
@@ -3243,7 +3346,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3243
3346
  ...prev,
3244
3347
  {
3245
3348
  id: prev.length,
3246
- component: /* @__PURE__ */ jsx14(UpdateNotice_default, { message: msg })
3349
+ component: /* @__PURE__ */ jsx15(UpdateNotice_default, { message: msg })
3247
3350
  }
3248
3351
  ]);
3249
3352
  }
@@ -3257,10 +3360,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3257
3360
  }
3258
3361
  let newComponent = null;
3259
3362
  if (parsed.type === "debug") {
3260
- newComponent = /* @__PURE__ */ jsx14(Text13, { color: "gray", children: parsed.message });
3363
+ newComponent = /* @__PURE__ */ jsx15(Text14, { color: "gray", children: parsed.message });
3261
3364
  } else if (parsed.type === "protocol_violation") {
3262
- newComponent = /* @__PURE__ */ jsxs12(
3263
- Box14,
3365
+ newComponent = /* @__PURE__ */ jsxs13(
3366
+ Box15,
3264
3367
  {
3265
3368
  borderStyle: "round",
3266
3369
  borderColor: "yellow",
@@ -3268,19 +3371,23 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3268
3371
  marginBottom: 1,
3269
3372
  paddingX: 1,
3270
3373
  children: [
3271
- /* @__PURE__ */ jsx14(Text13, { color: "yellow", bold: true, children: "Protocol Violation" }),
3272
- /* @__PURE__ */ jsx14(Text13, { color: "gray", children: parsed.content }),
3273
- /* @__PURE__ */ jsx14(Text13, { color: "yellow", children: parsed.message })
3374
+ /* @__PURE__ */ jsx15(Text14, { color: "yellow", bold: true, children: "Protocol Violation" }),
3375
+ /* @__PURE__ */ jsx15(Text14, { color: "gray", children: parsed.content }),
3376
+ /* @__PURE__ */ jsx15(Text14, { color: "yellow", children: parsed.message })
3274
3377
  ]
3275
3378
  }
3276
3379
  );
3277
3380
  } else if (parsed.type === "error") {
3278
- newComponent = /* @__PURE__ */ jsxs12(Text13, { color: "red", children: [
3279
- "\u274C ",
3280
- parsed.message
3281
- ] });
3381
+ newComponent = /* @__PURE__ */ jsx15(
3382
+ ErrorMessage_default,
3383
+ {
3384
+ message: parsed.message,
3385
+ details: parsed.details || void 0,
3386
+ hint: parsed.hint || void 0
3387
+ }
3388
+ );
3282
3389
  } else if (parsed.type === "tool_call") {
3283
- newComponent = /* @__PURE__ */ jsx14(
3390
+ newComponent = /* @__PURE__ */ jsx15(
3284
3391
  ToolCallDisplay,
3285
3392
  {
3286
3393
  toolName: parsed.tool_name,
@@ -3289,23 +3396,23 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3289
3396
  }
3290
3397
  );
3291
3398
  } else if (parsed.type === "tool_result") {
3292
- newComponent = /* @__PURE__ */ jsx14(
3399
+ newComponent = /* @__PURE__ */ jsx15(
3293
3400
  ToolResultDisplay,
3294
3401
  {
3295
3402
  toolName: parsed.tool_name,
3296
3403
  result: parsed.result
3297
3404
  }
3298
3405
  );
3299
- } else if (parsed.type === "dev_overlay") {
3300
- newComponent = /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: "gray", children: [
3301
- /* @__PURE__ */ jsxs12(Text13, { color: "blue", children: [
3406
+ } else if (parsed.type === "user_overlay") {
3407
+ newComponent = /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
3408
+ /* @__PURE__ */ jsxs13(Text14, { color: "blue", children: [
3302
3409
  ">",
3303
3410
  " "
3304
3411
  ] }),
3305
3412
  parsed.payload
3306
3413
  ] }) });
3307
3414
  } else if (parsed.type === "log") {
3308
- newComponent = /* @__PURE__ */ jsxs12(Text13, { color: "gray", children: [
3415
+ newComponent = /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
3309
3416
  "\u2139\uFE0F ",
3310
3417
  parsed.message,
3311
3418
  parsed.payload ? `: ${parsed.payload}` : ""
@@ -3323,19 +3430,19 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3323
3430
  }
3324
3431
  };
3325
3432
  const handleUiOverlay = (data) => {
3326
- eventBus2.emit("dev_overlay", data);
3433
+ eventBus2.emit("user_overlay", data);
3327
3434
  };
3328
- uiEventBus.on("dev_overlay", handleUiOverlay);
3435
+ uiEventBus.on("user_overlay", handleUiOverlay);
3329
3436
  eventBus2.on("backend_message", handleBackendMessage);
3330
3437
  initializeAgent();
3331
3438
  return () => {
3332
- uiEventBus.off("dev_overlay", handleUiOverlay);
3439
+ uiEventBus.off("user_overlay", handleUiOverlay);
3333
3440
  eventBus2.off("backend_message", handleBackendMessage);
3334
3441
  };
3335
3442
  }, [eventBus2, sessionId2, handleConfirmation]);
3336
3443
  const renderInteractiveComponent = () => {
3337
3444
  if (mcpStatus !== "connected") {
3338
- return /* @__PURE__ */ jsx14(Box14, { borderStyle: "round", borderColor: "black", children: /* @__PURE__ */ jsx14(
3445
+ return /* @__PURE__ */ jsx15(Box15, { borderStyle: "round", borderColor: "black", children: /* @__PURE__ */ jsx15(
3339
3446
  SessionInfoConnectingMCP_default,
3340
3447
  {
3341
3448
  sessionId: sessionId2,
@@ -3345,7 +3452,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3345
3452
  ) });
3346
3453
  }
3347
3454
  if (pendingConfirmation) {
3348
- return /* @__PURE__ */ jsx14(
3455
+ return /* @__PURE__ */ jsx15(
3349
3456
  ConfirmationPrompt,
3350
3457
  {
3351
3458
  toolCalls: pendingConfirmation,
@@ -3357,20 +3464,21 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
3357
3464
  }
3358
3465
  );
3359
3466
  }
3360
- return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", children: [
3361
- isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx14(WorkingTimer, {}),
3362
- /* @__PURE__ */ jsx14(
3467
+ return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
3468
+ isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx15(WorkingTimer, {}),
3469
+ /* @__PURE__ */ jsx15(
3363
3470
  InputPrompt,
3364
3471
  {
3365
3472
  onSubmit: handleSubmit,
3366
- isReadOnly: isProcessing,
3473
+ isReadOnly: isProcessing || isInitAgentActive,
3474
+ disableWhileProcessing: isInitAgentActive,
3367
3475
  onInterrupt: handleInterrupt
3368
3476
  }
3369
3477
  )
3370
3478
  ] });
3371
3479
  };
3372
- return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", children: [
3373
- /* @__PURE__ */ jsx14(Static, { items: history, children: (item) => /* @__PURE__ */ jsx14(Box14, { children: item.component }, item.id) }),
3480
+ return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
3481
+ /* @__PURE__ */ jsx15(Static, { items: history, children: (item) => /* @__PURE__ */ jsx15(Box15, { children: item.component }, item.id) }),
3374
3482
  renderInteractiveComponent()
3375
3483
  ] });
3376
3484
  };