@nomad-e/bluma-cli 0.1.1 → 0.1.4

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.
Files changed (2) hide show
  1. package/dist/main.js +446 -147
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -328,14 +328,14 @@ var init_async_command = __esm({
328
328
  });
329
329
 
330
330
  // src/main.ts
331
- import React10 from "react";
331
+ import React11 from "react";
332
332
  import { render } from "ink";
333
333
  import { EventEmitter as EventEmitter2 } from "events";
334
334
  import { v4 as uuidv43 } from "uuid";
335
335
 
336
336
  // src/app/ui/App.tsx
337
- import { useState as useState6, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback2, memo as memo10 } from "react";
338
- import { Box as Box16, Text as Text15, Static } from "ink";
337
+ import { useState as useState7, useEffect as useEffect6, useRef as useRef5, useCallback as useCallback2, memo as memo11 } from "react";
338
+ import { Box as Box17, Text as Text16, Static as Static2 } from "ink";
339
339
 
340
340
  // src/app/ui/layout.tsx
341
341
  import { Box, Text } from "ink";
@@ -3374,9 +3374,17 @@ async function loadSkill(args) {
3374
3374
  message: `Skill "${skill_name}" not found. Available skills: ${availableNames}`
3375
3375
  };
3376
3376
  }
3377
+ if (globalContext.history) {
3378
+ globalContext.history.push({
3379
+ role: "user",
3380
+ content: `[SKILL:${skill.name}]
3381
+
3382
+ ${skill.content}`
3383
+ });
3384
+ }
3377
3385
  return {
3378
3386
  success: true,
3379
- message: `Skill "${skill_name}" loaded.`,
3387
+ message: `Skill "${skill_name}" loaded successfully. Follow the instructions in the skill content above.`,
3380
3388
  skill_name: skill.name,
3381
3389
  description: skill.description
3382
3390
  };
@@ -3823,7 +3831,13 @@ async function doSaveSessionHistory(sessionFile, history) {
3823
3831
  });
3824
3832
  }
3825
3833
  async function saveSessionHistory(sessionFile, history) {
3826
- debouncedSave(sessionFile, history);
3834
+ const cleanHistory = history.filter((msg) => {
3835
+ if (msg.role === "user" && typeof msg.content === "string") {
3836
+ return !msg.content.startsWith("[SKILL:");
3837
+ }
3838
+ return true;
3839
+ });
3840
+ debouncedSave(sessionFile, cleanHistory);
3827
3841
  }
3828
3842
 
3829
3843
  // src/app/agent/core/prompt/prompt_builder.ts
@@ -4940,6 +4954,19 @@ var BluMaAgent = class {
4940
4954
  this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled before tool execution." });
4941
4955
  return;
4942
4956
  }
4957
+ const actionMap = {
4958
+ "edit_tool": "Editing",
4959
+ "shell_command": "Executing",
4960
+ "command_status": "Waiting",
4961
+ "ls_tool": "Reading",
4962
+ "read_file_lines": "Reading",
4963
+ "message": "Responding",
4964
+ "load_skill": "Loading skill",
4965
+ "search_web": "Searching",
4966
+ "todo": "Planning"
4967
+ };
4968
+ const action = actionMap[toolName] || "Processing";
4969
+ this.eventBus.emit("action_status", { action });
4943
4970
  const result = await this.mcpClient.invoke(toolName, toolArgs);
4944
4971
  let finalResult = result;
4945
4972
  if (Array.isArray(result) && result.length > 0 && result[0].type === "text" && typeof result[0].text === "string") {
@@ -5006,82 +5033,200 @@ ${editData.error.display}`;
5006
5033
  return;
5007
5034
  }
5008
5035
  const contextWindow = createApiContextWindow(this.history, this.maxContextTurns);
5009
- const response = await this.llm.chatCompletion({
5010
- model: "x-ai/grok-code-fast-1",
5011
- // model: "openrouter/polaris-alpha", //OpenRouter Openai training model
5012
- messages: contextWindow,
5013
- temperature: 0.3,
5014
- tools: this.mcpClient.getAvailableTools(),
5015
- tool_choice: "required",
5016
- parallel_tool_calls: false
5017
- });
5036
+ const llmService = this.llm;
5037
+ if (typeof llmService.chatCompletionStream === "function") {
5038
+ await this._handleStreamingResponse(contextWindow);
5039
+ } else {
5040
+ await this._handleNonStreamingResponse(contextWindow);
5041
+ }
5042
+ } catch (error) {
5043
+ const errorMessage = error instanceof Error ? error.message : "An unknown API error occurred.";
5044
+ this.eventBus.emit("backend_message", { type: "error", message: errorMessage });
5045
+ } finally {
5046
+ await saveSessionHistory(this.sessionFile, this.history);
5047
+ }
5048
+ }
5049
+ async _handleStreamingResponse(contextWindow) {
5050
+ const llmService = this.llm;
5051
+ this.eventBus.emit("action_status", { action: "Thinking" });
5052
+ let accumulatedContent = "";
5053
+ let toolCalls;
5054
+ let hasEmittedStart = false;
5055
+ let reasoningContent = "";
5056
+ const stream = llmService.chatCompletionStream({
5057
+ model: "x-ai/grok-code-fast-1",
5058
+ messages: contextWindow,
5059
+ temperature: 0.3,
5060
+ tools: this.mcpClient.getAvailableTools(),
5061
+ tool_choice: "required",
5062
+ parallel_tool_calls: false
5063
+ });
5064
+ for await (const chunk of stream) {
5018
5065
  if (this.isInterrupted) {
5066
+ this.eventBus.emit("stream_end", {});
5019
5067
  this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
5020
5068
  return;
5021
5069
  }
5022
- let message2 = response.choices[0].message;
5023
- message2 = ToolCallNormalizer.normalizeAssistantMessage(message2);
5024
- if (message2.reasoning_content || message2.reasoning) {
5025
- const reasoningText = message2.reasoning_content || message2.reasoning;
5070
+ if (chunk.reasoning) {
5071
+ if (!hasEmittedStart) {
5072
+ this.eventBus.emit("stream_start", {});
5073
+ hasEmittedStart = true;
5074
+ }
5075
+ reasoningContent += chunk.reasoning;
5076
+ this.eventBus.emit("stream_reasoning_chunk", { delta: chunk.reasoning });
5077
+ }
5078
+ if (chunk.delta) {
5079
+ if (!hasEmittedStart) {
5080
+ this.eventBus.emit("stream_start", {});
5081
+ hasEmittedStart = true;
5082
+ }
5083
+ accumulatedContent += chunk.delta;
5084
+ this.eventBus.emit("stream_chunk", { delta: chunk.delta });
5085
+ }
5086
+ if (chunk.tool_calls) {
5087
+ toolCalls = chunk.tool_calls;
5088
+ }
5089
+ if (chunk.finish_reason) {
5090
+ if (hasEmittedStart) {
5091
+ this.eventBus.emit("stream_end", {});
5092
+ }
5093
+ }
5094
+ }
5095
+ const message2 = {
5096
+ role: "assistant",
5097
+ content: accumulatedContent || null
5098
+ };
5099
+ if (toolCalls && toolCalls.length > 0) {
5100
+ message2.tool_calls = toolCalls;
5101
+ }
5102
+ const normalizedMessage = ToolCallNormalizer.normalizeAssistantMessage(message2);
5103
+ if (normalizedMessage.reasoning_content || normalizedMessage.reasoning) {
5104
+ const reasoningText = normalizedMessage.reasoning_content || normalizedMessage.reasoning;
5105
+ this.eventBus.emit("backend_message", {
5106
+ type: "reasoning",
5107
+ content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
5108
+ });
5109
+ }
5110
+ this.history.push(normalizedMessage);
5111
+ if (normalizedMessage.tool_calls && normalizedMessage.tool_calls.length > 0) {
5112
+ const validToolCalls = normalizedMessage.tool_calls.filter(
5113
+ (call) => ToolCallNormalizer.isValidToolCall(call)
5114
+ );
5115
+ if (validToolCalls.length === 0) {
5026
5116
  this.eventBus.emit("backend_message", {
5027
- type: "reasoning",
5028
- content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
5117
+ type: "error",
5118
+ message: "Model returned invalid tool calls. Retrying..."
5029
5119
  });
5120
+ await this._continueConversation();
5121
+ return;
5030
5122
  }
5031
- this.history.push(message2);
5032
- if (message2.tool_calls && message2.tool_calls.length > 0) {
5033
- const validToolCalls = message2.tool_calls.filter(
5034
- (call) => ToolCallNormalizer.isValidToolCall(call)
5035
- );
5036
- if (validToolCalls.length === 0) {
5037
- this.eventBus.emit("backend_message", {
5038
- type: "error",
5039
- message: "Model returned invalid tool calls. Retrying..."
5040
- });
5041
- await this._continueConversation();
5042
- return;
5043
- }
5044
- const autoApprovedTools = [
5045
- "message",
5046
- "ls_tool",
5047
- "count_file_lines",
5048
- "read_file_lines",
5049
- "todo",
5050
- "load_skill",
5051
- "search_web"
5052
- ];
5053
- const toolToCall = validToolCalls[0];
5054
- const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5055
- if (isSafeTool) {
5056
- await this.handleToolResponse({ type: "user_decision_execute", tool_calls: validToolCalls });
5123
+ const autoApprovedTools = [
5124
+ "message",
5125
+ "ls_tool",
5126
+ "count_file_lines",
5127
+ "read_file_lines",
5128
+ "todo",
5129
+ "load_skill",
5130
+ "search_web"
5131
+ ];
5132
+ const toolToCall = validToolCalls[0];
5133
+ const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5134
+ if (isSafeTool) {
5135
+ await this.handleToolResponse({ type: "user_decision_execute", tool_calls: validToolCalls });
5136
+ } else {
5137
+ const toolName = toolToCall.function.name;
5138
+ if (toolName === "edit_tool") {
5139
+ const args = JSON.parse(toolToCall.function.arguments);
5140
+ const previewContent = await this._generateEditPreview(args);
5141
+ this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls, preview: previewContent });
5057
5142
  } else {
5058
- const toolName = toolToCall.function.name;
5059
- if (toolName === "edit_tool") {
5060
- const args = JSON.parse(toolToCall.function.arguments);
5061
- const previewContent = await this._generateEditPreview(args);
5062
- this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls, preview: previewContent });
5063
- } else {
5064
- this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls });
5065
- }
5143
+ this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls });
5066
5144
  }
5067
- } else if (message2.content) {
5068
- this.eventBus.emit("backend_message", { type: "assistant_message", content: message2.content });
5069
- const feedback = this.feedbackSystem.generateFeedback({
5070
- event: "protocol_violation_direct_text",
5071
- details: { violationContent: message2.content }
5145
+ }
5146
+ } else if (accumulatedContent) {
5147
+ this.eventBus.emit("backend_message", { type: "assistant_message", content: accumulatedContent });
5148
+ const feedback = this.feedbackSystem.generateFeedback({
5149
+ event: "protocol_violation_direct_text",
5150
+ details: { violationContent: accumulatedContent }
5151
+ });
5152
+ this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: accumulatedContent });
5153
+ this.history.push({ role: "system", content: feedback.correction });
5154
+ await this._continueConversation();
5155
+ } else {
5156
+ this.eventBus.emit("backend_message", { type: "info", message: "Agent is thinking... continuing reasoning cycle." });
5157
+ await this._continueConversation();
5158
+ }
5159
+ }
5160
+ async _handleNonStreamingResponse(contextWindow) {
5161
+ const response = await this.llm.chatCompletion({
5162
+ model: "x-ai/grok-code-fast-1",
5163
+ messages: contextWindow,
5164
+ temperature: 0.3,
5165
+ tools: this.mcpClient.getAvailableTools(),
5166
+ tool_choice: "required",
5167
+ parallel_tool_calls: false
5168
+ });
5169
+ if (this.isInterrupted) {
5170
+ this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
5171
+ return;
5172
+ }
5173
+ let message2 = response.choices[0].message;
5174
+ message2 = ToolCallNormalizer.normalizeAssistantMessage(message2);
5175
+ if (message2.reasoning_content || message2.reasoning) {
5176
+ const reasoningText = message2.reasoning_content || message2.reasoning;
5177
+ this.eventBus.emit("backend_message", {
5178
+ type: "reasoning",
5179
+ content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
5180
+ });
5181
+ }
5182
+ this.history.push(message2);
5183
+ if (message2.tool_calls && message2.tool_calls.length > 0) {
5184
+ const validToolCalls = message2.tool_calls.filter(
5185
+ (call) => ToolCallNormalizer.isValidToolCall(call)
5186
+ );
5187
+ if (validToolCalls.length === 0) {
5188
+ this.eventBus.emit("backend_message", {
5189
+ type: "error",
5190
+ message: "Model returned invalid tool calls. Retrying..."
5072
5191
  });
5073
- this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: message2.content });
5074
- this.history.push({ role: "system", content: feedback.correction });
5075
5192
  await this._continueConversation();
5193
+ return;
5194
+ }
5195
+ const autoApprovedTools = [
5196
+ "message",
5197
+ "ls_tool",
5198
+ "count_file_lines",
5199
+ "read_file_lines",
5200
+ "todo",
5201
+ "load_skill",
5202
+ "search_web"
5203
+ ];
5204
+ const toolToCall = validToolCalls[0];
5205
+ const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
5206
+ if (isSafeTool) {
5207
+ await this.handleToolResponse({ type: "user_decision_execute", tool_calls: validToolCalls });
5076
5208
  } else {
5077
- this.eventBus.emit("backend_message", { type: "info", message: "Agent is thinking... continuing reasoning cycle." });
5078
- await this._continueConversation();
5209
+ const toolName = toolToCall.function.name;
5210
+ if (toolName === "edit_tool") {
5211
+ const args = JSON.parse(toolToCall.function.arguments);
5212
+ const previewContent = await this._generateEditPreview(args);
5213
+ this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls, preview: previewContent });
5214
+ } else {
5215
+ this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls });
5216
+ }
5079
5217
  }
5080
- } catch (error) {
5081
- const errorMessage = error instanceof Error ? error.message : "An unknown API error occurred.";
5082
- this.eventBus.emit("backend_message", { type: "error", message: errorMessage });
5083
- } finally {
5084
- await saveSessionHistory(this.sessionFile, this.history);
5218
+ } else if (message2.content) {
5219
+ this.eventBus.emit("backend_message", { type: "assistant_message", content: message2.content });
5220
+ const feedback = this.feedbackSystem.generateFeedback({
5221
+ event: "protocol_violation_direct_text",
5222
+ details: { violationContent: message2.content }
5223
+ });
5224
+ this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: message2.content });
5225
+ this.history.push({ role: "system", content: feedback.correction });
5226
+ await this._continueConversation();
5227
+ } else {
5228
+ this.eventBus.emit("backend_message", { type: "info", message: "Agent is thinking... continuing reasoning cycle." });
5229
+ await this._continueConversation();
5085
5230
  }
5086
5231
  }
5087
5232
  };
@@ -5098,6 +5243,9 @@ var LLMService = class {
5098
5243
  });
5099
5244
  this.defaultModel = config2.model || "";
5100
5245
  }
5246
+ /**
5247
+ * Chamada tradicional (não-streaming) - retorna resposta completa
5248
+ */
5101
5249
  async chatCompletion(params) {
5102
5250
  const resp = await this.client.chat.completions.create({
5103
5251
  model: params.model || this.defaultModel,
@@ -5110,6 +5258,64 @@ var LLMService = class {
5110
5258
  });
5111
5259
  return resp;
5112
5260
  }
5261
+ /**
5262
+ * Chamada com streaming - retorna chunks em tempo real
5263
+ *
5264
+ * Uso:
5265
+ * ```
5266
+ * for await (const chunk of llm.chatCompletionStream(params)) {
5267
+ * process.stdout.write(chunk.delta);
5268
+ * }
5269
+ * ```
5270
+ */
5271
+ async *chatCompletionStream(params) {
5272
+ const stream = await this.client.chat.completions.create({
5273
+ model: params.model || this.defaultModel,
5274
+ messages: params.messages,
5275
+ tools: params.tools,
5276
+ tool_choice: params.tool_choice,
5277
+ parallel_tool_calls: params.parallel_tool_calls,
5278
+ temperature: params.temperature,
5279
+ max_tokens: params.max_tokens,
5280
+ stream: true
5281
+ });
5282
+ const toolCallsAccumulator = /* @__PURE__ */ new Map();
5283
+ for await (const chunk of stream) {
5284
+ const choice = chunk.choices[0];
5285
+ if (!choice) continue;
5286
+ const delta = choice.delta;
5287
+ if (delta?.tool_calls) {
5288
+ for (const tc of delta.tool_calls) {
5289
+ const idx = tc.index;
5290
+ if (!toolCallsAccumulator.has(idx)) {
5291
+ toolCallsAccumulator.set(idx, {
5292
+ id: tc.id || "",
5293
+ type: tc.type || "function",
5294
+ function: { name: "", arguments: "" }
5295
+ });
5296
+ }
5297
+ const acc = toolCallsAccumulator.get(idx);
5298
+ if (tc.id) acc.id = tc.id;
5299
+ if (tc.function?.name) acc.function.name = tc.function.name;
5300
+ if (tc.function?.arguments) acc.function.arguments += tc.function.arguments;
5301
+ }
5302
+ }
5303
+ const reasoning = delta?.reasoning_content || delta?.reasoning || "";
5304
+ yield {
5305
+ delta: delta?.content || "",
5306
+ reasoning,
5307
+ tool_calls: choice.finish_reason === "tool_calls" ? Array.from(toolCallsAccumulator.values()) : void 0,
5308
+ finish_reason: choice.finish_reason
5309
+ };
5310
+ }
5311
+ if (toolCallsAccumulator.size > 0) {
5312
+ yield {
5313
+ delta: "",
5314
+ tool_calls: Array.from(toolCallsAccumulator.values()),
5315
+ finish_reason: "tool_calls"
5316
+ };
5317
+ }
5318
+ }
5113
5319
  /**
5114
5320
  * Retorna o modelo padrão configurado
5115
5321
  */
@@ -5688,44 +5894,55 @@ var Agent = class {
5688
5894
  };
5689
5895
 
5690
5896
  // src/app/ui/WorkingTimer.tsx
5691
- import { useState as useState4, useEffect as useEffect4, memo as memo5 } from "react";
5897
+ import React4, { useState as useState4, useEffect as useEffect4, memo as memo5 } from "react";
5692
5898
  import { Box as Box7, Text as Text7 } from "ink";
5693
5899
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
5694
- var WorkingTimerComponent = ({ taskName, taskStatus }) => {
5695
- const [seconds, setSeconds] = useState4(0);
5696
- const [frame, setFrame] = useState4(0);
5900
+ var WorkingTimerComponent = ({ eventBus: eventBus2, taskName, taskStatus }) => {
5901
+ const [currentAction, setCurrentAction] = useState4("Thinking...");
5902
+ const [shinePosition, setShinePosition] = useState4(0);
5903
+ const [dots, setDots] = useState4("");
5697
5904
  useEffect4(() => {
5698
- const timer = setInterval(() => {
5699
- setSeconds((prev) => prev + 1);
5700
- }, 1e3);
5701
- return () => clearInterval(timer);
5702
- }, []);
5905
+ if (!eventBus2) return;
5906
+ const handleActionStatus = (data) => {
5907
+ if (data.action) {
5908
+ setCurrentAction(data.action);
5909
+ }
5910
+ };
5911
+ eventBus2.on("action_status", handleActionStatus);
5912
+ return () => {
5913
+ eventBus2.off("action_status", handleActionStatus);
5914
+ };
5915
+ }, [eventBus2]);
5703
5916
  useEffect4(() => {
5704
- const animator = setInterval(() => {
5705
- setFrame((prev) => (prev + 1) % 4);
5917
+ const shineTimer = setInterval(() => {
5918
+ setShinePosition((prev) => (prev + 1) % 30);
5706
5919
  }, 150);
5707
- return () => clearInterval(animator);
5920
+ return () => clearInterval(shineTimer);
5708
5921
  }, []);
5709
- const spinners = ["|", "/", "-", "\\"];
5710
- const spinner = spinners[frame];
5711
- const formatTime = (s) => {
5712
- if (s < 60) return `${s}s`;
5713
- return `${Math.floor(s / 60)}m${s % 60}s`;
5714
- };
5922
+ useEffect4(() => {
5923
+ const dotsTimer = setInterval(() => {
5924
+ setDots((prev) => prev.length >= 3 ? "" : prev + ".");
5925
+ }, 500);
5926
+ return () => clearInterval(dotsTimer);
5927
+ }, []);
5928
+ const displayAction = taskStatus || currentAction;
5929
+ const shineText = React4.useMemo(() => {
5930
+ const chars = displayAction.split("");
5931
+ const textLen = displayAction.length;
5932
+ const shineIdx = shinePosition % (textLen + 6);
5933
+ return chars.map((char, i) => {
5934
+ const distance = Math.abs(i - shineIdx);
5935
+ if (distance <= 1) {
5936
+ return /* @__PURE__ */ jsx7(Text7, { color: "white", dimColor: true, children: char }, i);
5937
+ } else {
5938
+ return /* @__PURE__ */ jsx7(Text7, { color: "gray", dimColor: true, children: char }, i);
5939
+ }
5940
+ });
5941
+ }, [displayAction, shinePosition]);
5715
5942
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, children: [
5716
- /* @__PURE__ */ jsxs7(Box7, { children: [
5717
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: spinner }),
5718
- /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
5719
- " ",
5720
- taskStatus || "thinking"
5721
- ] }),
5722
- /* @__PURE__ */ jsxs7(Text7, { color: "gray", children: [
5723
- " ",
5724
- formatTime(seconds)
5725
- ] })
5726
- ] }),
5943
+ /* @__PURE__ */ jsx7(Box7, { children: shineText }),
5727
5944
  taskName && /* @__PURE__ */ jsx7(Box7, { paddingLeft: 2, children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
5728
- "^ ",
5945
+ "\u203A ",
5729
5946
  taskName
5730
5947
  ] }) })
5731
5948
  ] });
@@ -6658,14 +6875,79 @@ var ReasoningDisplayComponent = ({
6658
6875
  const lines = reasoning.split("\n");
6659
6876
  const displayText = isExpanded ? reasoning : lines.slice(0, maxLines).join("\n") + (lines.length > maxLines ? "..." : "");
6660
6877
  return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, marginBottom: 1, marginTop: 1, children: [
6661
- /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text14, { color: "white", bold: true, children: "Thinking" }) }),
6878
+ /* @__PURE__ */ jsx15(Box15, {}),
6662
6879
  /* @__PURE__ */ jsx15(Box15, { paddingLeft: 2, flexDirection: "column", children: displayText.split("\n").map((line, i) => /* @__PURE__ */ jsx15(Text14, { color: "gray", dimColor: true, children: line }, i)) })
6663
6880
  ] });
6664
6881
  };
6665
6882
  var ReasoningDisplay = memo9(ReasoningDisplayComponent);
6666
6883
 
6667
- // src/app/ui/App.tsx
6884
+ // src/app/ui/components/StreamingText.tsx
6885
+ import { useState as useState6, useEffect as useEffect5, useRef as useRef4, memo as memo10 } from "react";
6886
+ import { Box as Box16, Text as Text15, Static } from "ink";
6668
6887
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6888
+ var StreamingTextComponent = ({ eventBus: eventBus2, onReasoningComplete }) => {
6889
+ const [completedLines, setCompletedLines] = useState6([]);
6890
+ const [currentLine, setCurrentLine] = useState6("");
6891
+ const [isStreaming, setIsStreaming] = useState6(false);
6892
+ const reasoningRef = useRef4("");
6893
+ const lastProcessedLength = useRef4(0);
6894
+ useEffect5(() => {
6895
+ const handleStart = () => {
6896
+ setCompletedLines([]);
6897
+ setCurrentLine("");
6898
+ reasoningRef.current = "";
6899
+ lastProcessedLength.current = 0;
6900
+ setIsStreaming(true);
6901
+ };
6902
+ const handleReasoningChunk = (data) => {
6903
+ if (data.delta) {
6904
+ reasoningRef.current += data.delta;
6905
+ const fullText = reasoningRef.current;
6906
+ const lastNewlineIndex = fullText.lastIndexOf("\n");
6907
+ if (lastNewlineIndex > lastProcessedLength.current) {
6908
+ const newCompleteText = fullText.substring(0, lastNewlineIndex);
6909
+ const lines = newCompleteText.split("\n");
6910
+ setCompletedLines(lines.map((line, i) => ({ id: i, content: line })));
6911
+ setCurrentLine(fullText.substring(lastNewlineIndex + 1));
6912
+ lastProcessedLength.current = lastNewlineIndex + 1;
6913
+ } else {
6914
+ const startIdx = lastProcessedLength.current > 0 ? lastProcessedLength.current : fullText.lastIndexOf("\n") + 1;
6915
+ setCurrentLine(fullText.substring(startIdx));
6916
+ }
6917
+ }
6918
+ };
6919
+ const handleEnd = () => {
6920
+ setIsStreaming(false);
6921
+ const finalReasoning = reasoningRef.current;
6922
+ if (finalReasoning && onReasoningComplete) {
6923
+ onReasoningComplete(finalReasoning);
6924
+ }
6925
+ setCompletedLines([]);
6926
+ setCurrentLine("");
6927
+ reasoningRef.current = "";
6928
+ lastProcessedLength.current = 0;
6929
+ };
6930
+ eventBus2.on("stream_start", handleStart);
6931
+ eventBus2.on("stream_reasoning_chunk", handleReasoningChunk);
6932
+ eventBus2.on("stream_end", handleEnd);
6933
+ return () => {
6934
+ eventBus2.off("stream_start", handleStart);
6935
+ eventBus2.off("stream_reasoning_chunk", handleReasoningChunk);
6936
+ eventBus2.off("stream_end", handleEnd);
6937
+ };
6938
+ }, [eventBus2, onReasoningComplete]);
6939
+ if (!isStreaming || completedLines.length === 0 && !currentLine) {
6940
+ return null;
6941
+ }
6942
+ return /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", paddingX: 1, marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ jsxs15(Box16, { paddingLeft: 2, flexDirection: "column", children: [
6943
+ /* @__PURE__ */ jsx16(Static, { items: completedLines, children: (item) => /* @__PURE__ */ jsx16(Text15, { color: "gray", dimColor: true, children: item.content }, item.id) }),
6944
+ currentLine && /* @__PURE__ */ jsx16(Text15, { color: "gray", dimColor: true, children: currentLine })
6945
+ ] }) });
6946
+ };
6947
+ var StreamingText = memo10(StreamingTextComponent);
6948
+
6949
+ // src/app/ui/App.tsx
6950
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6669
6951
  var SAFE_AUTO_APPROVE_TOOLS = [
6670
6952
  // Comunicação/UI
6671
6953
  "message",
@@ -6683,28 +6965,28 @@ var SAFE_AUTO_APPROVE_TOOLS = [
6683
6965
  "command_status"
6684
6966
  ];
6685
6967
  var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
6686
- const agentInstance = useRef4(null);
6687
- const [history, setHistory] = useState6([]);
6688
- const [statusMessage, setStatusMessage] = useState6(
6968
+ const agentInstance = useRef5(null);
6969
+ const [history, setHistory] = useState7([]);
6970
+ const [statusMessage, setStatusMessage] = useState7(
6689
6971
  "Initializing agent..."
6690
6972
  );
6691
- const [toolsCount, setToolsCount] = useState6(null);
6692
- const [mcpStatus, setMcpStatus] = useState6(
6973
+ const [toolsCount, setToolsCount] = useState7(null);
6974
+ const [mcpStatus, setMcpStatus] = useState7(
6693
6975
  "connecting"
6694
6976
  );
6695
- const [isProcessing, setIsProcessing] = useState6(true);
6696
- const [pendingConfirmation, setPendingConfirmation] = useState6(
6977
+ const [isProcessing, setIsProcessing] = useState7(true);
6978
+ const [pendingConfirmation, setPendingConfirmation] = useState7(
6697
6979
  null
6698
6980
  );
6699
- const [confirmationPreview, setConfirmationPreview] = useState6(
6981
+ const [confirmationPreview, setConfirmationPreview] = useState7(
6700
6982
  null
6701
6983
  );
6702
- const [isInitAgentActive, setIsInitAgentActive] = useState6(false);
6703
- const alwaysAcceptList = useRef4([]);
6984
+ const [isInitAgentActive, setIsInitAgentActive] = useState7(false);
6985
+ const alwaysAcceptList = useRef5([]);
6704
6986
  const workdir = process.cwd();
6705
- const updateCheckRan = useRef4(false);
6706
- const sessionStartTime = useRef4(Date.now());
6707
- const [toolCallCount, setToolCallCount] = useState6(0);
6987
+ const updateCheckRan = useRef5(false);
6988
+ const sessionStartTime = useRef5(Date.now());
6989
+ const [toolCallCount, setToolCallCount] = useState7(0);
6708
6990
  const handleInterrupt = useCallback2(() => {
6709
6991
  if (!isProcessing) return;
6710
6992
  eventBus2.emit("user_interrupt");
@@ -6713,7 +6995,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
6713
6995
  ...prev,
6714
6996
  {
6715
6997
  id: prev.length,
6716
- component: /* @__PURE__ */ jsx16(Text15, { color: "yellow", children: "-- Task cancelled by dev. --" })
6998
+ component: /* @__PURE__ */ jsx17(Text16, { color: "yellow", children: "-- Task cancelled by dev. --" })
6717
6999
  }
6718
7000
  ]);
6719
7001
  }, [isProcessing, eventBus2]);
@@ -6737,11 +7019,11 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
6737
7019
  ...prev,
6738
7020
  {
6739
7021
  id: prev.length,
6740
- component: /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { color: "white", dimColor: true, children: text }) })
7022
+ component: /* @__PURE__ */ jsx17(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { color: "white", dimColor: true, children: text }) })
6741
7023
  },
6742
7024
  {
6743
7025
  id: prev.length + 1,
6744
- component: /* @__PURE__ */ jsx16(
7026
+ component: /* @__PURE__ */ jsx17(
6745
7027
  SlashCommands_default,
6746
7028
  {
6747
7029
  input: text,
@@ -6763,9 +7045,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
6763
7045
  ...prev,
6764
7046
  {
6765
7047
  id: prev.length,
6766
- component: /* @__PURE__ */ jsxs15(Box16, { marginBottom: 1, children: [
6767
- /* @__PURE__ */ jsx16(Text15, { color: "white", bold: true, children: "$ " }),
6768
- /* @__PURE__ */ jsx16(Text15, { color: "white", children: command })
7048
+ component: /* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
7049
+ /* @__PURE__ */ jsx17(Text16, { color: "white", bold: true, children: "$ " }),
7050
+ /* @__PURE__ */ jsx17(Text16, { color: "white", children: command })
6769
7051
  ] })
6770
7052
  }
6771
7053
  ]);
@@ -6784,7 +7066,7 @@ Please use command_status to check the result and report back to the user.`;
6784
7066
  ...prev,
6785
7067
  {
6786
7068
  id: prev.length,
6787
- component: /* @__PURE__ */ jsxs15(Text15, { color: "red", children: [
7069
+ component: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
6788
7070
  "Failed to execute: ",
6789
7071
  result.error || result.message
6790
7072
  ] })
@@ -6797,7 +7079,7 @@ Please use command_status to check the result and report back to the user.`;
6797
7079
  ...prev,
6798
7080
  {
6799
7081
  id: prev.length,
6800
- component: /* @__PURE__ */ jsxs15(Text15, { color: "red", children: [
7082
+ component: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
6801
7083
  "Error: ",
6802
7084
  err.message
6803
7085
  ] })
@@ -6816,8 +7098,8 @@ Please use command_status to check the result and report back to the user.`;
6816
7098
  id: prev.length,
6817
7099
  component: (
6818
7100
  // Uma única Box para o espaçamento
6819
- /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "white", dimColor: true, children: [
6820
- /* @__PURE__ */ jsxs15(Text15, { color: "white", children: [
7101
+ /* @__PURE__ */ jsx17(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "white", dimColor: true, children: [
7102
+ /* @__PURE__ */ jsxs16(Text16, { color: "white", children: [
6821
7103
  ">",
6822
7104
  " "
6823
7105
  ] }),
@@ -6851,8 +7133,8 @@ Please use command_status to check the result and report back to the user.`;
6851
7133
  },
6852
7134
  []
6853
7135
  );
6854
- useEffect5(() => {
6855
- setHistory([{ id: 0, component: /* @__PURE__ */ jsx16(Header, { sessionId: sessionId2, workdir }) }]);
7136
+ useEffect6(() => {
7137
+ setHistory([{ id: 0, component: /* @__PURE__ */ jsx17(Header, { sessionId: sessionId2, workdir }) }]);
6856
7138
  const initializeAgent = async () => {
6857
7139
  try {
6858
7140
  agentInstance.current = new Agent(sessionId2, eventBus2);
@@ -6918,7 +7200,7 @@ Please use command_status to check the result and report back to the user.`;
6918
7200
  ...prev,
6919
7201
  {
6920
7202
  id: prev.length,
6921
- component: /* @__PURE__ */ jsx16(UpdateNotice_default, { message: msg })
7203
+ component: /* @__PURE__ */ jsx17(UpdateNotice_default, { message: msg })
6922
7204
  }
6923
7205
  ]);
6924
7206
  }
@@ -6932,10 +7214,10 @@ Please use command_status to check the result and report back to the user.`;
6932
7214
  }
6933
7215
  let newComponent = null;
6934
7216
  if (parsed.type === "debug") {
6935
- newComponent = /* @__PURE__ */ jsx16(Text15, { color: "gray", children: parsed.message });
7217
+ newComponent = /* @__PURE__ */ jsx17(Text16, { color: "gray", children: parsed.message });
6936
7218
  } else if (parsed.type === "protocol_violation") {
6937
- newComponent = /* @__PURE__ */ jsxs15(
6938
- Box16,
7219
+ newComponent = /* @__PURE__ */ jsxs16(
7220
+ Box17,
6939
7221
  {
6940
7222
  borderStyle: "round",
6941
7223
  borderColor: "yellow",
@@ -6943,14 +7225,14 @@ Please use command_status to check the result and report back to the user.`;
6943
7225
  marginBottom: 1,
6944
7226
  paddingX: 1,
6945
7227
  children: [
6946
- /* @__PURE__ */ jsx16(Text15, { color: "yellow", bold: true, children: "Protocol Violation" }),
6947
- /* @__PURE__ */ jsx16(Text15, { color: "gray", children: parsed.content }),
6948
- /* @__PURE__ */ jsx16(Text15, { color: "yellow", children: parsed.message })
7228
+ /* @__PURE__ */ jsx17(Text16, { color: "yellow", bold: true, children: "Protocol Violation" }),
7229
+ /* @__PURE__ */ jsx17(Text16, { color: "gray", children: parsed.content }),
7230
+ /* @__PURE__ */ jsx17(Text16, { color: "yellow", children: parsed.message })
6949
7231
  ]
6950
7232
  }
6951
7233
  );
6952
7234
  } else if (parsed.type === "error") {
6953
- newComponent = /* @__PURE__ */ jsx16(
7235
+ newComponent = /* @__PURE__ */ jsx17(
6954
7236
  ErrorMessage_default,
6955
7237
  {
6956
7238
  message: parsed.message,
@@ -6960,7 +7242,7 @@ Please use command_status to check the result and report back to the user.`;
6960
7242
  );
6961
7243
  } else if (parsed.type === "tool_call") {
6962
7244
  const nextId2 = history.length;
6963
- newComponent = /* @__PURE__ */ jsx16(
7245
+ newComponent = /* @__PURE__ */ jsx17(
6964
7246
  ToolCallDisplay,
6965
7247
  {
6966
7248
  toolName: parsed.tool_name,
@@ -6969,7 +7251,7 @@ Please use command_status to check the result and report back to the user.`;
6969
7251
  }
6970
7252
  );
6971
7253
  } else if (parsed.type === "tool_result") {
6972
- newComponent = /* @__PURE__ */ jsx16(
7254
+ newComponent = /* @__PURE__ */ jsx17(
6973
7255
  ToolResultDisplay,
6974
7256
  {
6975
7257
  toolName: parsed.tool_name,
@@ -6977,17 +7259,17 @@ Please use command_status to check the result and report back to the user.`;
6977
7259
  }
6978
7260
  );
6979
7261
  } else if (parsed.type === "user_overlay") {
6980
- newComponent = /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
6981
- /* @__PURE__ */ jsxs15(Text15, { color: "magenta", children: [
7262
+ newComponent = /* @__PURE__ */ jsx17(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "gray", children: [
7263
+ /* @__PURE__ */ jsxs16(Text16, { color: "magenta", children: [
6982
7264
  ">",
6983
7265
  " "
6984
7266
  ] }),
6985
7267
  parsed.payload
6986
7268
  ] }) });
6987
7269
  } else if (parsed.type === "reasoning") {
6988
- newComponent = /* @__PURE__ */ jsx16(ReasoningDisplay, { reasoning: parsed.content });
7270
+ newComponent = /* @__PURE__ */ jsx17(ReasoningDisplay, { reasoning: parsed.content });
6989
7271
  } else if (parsed.type === "log") {
6990
- newComponent = /* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
7272
+ newComponent = /* @__PURE__ */ jsxs16(Text16, { color: "gray", children: [
6991
7273
  "\u2139\uFE0F ",
6992
7274
  parsed.message,
6993
7275
  parsed.payload ? `: ${parsed.payload}` : ""
@@ -7020,7 +7302,7 @@ Please use command_status to check the result and report back to the user.`;
7020
7302
  return;
7021
7303
  }
7022
7304
  if (pendingConfirmation) {
7023
- return /* @__PURE__ */ jsx16(
7305
+ return /* @__PURE__ */ jsx17(
7024
7306
  ConfirmationPrompt,
7025
7307
  {
7026
7308
  toolCalls: pendingConfirmation,
@@ -7032,9 +7314,9 @@ Please use command_status to check the result and report back to the user.`;
7032
7314
  }
7033
7315
  );
7034
7316
  }
7035
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
7036
- isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx16(WorkingTimer, {}),
7037
- /* @__PURE__ */ jsx16(
7317
+ return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
7318
+ isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx17(WorkingTimer, { eventBus: eventBus2 }),
7319
+ /* @__PURE__ */ jsx17(
7038
7320
  InputPrompt,
7039
7321
  {
7040
7322
  onSubmit: handleSubmit,
@@ -7045,12 +7327,29 @@ Please use command_status to check the result and report back to the user.`;
7045
7327
  )
7046
7328
  ] });
7047
7329
  };
7048
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
7049
- /* @__PURE__ */ jsx16(Static, { items: history, children: (item) => /* @__PURE__ */ jsx16(Box16, { children: item.component }, item.id) }),
7330
+ return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
7331
+ /* @__PURE__ */ jsx17(Static2, { items: history, children: (item) => /* @__PURE__ */ jsx17(Box17, { children: item.component }, item.id) }),
7332
+ /* @__PURE__ */ jsx17(
7333
+ StreamingText,
7334
+ {
7335
+ eventBus: eventBus2,
7336
+ onReasoningComplete: (reasoning) => {
7337
+ if (reasoning) {
7338
+ setHistory((prev) => [
7339
+ ...prev,
7340
+ {
7341
+ id: prev.length,
7342
+ component: /* @__PURE__ */ jsx17(ReasoningDisplay, { reasoning })
7343
+ }
7344
+ ]);
7345
+ }
7346
+ }
7347
+ }
7348
+ ),
7050
7349
  renderInteractiveComponent()
7051
7350
  ] });
7052
7351
  };
7053
- var App = memo10(AppComponent);
7352
+ var App = memo11(AppComponent);
7054
7353
  var App_default = App;
7055
7354
 
7056
7355
  // src/app/ui/utils/terminalTitle.ts
@@ -7117,4 +7416,4 @@ var props = {
7117
7416
  eventBus,
7118
7417
  sessionId
7119
7418
  };
7120
- render(React10.createElement(App_default, props));
7419
+ render(React11.createElement(App_default, props));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomad-e/bluma-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "BluMa independent agent for automation and advanced software engineering.",
5
5
  "author": "Alex Fonseca",
6
6
  "license": "Apache-2.0",