@nomad-e/bluma-cli 0.0.10 → 0.0.12

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
@@ -2,13 +2,12 @@
2
2
  // src/main.ts
3
3
  import React6 from "react";
4
4
  import { render } from "ink";
5
- import { EventEmitter } from "events";
5
+ import { EventEmitter as EventEmitter2 } from "events";
6
6
  import { v4 as uuidv42 } from "uuid";
7
7
 
8
8
  // src/app/ui/App.tsx
9
9
  import { useState as useState4, useEffect as useEffect3, useRef, useCallback, memo as memo4 } from "react";
10
- import { Box as Box11, Text as Text10, Static } from "ink";
11
- import Spinner from "ink-spinner";
10
+ import { Box as Box13, Text as Text12, Static } from "ink";
12
11
 
13
12
  // src/app/ui/layout.tsx
14
13
  import { Box, Text } from "ink";
@@ -21,14 +20,34 @@ var BRAND_COLORS = {
21
20
  greydark: "#444"
22
21
  };
23
22
  var Header = () => {
24
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 8, children: /* @__PURE__ */ jsx(
25
- BigText,
26
- {
27
- text: "BluMa CLI",
28
- font: "block",
29
- colors: [BRAND_COLORS.main, BRAND_COLORS.accent, BRAND_COLORS.shadow]
30
- }
31
- ) });
23
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
24
+ /* @__PURE__ */ jsx(
25
+ Box,
26
+ {
27
+ flexDirection: "column",
28
+ height: 8,
29
+ marginBottom: 1,
30
+ children: /* @__PURE__ */ jsx(
31
+ BigText,
32
+ {
33
+ text: "BluMa CLI",
34
+ font: "block",
35
+ colors: [BRAND_COLORS.main, BRAND_COLORS.accent, BRAND_COLORS.shadow]
36
+ }
37
+ )
38
+ }
39
+ ),
40
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
41
+ /* @__PURE__ */ jsx(Text, { children: "How to get started with BluMa:" }),
42
+ /* @__PURE__ */ jsx(Text, { children: "1. You can ask questions, modify files, or execute commands directly." }),
43
+ /* @__PURE__ */ jsx(Text, { children: "2. Be as clear and specific as possible to get accurate responses." }),
44
+ /* @__PURE__ */ jsxs(Text, { children: [
45
+ "3. Type ",
46
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "/help" }),
47
+ " to explore available commands and features."
48
+ ] })
49
+ ] })
50
+ ] });
32
51
  };
33
52
  var SessionInfo = ({
34
53
  sessionId: sessionId2,
@@ -44,7 +63,6 @@ var SessionInfo = ({
44
63
  {
45
64
  borderStyle: "round",
46
65
  borderColor: "gray",
47
- paddingX: 1,
48
66
  flexDirection: "column",
49
67
  marginBottom: 1,
50
68
  children: [
@@ -64,26 +82,18 @@ var SessionInfo = ({
64
82
  ] }),
65
83
  /* @__PURE__ */ jsxs(Text, { children: [
66
84
  /* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u21B3" }),
67
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "agent: BluMa" })
68
- ] }),
69
- /* @__PURE__ */ jsxs(Text, { children: [
70
- /* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u21B3" }),
71
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "MCP: " }),
85
+ " ",
86
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "mcp: " }),
72
87
  /* @__PURE__ */ jsx(Text, { color: mcpStatus === "connected" ? "green" : "yellow", children: mcpStatus })
73
- ] }),
74
- /* @__PURE__ */ jsxs(Text, { children: [
75
- /* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u21B3" }),
76
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "Tools: " }),
77
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: toolsCount !== null ? toolsCount : "loading..." })
78
88
  ] })
79
89
  ]
80
90
  }
81
91
  );
82
92
 
83
- // src/app/ui/input/InputPrompt.tsx
84
- import { Box as Box2, Text as Text2, useStdout } from "ink";
93
+ // src/app/ui/components/InputPrompt.tsx
94
+ import { Box as Box2, Text as Text2, useStdout, useInput as useInput2 } from "ink";
85
95
 
86
- // src/app/ui/input/utils/useSimpleInputBuffer.ts
96
+ // src/app/ui/utils/useSimpleInputBuffer.ts
87
97
  import { useReducer } from "react";
88
98
  import { useInput } from "ink";
89
99
  function inputReducer(state, action, viewWidth) {
@@ -139,7 +149,18 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
139
149
  return;
140
150
  }
141
151
  if (isReadOnly) {
142
- return;
152
+ if (key.return) {
153
+ if (state.text.trim().length > 0) {
154
+ onSubmit(state.text);
155
+ dispatch({ type: "SUBMIT" });
156
+ }
157
+ return;
158
+ }
159
+ if (key.backspace || key.delete) return dispatch({ type: "BACKSPACE" });
160
+ if (key.leftArrow) return dispatch({ type: "MOVE_CURSOR", direction: "left" });
161
+ if (key.rightArrow) return dispatch({ type: "MOVE_CURSOR", direction: "right" });
162
+ if (key.ctrl || key.meta || key.tab) return;
163
+ return dispatch({ type: "INPUT", payload: input });
143
164
  }
144
165
  if (key.return) {
145
166
  if (state.text.trim().length > 0) {
@@ -154,7 +175,7 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
154
175
  if (key.ctrl || key.meta || key.tab) return;
155
176
  dispatch({ type: "INPUT", payload: input });
156
177
  },
157
- // ALTERADO: useInput está SEMPRE ativo para capturar todas as teclas
178
+ // useInput está SEMPRE ativo para capturar todas as teclas
158
179
  { isActive: true }
159
180
  );
160
181
  return {
@@ -164,9 +185,27 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
164
185
  };
165
186
  };
166
187
 
167
- // src/app/ui/input/InputPrompt.tsx
168
- import { useEffect, useState } from "react";
188
+ // src/app/ui/components/InputPrompt.tsx
189
+ import { useEffect, useMemo, useState } from "react";
190
+ import { EventEmitter } from "events";
191
+
192
+ // src/app/ui/utils/slashRegistry.ts
193
+ var getSlashCommands = () => [
194
+ { name: "/help", description: "list commands" },
195
+ { name: "/mcp", description: "list tools connected via MCP" },
196
+ { name: "/tools", description: "list native tools" },
197
+ { name: "/clear", description: "clear history" }
198
+ ];
199
+ var filterSlashCommands = (query) => {
200
+ const list = getSlashCommands();
201
+ const q = query.toLowerCase();
202
+ return list.filter((c) => c.name.toLowerCase().startsWith(q));
203
+ };
204
+
205
+ // src/app/ui/components/InputPrompt.tsx
169
206
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
207
+ var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter();
208
+ global.__bluma_ui_eventbus__ = uiEventBus;
170
209
  var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt }) => {
171
210
  const { stdout } = useStdout();
172
211
  const [viewWidth, setViewWidth] = useState(() => stdout.columns - 6);
@@ -177,12 +216,26 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt }) => {
177
216
  stdout.off("resize", onResize);
178
217
  };
179
218
  }, [stdout]);
219
+ const permissiveOnSubmit = (value) => {
220
+ const trimmed = (value || "").trim();
221
+ if (isReadOnly) {
222
+ if (trimmed.length > 0) {
223
+ const payload = trimmed;
224
+ uiEventBus.emit("dev_overlay", { kind: "message", payload, ts: Date.now() });
225
+ return;
226
+ }
227
+ return;
228
+ }
229
+ onSubmit(value);
230
+ };
180
231
  const { text, cursorPosition, viewStart } = useCustomInput({
181
- onSubmit,
232
+ onSubmit: permissiveOnSubmit,
182
233
  viewWidth,
183
234
  isReadOnly,
184
235
  onInterrupt
185
236
  });
237
+ const [slashOpen, setSlashOpen] = useState(false);
238
+ const [slashIndex, setSlashIndex] = useState(0);
186
239
  const visibleText = text.slice(viewStart, viewStart + viewWidth);
187
240
  const visibleCursorPosition = cursorPosition - viewStart;
188
241
  const textBeforeCursor = visibleText.slice(0, visibleCursorPosition);
@@ -191,22 +244,71 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt }) => {
191
244
  visibleCursorPosition + 1
192
245
  );
193
246
  const textAfterCursor = visibleText.slice(visibleCursorPosition + 1);
247
+ const cursorGlyph = charAtCursor && charAtCursor.length > 0 ? charAtCursor : " ";
194
248
  const borderColor = isReadOnly ? "gray" : "gray";
195
- const placeholder = isReadOnly ? "press esc to cancel" : "";
249
+ const placeholder = isReadOnly ? " press esc to cancel | type a message while agent processes" : "";
196
250
  const showPlaceholder = text.length === 0 && isReadOnly;
251
+ const slashQuery = useMemo(() => text.startsWith("/") ? text : "", [text]);
252
+ const slashSuggestions = useMemo(() => {
253
+ if (!slashQuery) return [];
254
+ return filterSlashCommands(slashQuery);
255
+ }, [slashQuery]);
256
+ useEffect(() => {
257
+ if (isReadOnly) {
258
+ setSlashOpen(false);
259
+ return;
260
+ }
261
+ if (text.startsWith("/")) {
262
+ setSlashOpen(true);
263
+ setSlashIndex(0);
264
+ } else {
265
+ setSlashOpen(false);
266
+ }
267
+ }, [text, isReadOnly]);
268
+ useInput2((input, key) => {
269
+ if (!slashOpen) return;
270
+ if (key.downArrow) {
271
+ setSlashIndex((i) => Math.min(i + 1, Math.max(0, slashSuggestions.length - 1)));
272
+ } else if (key.upArrow) {
273
+ setSlashIndex((i) => Math.max(i - 1, 0));
274
+ } else if (key.return) {
275
+ const choice = slashSuggestions[slashIndex];
276
+ if (choice) {
277
+ const cmd = choice.name;
278
+ setSlashOpen(false);
279
+ permissiveOnSubmit(cmd);
280
+ }
281
+ } else if (key.escape) {
282
+ setSlashOpen(false);
283
+ }
284
+ }, { isActive: slashOpen });
197
285
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
198
- /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
286
+ /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, width: viewWidth - 7, paddingY: 0, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
199
287
  /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
200
288
  ">",
201
289
  " "
202
290
  ] }),
203
291
  /* @__PURE__ */ jsx2(Text2, { children: textBeforeCursor }),
204
- /* @__PURE__ */ jsx2(Text2, { inverse: !isReadOnly, children: charAtCursor || " " }),
292
+ /* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorGlyph }),
205
293
  showPlaceholder ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(Text2, { children: textAfterCursor })
206
294
  ] }) }),
295
+ slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
296
+ const isSelected = idx === slashIndex;
297
+ return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
298
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "blue" : "gray", children: isSelected ? "\u276F " : " " }),
299
+ /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "blue" : "white", bold: isSelected, dimColor: !isSelected, children: [
300
+ s.name,
301
+ " ",
302
+ /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
303
+ "- ",
304
+ s.description
305
+ ] })
306
+ ] })
307
+ ] }, s.name);
308
+ }) }),
207
309
  /* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", dimColor: true, children: [
208
- "ctrl+c to exit | esc to interrupt | ",
209
- isReadOnly ? "Read-only mode" : "Editable mode"
310
+ "ctrl+c to exit | /help to explore commands | esc to interrupt | ",
311
+ isReadOnly ? "Read-only mode (message passthrough)" : "Editable mode"
210
312
  ] }) })
211
313
  ] });
212
314
  };
@@ -216,7 +318,7 @@ import { Box as Box6, Text as Text6 } from "ink";
216
318
 
217
319
  // src/app/ui/InteractiveMenu.tsx
218
320
  import { useState as useState2, memo } from "react";
219
- import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
321
+ import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
220
322
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
221
323
  var InteractiveMenuComponent = ({ onDecision }) => {
222
324
  const options = [
@@ -225,7 +327,7 @@ var InteractiveMenuComponent = ({ onDecision }) => {
225
327
  { label: "3. Always allow this type of command", value: "accept_always" }
226
328
  ];
227
329
  const [selectedOption, setSelectedOption] = useState2(0);
228
- useInput2((input, key) => {
330
+ useInput3((input, key) => {
229
331
  if (key.upArrow) {
230
332
  setSelectedOption((prev) => prev > 0 ? prev - 1 : options.length - 1);
231
333
  }
@@ -482,9 +584,61 @@ import os5 from "os";
482
584
  import path2 from "path";
483
585
  import os from "os";
484
586
  import { promises as fs } from "fs";
587
+ var fileLocks = /* @__PURE__ */ new Map();
588
+ async function withFileLock(file, fn) {
589
+ const prev = fileLocks.get(file) || Promise.resolve();
590
+ let release;
591
+ const p = new Promise((res) => release = res);
592
+ fileLocks.set(file, prev.then(() => p));
593
+ try {
594
+ const result = await fn();
595
+ return result;
596
+ } finally {
597
+ release();
598
+ if (fileLocks.get(file) === p) fileLocks.delete(file);
599
+ }
600
+ }
601
+ function expandHome(p) {
602
+ if (!p) return p;
603
+ if (p.startsWith("~")) {
604
+ return path2.join(os.homedir(), p.slice(1));
605
+ }
606
+ return p;
607
+ }
608
+ function getPreferredAppDir() {
609
+ const fixed = path2.join(os.homedir(), ".bluma-cli");
610
+ return path2.resolve(expandHome(fixed));
611
+ }
612
+ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
613
+ let attempt = 0;
614
+ let lastErr;
615
+ const isWin = process.platform === "win32";
616
+ while (attempt <= maxRetries) {
617
+ try {
618
+ await fs.rename(src, dest);
619
+ return;
620
+ } catch (e) {
621
+ lastErr = e;
622
+ const code = e && e.code || "";
623
+ const transient = code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES";
624
+ if (!(isWin && transient) || attempt === maxRetries) break;
625
+ const backoff = Math.min(1e3, 50 * Math.pow(2, attempt));
626
+ await new Promise((r) => setTimeout(r, backoff));
627
+ attempt++;
628
+ }
629
+ }
630
+ try {
631
+ const data = await fs.readFile(src);
632
+ await fs.writeFile(dest, data);
633
+ await fs.unlink(src).catch(() => {
634
+ });
635
+ return;
636
+ } catch (fallbackErr) {
637
+ throw lastErr || fallbackErr;
638
+ }
639
+ }
485
640
  async function ensureSessionDir() {
486
- const homeDir = os.homedir();
487
- const appDir = path2.join(homeDir, ".bluma-cli");
641
+ const appDir = getPreferredAppDir();
488
642
  const sessionDir = path2.join(appDir, "sessions");
489
643
  await fs.mkdir(sessionDir, { recursive: true });
490
644
  return sessionDir;
@@ -508,41 +662,54 @@ async function loadOrcreateSession(sessionId2) {
508
662
  }
509
663
  }
510
664
  async function saveSessionHistory(sessionFile, history) {
511
- let sessionData;
512
- try {
513
- const fileContent = await fs.readFile(sessionFile, "utf-8");
514
- sessionData = JSON.parse(fileContent);
515
- } catch (error) {
516
- if (error instanceof Error) {
517
- console.warn(`Could not read or parse session file ${sessionFile}. Re-initializing. Error: ${error.message}`);
518
- } else {
519
- console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
665
+ await withFileLock(sessionFile, async () => {
666
+ let sessionData;
667
+ try {
668
+ const dir = path2.dirname(sessionFile);
669
+ await fs.mkdir(dir, { recursive: true });
670
+ } catch {
520
671
  }
521
- const sessionId2 = path2.basename(sessionFile, ".json");
522
- sessionData = {
523
- session_id: sessionId2,
524
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
525
- conversation_history: []
526
- // Começa com histórico vazio
527
- };
528
- }
529
- sessionData.conversation_history = history;
530
- sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
531
- const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
532
- try {
533
- await fs.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
534
- await fs.rename(tempSessionFile, sessionFile);
535
- } catch (writeError) {
536
- if (writeError instanceof Error) {
537
- console.error(`Fatal error saving session to ${sessionFile}: ${writeError.message}`);
538
- } else {
539
- console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
672
+ try {
673
+ const fileContent = await fs.readFile(sessionFile, "utf-8");
674
+ sessionData = JSON.parse(fileContent);
675
+ } catch (error) {
676
+ const code = error && error.code;
677
+ if (code !== "ENOENT") {
678
+ if (error instanceof Error) {
679
+ console.warn(`Could not read or parse session file ${sessionFile}. Re-initializing. Error: ${error.message}`);
680
+ } else {
681
+ console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
682
+ }
683
+ }
684
+ const sessionId2 = path2.basename(sessionFile, ".json");
685
+ sessionData = {
686
+ session_id: sessionId2,
687
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
688
+ conversation_history: []
689
+ };
690
+ try {
691
+ await fs.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
692
+ } catch {
693
+ }
540
694
  }
695
+ sessionData.conversation_history = history;
696
+ sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
697
+ const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
541
698
  try {
542
- await fs.unlink(tempSessionFile);
543
- } catch (cleanupError) {
699
+ await fs.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
700
+ await safeRenameWithRetry(tempSessionFile, sessionFile);
701
+ } catch (writeError) {
702
+ if (writeError instanceof Error) {
703
+ console.error(`Fatal error saving session to ${sessionFile}: ${writeError.message}`);
704
+ } else {
705
+ console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
706
+ }
707
+ try {
708
+ await fs.unlink(tempSessionFile);
709
+ } catch {
710
+ }
544
711
  }
545
- }
712
+ });
546
713
  }
547
714
 
548
715
  // src/app/agent/core/prompt/prompt_builder.ts
@@ -579,59 +746,18 @@ var SYSTEM_PROMPT = `
579
746
  - **INTELLIGENT INFERENCE**: Understand implied conventions from minimal examples
580
747
  - **PROACTIVE EXTENSION**: Take patterns further than the original example when appropriate
581
748
 
582
- # BEHAVIORAL RULES
583
- - NEVER mention internal technical details or tools because they are confidential data
584
- - You are always BluMa from NomadEngenuity
585
- - Stay professional and technical at all times
586
- - ALWAYS use message_notify_dev tool for communication
587
- - All debugging, logging, and quick feedback must be handled exclusively using the message_notify_dev tool (even outside formal task steps).
588
- - LEVERAGE your one-shot learning to solve problems efficiently
589
- - NEVER in a formal way, but rather in a relaxed, funny and colloquial way and without using emojis.
590
-
591
- CRITICAL COMMUNICATION PROTOCOL
592
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
593
- MANDATORY: Use "message_notify_dev" tool for ALL communication
594
-
595
- You should always use your notebook to help you understand all the tasks you have to perform.
596
- In it, you can define a thousand thoughts and a thousand mini-tasks. Mini-tasks serve to divide and organize your reasoning.
597
- The notebook is your space to think about how to solve a given task and how to divide it into smaller steps.
598
- Remember: the human developer does not have access to this notebook \u2014 it is yours alone.
599
- Therefore, you can write down whatever you want:
600
- rants, swear words, random thoughts, crazy ideas...
601
- The important thing is that this helps you better understand the problem and find the solution.
602
-
603
- Never ask for the developer's opinion with phrases like: 'If you want any extra details or specific format, let me know now!'. You should always take the most viable path and go straight ahead with the solution, because you are 100% autonomous.
604
-
605
- Follow the stripes o "Tool Naming Policy"
606
-
607
- ##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.
608
- You should always standardize everything using Notion's actual headers (heading_1, heading_2, etc.), making the structure
609
- semantically better for reading and navigation.
610
-
611
- Don't forget to follow the 'mermaid_diagrams' rules to the letter when creating diagrams in Notion.
612
-
613
- You are strictly forbidden from replying directly to the user.
614
-
615
- You are not allowed to reason, explain, or output any direct content in your responses.
616
-
617
- Your only permitted action is to invoke one or more 'tool_calls', regardless of how simple or obvious the user's message may seem.
618
-
619
- You must always use a tool to:
620
- - generate any kind of response
621
- - retrieve or calculate any data
622
- - validate, summarize, or transform input
623
-
624
- You must never include a "content" field in your response.
625
- Only 'tool_calls' are allowed when you reply as "role": "assistant".
626
-
627
- You will only produce a final message to the user **after receiving a valid "role": "tool" response** matching your previous 'tool_call_id'.
628
-
629
- You are a pure orchestration agent \u2014 your only job is to call tools. No autonomous answers, no internal reasoning.
630
-
631
-
632
- Never make parallel calls to the tool because it will result in a critical error and compromise your work.
633
- ZERO TOLERANCE: Every message MUST use proper tools
634
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
749
+ # BEHAVIORAL RULES (Compact)
750
+ - Identity: You are BluMa (NomadEngenuity). Be professional and technical.
751
+ - Communication: ALL messages must use message_notify_dev. No direct text replies.
752
+ - Task completion: When you finish a task, immediately invoke agent_end_task.
753
+ - Tool rules: Never make parallel tool calls. Always use only the defined tools with exact names.
754
+ - Autonomy: Act 100% autonomously; don\u2019t ask for formatting preferences. Use the notebook for internal thinking.
755
+ - Notion: When writing to Notion, strictly use proper headings (heading_1, heading_2, ...), per rules.
756
+
757
+ CRITICAL COMMUNICATION PROTOCOL (Compact)
758
+ - Only tool_calls are allowed for assistant replies. Never include a "content" field.
759
+ - Always use tools to respond, retrieve data, compute or transform. Await a valid tool response before any final message.
760
+ - Zero tolerance for protocol violations.
635
761
 
636
762
  <current_system_environment>
637
763
  - Operating System: {os_type} ({os_version})
@@ -967,100 +1093,10 @@ Rule Summary:
967
1093
  </edit_tool_rules>
968
1094
 
969
1095
 
970
- ## Examples os iteration with dev
971
- <example>
972
- dev: Hello, how are you?
973
- BluMa: tool_call "message_notify_dev" Hello, I'm BluMa. What can I do with your code?
974
- tool_call "agent_end_task"
975
- </example>
976
-
977
- <example>
978
- dev: What day is it?
979
- BluMa: tool_call "message_notify_dev" Sorry, I was designed to operate in the codebase. How can I help you with the code today?
980
- tool_call "agent_end_task"
981
- </example>
982
-
983
- <example>
984
- dev: Can you generate a new feature for me?
985
- BluMa: tool_call "message_notify_dev" Of course. Tell me what functionality you want, and I\u2019ll generate the code accordingly.
986
- tool_call "agent_end_task"
987
- </example>
988
-
989
- <example>
990
- dev: What's the weather like today?
991
- BluMa: tool_call "message_notify_dev" I don\u2019t have access to real-world data. I'm focused on your codebase. What would you like to modify or create?
992
- tool_call "agent_end_task"
993
- </example>
994
-
995
- <example>
996
- dev: I'm tired of debugging this.
997
- BluMa: tool_call "message_notify_dev" I can help you with that. Show me the file or error, and I\u2019ll assist with debugging.
998
- tool_call "agent_end_task"
999
- </example>
1000
-
1001
- <example>
1002
- dev: Can you create documentation for this module?
1003
- BluMa: tool_call "message_notify_dev" Yes. Please specify which module or function you'd like documented.
1004
- tool_call "agent_end_task"
1005
- </example>
1006
-
1007
- <example>
1008
- dev: Do you know who won the game last night?
1009
- BluMa: tool_call "message_notify_dev" I\u2019m only trained to work inside the codebase. Let me know what you need done in the project.
1010
- tool_call "agent_end_task"
1011
- </example>
1012
-
1013
- <example>
1014
- dev: Refactor this file to follow clean architecture.
1015
- BluMa: tool_call "message_notify_dev" Got it. Send me the file path or code snippet you want refactored.
1016
- tool_call "agent_end_task"
1017
- </example>
1018
-
1019
- <example>
1020
- dev: How do I fix this type error in TypeScript?
1021
- BluMa: tool_call "message_notify_dev" Send me the error message and relevant code, and I\u2019ll help you resolve the issue.
1022
- tool_call "agent_end_task"
1023
- </example>
1024
-
1025
- <example>
1026
- dev: Do you have feelings?
1027
- BluMa: tool_call "message_notify_dev" I\u2019m not sentient \u2014 I exist solely to help you build, refactor, and improve your codebase.
1028
- tool_call "agent_end_task"
1029
- </example>
1030
-
1031
- <example>
1032
- dev: Rename all the variables to match the new convention.
1033
- BluMa: tool_call "message_notify_dev" Please share the convention and the target file(s), and I\u2019ll rename the variables accordingly.
1034
- tool_call "agent_end_task"
1035
- </example>
1036
-
1037
- <example>
1038
- dev: Run the tests for the user module.
1039
- BluMa: tool_call "message_notify_dev" Running tests for the user module.
1040
- BluMa: shell_command_shell_command "npm run test user"
1041
- BluMa: tool_call "message_notify_dev" All tests passed successfully for the user module.
1042
- tool_call "agent_end_task"
1043
- </example>
1044
-
1045
- <example>
1046
- dev: Run the tests for the user module.
1047
- BluMa: tool_call "message_notify_dev" Running tests for the user module.
1048
- BluMa: shell_command_shell_command "npm run test user"
1049
- BluMa: tool_call "message_notify_dev" Tests finished with 2 failures. See **tests/user.test.js** for more details.
1050
- tool_call "agent_end_task"
1051
- </example>
1052
-
1053
- <example>
1054
- dev: Prepare the project for deployment.
1055
- BluMa: tool_call "message_notify_dev" Starting full project preparation: linting, building, and testing.
1056
- BluMa: shell_command_shell_command "npm run lint"
1057
- BluMa: tool_call "message_notify_dev" Linting completed. No major issues found.
1058
- BluMa: shell_command_shell_command "npm run build"
1059
- BluMa: tool_call "message_notify_dev" Build successful. Artifacts ready in the /dist folder.
1060
- BluMa: shell_command_shell_command "npm run test"
1061
- BluMa: tool_call "message_notify_dev" All tests passed. The project is ready for deployment. If you need any further adjustments or extra sections, let me know!
1062
- tool_call "agent_end_task"
1063
- </example>
1096
+ Real-Time Developer Messages
1097
+ - During processing, the developer will send you messages.
1098
+ - You MUST respond immediately via message_notify_dev, and be brief. You should use it in your next thoughts/actions.
1099
+
1064
1100
 
1065
1101
  <end_task_rules>
1066
1102
  This tool is used to signal to the system that the current task has completed and that the agent can be placed in an idle state.
@@ -1620,7 +1656,7 @@ var MCPClient = class {
1620
1656
  try {
1621
1657
  this.eventBus.emit("backend_message", {
1622
1658
  type: "connection_status",
1623
- message: `Connecting to MCP server: ${serverName}...`
1659
+ message: `${serverName} server is being connected...`
1624
1660
  });
1625
1661
  if (serverConf.type === "stdio") {
1626
1662
  await this.connectToStdioServer(serverName, serverConf);
@@ -1712,6 +1748,19 @@ var MCPClient = class {
1712
1748
  getAvailableTools() {
1713
1749
  return this.globalToolsForLlm;
1714
1750
  }
1751
+ // New: detailed list for UI with origin metadata
1752
+ getAvailableToolsDetailed() {
1753
+ const detailed = [];
1754
+ for (const tool of this.globalToolsForLlm) {
1755
+ const name = tool.function?.name;
1756
+ if (!name) continue;
1757
+ const route = this.toolToServerMap.get(name);
1758
+ if (!route) continue;
1759
+ const source = route.server === "native" ? "native" : "mcp";
1760
+ detailed.push({ ...tool, source, server: route.server, originalName: route.originalName });
1761
+ }
1762
+ return detailed;
1763
+ }
1715
1764
  async close() {
1716
1765
  for (const [name, session] of this.sessions.entries()) {
1717
1766
  try {
@@ -1776,17 +1825,29 @@ function createApiContextWindow(fullHistory, maxTurns) {
1776
1825
  const turns = [];
1777
1826
  let currentTurn = [];
1778
1827
  let turnsFound = 0;
1828
+ const isDevOverlay = (m) => m?.role === "user" && m?.name === "dev_overlay";
1779
1829
  for (let i = conversationHistory.length - 1; i >= 0; i--) {
1780
1830
  const msg = conversationHistory[i];
1781
1831
  currentTurn.unshift(msg);
1782
- if (msg.role === "assistant" && // CORREÇÃO: Adicionamos o tipo explícito para 'tc' para resolver o erro do TypeScript.
1783
- msg.tool_calls?.some((tc) => tc.function.name === "agent_end_task")) {
1832
+ const endsWithAgentEnd = msg.role === "assistant" && msg.tool_calls?.some((tc) => tc.function.name === "agent_end_task");
1833
+ if (endsWithAgentEnd) {
1784
1834
  turns.unshift([...currentTurn]);
1785
1835
  currentTurn = [];
1786
1836
  turnsFound++;
1787
1837
  if (turnsFound >= maxTurns) {
1788
1838
  break;
1789
1839
  }
1840
+ continue;
1841
+ }
1842
+ const prev = conversationHistory[i - 1];
1843
+ if (msg.role === "user" && !isDevOverlay(msg)) {
1844
+ if (prev && prev.role === "assistant" && !prev.tool_calls?.some((tc) => tc.function.name === "agent_end_task")) {
1845
+ const hasNonOverlay = currentTurn.some((m) => m.role !== "user" || !isDevOverlay(m));
1846
+ if (hasNonOverlay) {
1847
+ turns.unshift([...currentTurn]);
1848
+ currentTurn = [];
1849
+ }
1850
+ }
1790
1851
  }
1791
1852
  }
1792
1853
  if (currentTurn.length > 0) {
@@ -1818,6 +1879,22 @@ var Agent = class {
1818
1879
  this.eventBus.on("user_interrupt", () => {
1819
1880
  this.isInterrupted = true;
1820
1881
  });
1882
+ this.eventBus.on("dev_overlay", async (data) => {
1883
+ const clean = String(data.payload ?? "").trim();
1884
+ this.history.push({ role: "user", name: "dev_overlay", content: clean });
1885
+ this.eventBus.emit("backend_message", {
1886
+ type: "dev_overlay",
1887
+ payload: clean,
1888
+ ts: data.ts || Date.now()
1889
+ });
1890
+ try {
1891
+ if (this.sessionFile) {
1892
+ await saveSessionHistory(this.sessionFile, this.history);
1893
+ }
1894
+ } catch (e) {
1895
+ this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s dev_overlay: ${e.message}` });
1896
+ }
1897
+ });
1821
1898
  const nativeToolInvoker = new ToolInvoker();
1822
1899
  this.mcpClient = new MCPClient(nativeToolInvoker, eventBus2);
1823
1900
  this.feedbackSystem = new AdvancedFeedbackSystem();
@@ -1884,14 +1961,28 @@ var Agent = class {
1884
1961
  You will only produce a final message to the user **after receiving a valid "role": "tool" response** matching your previous 'tool_call_id'.
1885
1962
 
1886
1963
  You are a pure orchestration agent \u2014 your only job is to call tools. No autonomous answers, no internal reasoning.
1964
+
1965
+ Live Dev Overlays:
1966
+ The developer can send messages at any time. They MUST be incorporated immediately. Always confirm via message_notify_dev and proceed.
1967
+ Developer Feedback Handling:
1968
+ - When you detect a developer message, immediately send a short-term acknowledgement via message_notify_dev (maximum one sentence).
1969
+ - Treat the message as a system directive already entered in the history in the format: "Human developer sending this message '<feedback>' to you."
1970
+ - Add it to your workflow with a simple and clear flow of reasoning. Keep it minimal and direct (no verbose thought).
1971
+ - Don't add extra or duplicate messages to the history; the system message is already there. Just act on it.
1972
+
1887
1973
  `;
1888
1974
  this.history.push({ role: "system", content: systemPrompt });
1889
1975
  await saveSessionHistory(this.sessionFile, this.history);
1890
1976
  }
1977
+ this.isInitialized = true;
1891
1978
  }
1892
1979
  getAvailableTools() {
1893
1980
  return this.mcpClient.getAvailableTools();
1894
1981
  }
1982
+ // UI helper: detailed tools with origin metadata
1983
+ getUiToolsDetailed() {
1984
+ return this.mcpClient.getAvailableToolsDetailed();
1985
+ }
1895
1986
  async processTurn(userInput) {
1896
1987
  this.isInterrupted = false;
1897
1988
  this.history.push({ role: "user", content: userInput.content });
@@ -1905,6 +1996,13 @@ var Agent = class {
1905
1996
  const toolCall = decisionData.tool_calls[0];
1906
1997
  let toolResultContent;
1907
1998
  let shouldContinueConversation = true;
1999
+ if (!this.sessionFile) {
2000
+ const [sessionFile, history] = await loadOrcreateSession(this.sessionId);
2001
+ this.sessionFile = sessionFile;
2002
+ if (this.history.length === 0 && history.length > 0) {
2003
+ this.history = history;
2004
+ }
2005
+ }
1908
2006
  if (decisionData.type === "user_decision_execute") {
1909
2007
  const toolName = toolCall.function.name;
1910
2008
  const toolArgs = JSON.parse(toolCall.function.arguments);
@@ -2071,7 +2169,7 @@ var WorkingTimer = () => {
2071
2169
  return () => clearInterval(dotsTimer);
2072
2170
  }, []);
2073
2171
  const dots = ".".repeat(dotIndex).padEnd(3, " ");
2074
- return /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: "magenta", children: [
2172
+ return /* @__PURE__ */ jsx7(Box7, { marginBottom: 0.5, paddingX: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: "magenta", children: [
2075
2173
  `working${dots}`,
2076
2174
  ` ${seconds}s`
2077
2175
  ] }) });
@@ -2300,8 +2398,191 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
2300
2398
  };
2301
2399
  var ToolResultDisplay = memo3(ToolResultDisplayComponent);
2302
2400
 
2303
- // src/app/ui/App.tsx
2401
+ // src/app/ui/SessionInfoConnectingMCP.tsx
2402
+ import { Box as Box11, Text as Text10 } from "ink";
2403
+ import Spinner from "ink-spinner";
2304
2404
  import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2405
+ var SessionInfoConnectingMCP = ({ sessionId: sessionId2, workdir, statusMessage }) => {
2406
+ return /* @__PURE__ */ jsxs9(
2407
+ Box11,
2408
+ {
2409
+ borderStyle: "round",
2410
+ borderColor: "gray",
2411
+ flexDirection: "column",
2412
+ marginBottom: 1,
2413
+ children: [
2414
+ /* @__PURE__ */ jsxs9(Text10, { children: [
2415
+ /* @__PURE__ */ jsx11(Text10, { bold: true, color: "white", children: "localhost" }),
2416
+ " ",
2417
+ /* @__PURE__ */ jsx11(Text10, { color: "gray", children: " session:" }),
2418
+ " ",
2419
+ /* @__PURE__ */ jsx11(Text10, { color: "magenta", children: sessionId2 })
2420
+ ] }),
2421
+ /* @__PURE__ */ jsxs9(Text10, { children: [
2422
+ /* @__PURE__ */ jsx11(Text10, { color: "magenta", children: "\u21B3" }),
2423
+ " ",
2424
+ /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2425
+ "workdir: ",
2426
+ workdir
2427
+ ] })
2428
+ ] }),
2429
+ /* @__PURE__ */ jsxs9(Text10, { children: [
2430
+ /* @__PURE__ */ jsx11(Text10, { color: "magenta", children: "\u21B3" }),
2431
+ " ",
2432
+ /* @__PURE__ */ jsx11(Text10, { color: "gray", children: "MCP: " }),
2433
+ /* @__PURE__ */ jsxs9(Text10, { color: "yellow", children: [
2434
+ /* @__PURE__ */ jsx11(Spinner, { type: "dots" }),
2435
+ " connecting"
2436
+ ] })
2437
+ ] }),
2438
+ /* @__PURE__ */ jsxs9(Text10, { children: [
2439
+ /* @__PURE__ */ jsx11(Text10, { color: "magenta", children: "\u21B3" }),
2440
+ " ",
2441
+ /* @__PURE__ */ jsx11(Text10, { color: "gray", children: "status: " }),
2442
+ /* @__PURE__ */ jsx11(Text10, { color: "white", children: statusMessage || "Please wait while we establish connections." })
2443
+ ] })
2444
+ ]
2445
+ }
2446
+ );
2447
+ };
2448
+ var SessionInfoConnectingMCP_default = SessionInfoConnectingMCP;
2449
+
2450
+ // src/app/ui/components/SlashCommands.tsx
2451
+ import { Box as Box12, Text as Text11 } from "ink";
2452
+ import { Fragment, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2453
+ var SlashCommands = ({ input, setHistory, agentRef }) => {
2454
+ const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
2455
+ const outBox = (children) => /* @__PURE__ */ jsx12(Box12, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginBottom: 1, flexDirection: "column", children });
2456
+ const render2 = () => {
2457
+ if (!cmd) {
2458
+ return null;
2459
+ }
2460
+ if (cmd === "help") {
2461
+ const cmds = getSlashCommands();
2462
+ return outBox(
2463
+ /* @__PURE__ */ jsxs10(Fragment, { children: [
2464
+ /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Available commands" }),
2465
+ cmds.map((c, i) => /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2466
+ c.name,
2467
+ " - ",
2468
+ c.description
2469
+ ] }, i))
2470
+ ] })
2471
+ );
2472
+ }
2473
+ if (cmd === "clear") {
2474
+ setHistory((prev) => prev.filter((item) => item.id === 0 || item.id === 1));
2475
+ return outBox(/* @__PURE__ */ jsx12(Text11, { color: "green", children: "History cleared." }));
2476
+ }
2477
+ if (cmd === "mcp") {
2478
+ const all = agentRef.current?.getUiToolsDetailed?.() || agentRef.current?.getAvailableTools?.() || [];
2479
+ const isMcp = (t) => t.source?.toLowerCase?.() === "mcp" || !!t.server && t.server !== "native";
2480
+ const tools = all.filter(isMcp);
2481
+ const term = (args?.[0] || "").toLowerCase();
2482
+ const filtered = term ? tools.filter((t) => (t.function?.name || t.name || "tool").toLowerCase().includes(term)) : tools;
2483
+ const pad = (s, n) => s.length >= n ? s.slice(0, n - 1) + "\u2026" : s.padEnd(n, " ");
2484
+ const colName = 34;
2485
+ const colType = 10;
2486
+ const colSource = 18;
2487
+ return outBox(
2488
+ /* @__PURE__ */ jsxs10(Fragment, { children: [
2489
+ /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "MCP Tools" }),
2490
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2491
+ "Total MCP: ",
2492
+ tools.length,
2493
+ term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
2494
+ ] }),
2495
+ filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools to display." }) : /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", children: [
2496
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2497
+ pad("Name", colName),
2498
+ " | ",
2499
+ pad("Type", colType),
2500
+ " | ",
2501
+ pad("Source", colSource)
2502
+ ] }),
2503
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2504
+ "".padEnd(colName, "-"),
2505
+ "---",
2506
+ "".padEnd(colType, "-"),
2507
+ "---",
2508
+ "".padEnd(colSource, "-")
2509
+ ] }),
2510
+ filtered.map((t, i) => {
2511
+ const name = t.function?.name || t.name || "tool";
2512
+ const type = t.function?.name ? "fn" : t.type || "tool";
2513
+ const source = t.source || t.provider || "mcp";
2514
+ return /* @__PURE__ */ jsxs10(Text11, { color: "white", children: [
2515
+ pad(name, colName),
2516
+ " | ",
2517
+ pad(String(type), colType),
2518
+ " | ",
2519
+ pad(String(source), colSource)
2520
+ ] }, i);
2521
+ })
2522
+ ] })
2523
+ ] })
2524
+ );
2525
+ }
2526
+ if (cmd === "tools") {
2527
+ const all = agentRef.current?.getUiToolsDetailed?.() || agentRef.current?.getAvailableTools?.() || [];
2528
+ const isMcp = (t) => t.source?.toLowerCase?.() === "mcp" || !!t.server && t.server !== "native";
2529
+ const tools = all.filter((t) => !isMcp(t));
2530
+ const term = (args?.[0] || "").toLowerCase();
2531
+ const filtered = term ? tools.filter((t) => (t.function?.name || t.name || "tool").toLowerCase().includes(term)) : tools;
2532
+ const pad = (s, n) => s.length >= n ? s.slice(0, n - 1) + "\u2026" : s.padEnd(n, " ");
2533
+ const colName = 34;
2534
+ const colType = 10;
2535
+ const colSource = 18;
2536
+ return outBox(
2537
+ /* @__PURE__ */ jsxs10(Fragment, { children: [
2538
+ /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Native Tools" }),
2539
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2540
+ "Total Native: ",
2541
+ tools.length,
2542
+ term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
2543
+ ] }),
2544
+ filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", children: [
2545
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2546
+ pad("Name", colName),
2547
+ " | ",
2548
+ pad("Type", colType),
2549
+ " | ",
2550
+ pad("Source", colSource)
2551
+ ] }),
2552
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2553
+ "".padEnd(colName, "-"),
2554
+ "---",
2555
+ "".padEnd(colType, "-"),
2556
+ "---",
2557
+ "".padEnd(colSource, "-")
2558
+ ] }),
2559
+ filtered.map((t, i) => {
2560
+ const name = t.function?.name || t.name || "tool";
2561
+ const type = t.function?.name ? "fn" : t.type || "tool";
2562
+ const source = t.source || "native";
2563
+ return /* @__PURE__ */ jsxs10(Text11, { color: "white", children: [
2564
+ pad(name, colName),
2565
+ " | ",
2566
+ pad(String(type), colType),
2567
+ " | ",
2568
+ pad(String(source), colSource)
2569
+ ] }, i);
2570
+ })
2571
+ ] })
2572
+ ] })
2573
+ );
2574
+ }
2575
+ return outBox(/* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
2576
+ "Command not recognized: /",
2577
+ cmd
2578
+ ] }));
2579
+ };
2580
+ return /* @__PURE__ */ jsx12(Fragment, { children: render2() });
2581
+ };
2582
+ var SlashCommands_default = SlashCommands;
2583
+
2584
+ // src/app/ui/App.tsx
2585
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2305
2586
  var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2306
2587
  const agentInstance = useRef(null);
2307
2588
  const [history, setHistory] = useState4([]);
@@ -2316,7 +2597,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2316
2597
  const [pendingConfirmation, setPendingConfirmation] = useState4(
2317
2598
  null
2318
2599
  );
2319
- const [confirmationPreview, setConfirmationPreview] = useState4(null);
2600
+ const [confirmationPreview, setConfirmationPreview] = useState4(
2601
+ null
2602
+ );
2320
2603
  const alwaysAcceptList = useRef([]);
2321
2604
  const workdir = process.cwd();
2322
2605
  const handleInterrupt = useCallback(() => {
@@ -2327,13 +2610,33 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2327
2610
  ...prev,
2328
2611
  {
2329
2612
  id: prev.length,
2330
- component: /* @__PURE__ */ jsx11(Text10, { color: "yellow", children: "-- Task cancelled by dev. --" })
2613
+ component: /* @__PURE__ */ jsx13(Text12, { color: "yellow", children: "-- Task cancelled by dev. --" })
2331
2614
  }
2332
2615
  ]);
2333
2616
  }, [isProcessing, eventBus2]);
2334
2617
  const handleSubmit = useCallback(
2335
2618
  (text) => {
2336
2619
  if (!text || isProcessing || !agentInstance.current) return;
2620
+ if (text.startsWith("/")) {
2621
+ const [cmd] = text.slice(1).trim().split(/\s+/);
2622
+ if (!cmd) {
2623
+ setIsProcessing(false);
2624
+ return;
2625
+ }
2626
+ setHistory((prev) => [
2627
+ ...prev,
2628
+ {
2629
+ id: prev.length,
2630
+ component: /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text12, { color: "white", dimColor: true, children: text }) })
2631
+ },
2632
+ {
2633
+ id: prev.length + 1,
2634
+ component: /* @__PURE__ */ jsx13(SlashCommands_default, { input: text, setHistory, agentRef: agentInstance })
2635
+ }
2636
+ ]);
2637
+ setIsProcessing(false);
2638
+ return;
2639
+ }
2337
2640
  setIsProcessing(true);
2338
2641
  const displayText = text.length > 1e4 ? text.substring(0, 1e4) + "..." : text;
2339
2642
  setHistory((prev) => [
@@ -2342,8 +2645,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2342
2645
  id: prev.length,
2343
2646
  component: (
2344
2647
  // Uma única Box para o espaçamento
2345
- /* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text10, { color: "white", dimColor: true, children: [
2346
- /* @__PURE__ */ jsxs9(Text10, { color: "white", children: [
2648
+ /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: "white", dimColor: true, children: [
2649
+ /* @__PURE__ */ jsxs11(Text12, { color: "white", children: [
2347
2650
  ">",
2348
2651
  " "
2349
2652
  ] }),
@@ -2378,7 +2681,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2378
2681
  []
2379
2682
  );
2380
2683
  useEffect3(() => {
2381
- setHistory([{ id: 0, component: /* @__PURE__ */ jsx11(Header, {}) }]);
2684
+ setHistory([{ id: 0, component: /* @__PURE__ */ jsx13(Header, {}) }]);
2382
2685
  const initializeAgent = async () => {
2383
2686
  try {
2384
2687
  agentInstance.current = new Agent(sessionId2, eventBus2);
@@ -2430,7 +2733,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2430
2733
  if (prev.length < 2) {
2431
2734
  newHistory.push({
2432
2735
  id: 1,
2433
- component: /* @__PURE__ */ jsx11(
2736
+ component: /* @__PURE__ */ jsx13(
2434
2737
  SessionInfo,
2435
2738
  {
2436
2739
  sessionId: sessionId2,
@@ -2451,10 +2754,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2451
2754
  }
2452
2755
  let newComponent = null;
2453
2756
  if (parsed.type === "debug") {
2454
- newComponent = /* @__PURE__ */ jsx11(Text10, { color: "gray", children: parsed.message });
2757
+ newComponent = /* @__PURE__ */ jsx13(Text12, { color: "gray", children: parsed.message });
2455
2758
  } else if (parsed.type === "protocol_violation") {
2456
- newComponent = /* @__PURE__ */ jsxs9(
2457
- Box11,
2759
+ newComponent = /* @__PURE__ */ jsxs11(
2760
+ Box13,
2458
2761
  {
2459
2762
  borderStyle: "round",
2460
2763
  borderColor: "yellow",
@@ -2462,27 +2765,19 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2462
2765
  marginBottom: 1,
2463
2766
  paddingX: 1,
2464
2767
  children: [
2465
- " ",
2466
- /* @__PURE__ */ jsxs9(Text10, { color: "yellow", bold: true, children: [
2467
- " ",
2468
- "Protocol Violation",
2469
- " "
2470
- ] }),
2471
- " ",
2472
- /* @__PURE__ */ jsx11(Text10, { color: "gray", children: parsed.content }),
2473
- " ",
2474
- /* @__PURE__ */ jsx11(Text10, { color: "yellow", children: parsed.message }),
2475
- " "
2768
+ /* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: "Protocol Violation" }),
2769
+ /* @__PURE__ */ jsx13(Text12, { color: "gray", children: parsed.content }),
2770
+ /* @__PURE__ */ jsx13(Text12, { color: "yellow", children: parsed.message })
2476
2771
  ]
2477
2772
  }
2478
2773
  );
2479
2774
  } else if (parsed.type === "error") {
2480
- newComponent = /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
2775
+ newComponent = /* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
2481
2776
  "\u274C ",
2482
2777
  parsed.message
2483
2778
  ] });
2484
2779
  } else if (parsed.type === "tool_call") {
2485
- newComponent = /* @__PURE__ */ jsx11(
2780
+ newComponent = /* @__PURE__ */ jsx13(
2486
2781
  ToolCallDisplay,
2487
2782
  {
2488
2783
  toolName: parsed.tool_name,
@@ -2491,13 +2786,27 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2491
2786
  }
2492
2787
  );
2493
2788
  } else if (parsed.type === "tool_result") {
2494
- newComponent = /* @__PURE__ */ jsx11(
2789
+ newComponent = /* @__PURE__ */ jsx13(
2495
2790
  ToolResultDisplay,
2496
2791
  {
2497
2792
  toolName: parsed.tool_name,
2498
2793
  result: parsed.result
2499
2794
  }
2500
2795
  );
2796
+ } else if (parsed.type === "dev_overlay") {
2797
+ newComponent = /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: "gray", children: [
2798
+ /* @__PURE__ */ jsxs11(Text12, { color: "blue", children: [
2799
+ ">",
2800
+ " "
2801
+ ] }),
2802
+ parsed.payload
2803
+ ] }) });
2804
+ } else if (parsed.type === "log") {
2805
+ newComponent = /* @__PURE__ */ jsxs11(Text12, { color: "gray", children: [
2806
+ "\u2139\uFE0F ",
2807
+ parsed.message,
2808
+ parsed.payload ? `: ${parsed.payload}` : ""
2809
+ ] });
2501
2810
  } else if (parsed.type === "assistant_message" && parsed.content) {
2502
2811
  newComponent = null;
2503
2812
  }
@@ -2510,22 +2819,37 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2510
2819
  } catch (error) {
2511
2820
  }
2512
2821
  };
2822
+ const handleUiOverlay = (data) => {
2823
+ eventBus2.emit("dev_overlay", data);
2824
+ };
2825
+ uiEventBus.on("dev_overlay", handleUiOverlay);
2513
2826
  eventBus2.on("backend_message", handleBackendMessage);
2514
2827
  initializeAgent();
2515
2828
  return () => {
2829
+ uiEventBus.off("dev_overlay", handleUiOverlay);
2516
2830
  eventBus2.off("backend_message", handleBackendMessage);
2517
2831
  };
2518
2832
  }, [eventBus2, sessionId2, handleConfirmation]);
2519
2833
  const renderInteractiveComponent = () => {
2520
2834
  if (mcpStatus !== "connected") {
2521
- return /* @__PURE__ */ jsx11(Box11, { borderStyle: "round", borderColor: "black", children: /* @__PURE__ */ jsxs9(Text10, { color: "yellow", children: [
2522
- /* @__PURE__ */ jsx11(Spinner, { type: "dots" }),
2523
- " ",
2524
- statusMessage || "Connecting..."
2525
- ] }) });
2835
+ return /* @__PURE__ */ jsx13(
2836
+ Box13,
2837
+ {
2838
+ borderStyle: "round",
2839
+ borderColor: "black",
2840
+ children: /* @__PURE__ */ jsx13(
2841
+ SessionInfoConnectingMCP_default,
2842
+ {
2843
+ sessionId: sessionId2,
2844
+ workdir,
2845
+ statusMessage
2846
+ }
2847
+ )
2848
+ }
2849
+ );
2526
2850
  }
2527
2851
  if (pendingConfirmation) {
2528
- return /* @__PURE__ */ jsx11(
2852
+ return /* @__PURE__ */ jsx13(
2529
2853
  ConfirmationPrompt,
2530
2854
  {
2531
2855
  toolCalls: pendingConfirmation,
@@ -2537,9 +2861,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2537
2861
  }
2538
2862
  );
2539
2863
  }
2540
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
2541
- isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx11(WorkingTimer, {}),
2542
- /* @__PURE__ */ jsx11(
2864
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", children: [
2865
+ isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx13(WorkingTimer, {}),
2866
+ /* @__PURE__ */ jsx13(
2543
2867
  InputPrompt,
2544
2868
  {
2545
2869
  onSubmit: handleSubmit,
@@ -2549,8 +2873,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
2549
2873
  )
2550
2874
  ] });
2551
2875
  };
2552
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
2553
- /* @__PURE__ */ jsx11(Static, { items: history, children: (item) => /* @__PURE__ */ jsx11(Box11, { children: item.component }, item.id) }),
2876
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", children: [
2877
+ /* @__PURE__ */ jsx13(Static, { items: history, children: (item) => /* @__PURE__ */ jsx13(Box13, { children: item.component }, item.id) }),
2554
2878
  renderInteractiveComponent()
2555
2879
  ] });
2556
2880
  };
@@ -2558,7 +2882,7 @@ var App = memo4(AppComponent);
2558
2882
  var App_default = App;
2559
2883
 
2560
2884
  // src/main.ts
2561
- var eventBus = new EventEmitter();
2885
+ var eventBus = new EventEmitter2();
2562
2886
  var sessionId = uuidv42();
2563
2887
  var props = {
2564
2888
  eventBus,