@nomad-e/bluma-cli 0.0.109 → 0.0.111

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
@@ -176,7 +176,8 @@ async function commandStatus(args) {
176
176
  error: `Command with id "${command_id}" not found. It may have expired or never existed.`
177
177
  };
178
178
  }
179
- if (wait_seconds > 0 && entry.status === "running") {
179
+ const maxWait = Math.min(wait_seconds, 15);
180
+ if (maxWait > 0 && entry.status === "running") {
180
181
  await new Promise((resolve) => {
181
182
  const checkInterval = setInterval(() => {
182
183
  if (entry.status !== "running") {
@@ -187,7 +188,7 @@ async function commandStatus(args) {
187
188
  setTimeout(() => {
188
189
  clearInterval(checkInterval);
189
190
  resolve();
190
- }, wait_seconds * 1e3);
191
+ }, maxWait * 1e3);
191
192
  });
192
193
  }
193
194
  let stdout = entry.stdout;
@@ -326,160 +327,6 @@ var init_async_command = __esm({
326
327
  }
327
328
  });
328
329
 
329
- // src/app/agent/core/llm/tool_call_normalizer.ts
330
- import { randomUUID } from "crypto";
331
- var ToolCallNormalizer;
332
- var init_tool_call_normalizer = __esm({
333
- "src/app/agent/core/llm/tool_call_normalizer.ts"() {
334
- "use strict";
335
- ToolCallNormalizer = class {
336
- /**
337
- * Normaliza a mensagem do assistant, convertendo diferentes formatos de tool calls
338
- */
339
- static normalizeAssistantMessage(message) {
340
- if (message.tool_calls && this.isOpenAIFormat(message.tool_calls)) {
341
- return message;
342
- }
343
- const toolCalls = this.extractToolCalls(message);
344
- if (toolCalls.length > 0) {
345
- return {
346
- role: message.role || "assistant",
347
- content: message.content || null,
348
- tool_calls: toolCalls
349
- };
350
- }
351
- return message;
352
- }
353
- /**
354
- * Verifica se já está no formato OpenAI
355
- */
356
- static isOpenAIFormat(toolCalls) {
357
- if (!Array.isArray(toolCalls) || toolCalls.length === 0) return false;
358
- const firstCall = toolCalls[0];
359
- return typeof firstCall.id === "string" && firstCall.type === "function" && typeof firstCall.function?.name === "string" && typeof firstCall.function?.arguments === "string";
360
- }
361
- /**
362
- * Extrai tool calls de diversos formatos possíveis
363
- */
364
- static extractToolCalls(message) {
365
- const results = [];
366
- if (message.tool_calls && Array.isArray(message.tool_calls)) {
367
- for (const call of message.tool_calls) {
368
- const normalized = this.normalizeToolCall(call);
369
- if (normalized) results.push(normalized);
370
- }
371
- }
372
- if (typeof message.content === "string" && message.content.trim()) {
373
- const extracted = this.extractFromContent(message.content);
374
- results.push(...extracted);
375
- }
376
- if (message.function_call) {
377
- const normalized = this.normalizeToolCall(message.function_call);
378
- if (normalized) results.push(normalized);
379
- }
380
- return results;
381
- }
382
- /**
383
- * Normaliza um único tool call para o formato OpenAI
384
- */
385
- static normalizeToolCall(call) {
386
- try {
387
- if (call.id && call.function?.name) {
388
- return {
389
- id: call.id,
390
- type: "function",
391
- function: {
392
- name: call.function.name,
393
- arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments)
394
- }
395
- };
396
- }
397
- if (call.name) {
398
- return {
399
- id: call.id || randomUUID(),
400
- type: "function",
401
- function: {
402
- name: call.name,
403
- arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments || {})
404
- }
405
- };
406
- }
407
- if (call.function && typeof call.function === "object") {
408
- return {
409
- id: call.id || randomUUID(),
410
- type: "function",
411
- function: {
412
- name: call.function.name,
413
- arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments || {})
414
- }
415
- };
416
- }
417
- return null;
418
- } catch (error) {
419
- console.error("Error normalizing tool call:", error, call);
420
- return null;
421
- }
422
- }
423
- /**
424
- * Extrai tool calls do content (pode estar em markdown, JSON, etc)
425
- */
426
- static extractFromContent(content) {
427
- const results = [];
428
- const cleanContent = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
429
- const jsonMatches = this.extractJsonObjects(cleanContent);
430
- for (const jsonStr of jsonMatches) {
431
- try {
432
- const parsed = JSON.parse(jsonStr);
433
- if (Array.isArray(parsed)) {
434
- for (const call of parsed) {
435
- const normalized = this.normalizeToolCall(call);
436
- if (normalized) results.push(normalized);
437
- }
438
- } else if (parsed.name || parsed.function) {
439
- const normalized = this.normalizeToolCall(parsed);
440
- if (normalized) results.push(normalized);
441
- } else if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
442
- for (const call of parsed.tool_calls) {
443
- const normalized = this.normalizeToolCall(call);
444
- if (normalized) results.push(normalized);
445
- }
446
- }
447
- } catch (e) {
448
- }
449
- }
450
- return results;
451
- }
452
- /**
453
- * Extrai objetos JSON de uma string (suporta múltiplos objetos)
454
- */
455
- static extractJsonObjects(text) {
456
- const results = [];
457
- let depth = 0;
458
- let start = -1;
459
- for (let i = 0; i < text.length; i++) {
460
- if (text[i] === "{") {
461
- if (depth === 0) start = i;
462
- depth++;
463
- } else if (text[i] === "}") {
464
- depth--;
465
- if (depth === 0 && start !== -1) {
466
- results.push(text.substring(start, i + 1));
467
- start = -1;
468
- }
469
- }
470
- }
471
- return results;
472
- }
473
- /**
474
- * Valida se um tool call normalizado é válido
475
- */
476
- static isValidToolCall(call) {
477
- return !!(call.id && call.type === "function" && call.function?.name && typeof call.function.arguments === "string");
478
- }
479
- };
480
- }
481
- });
482
-
483
330
  // src/main.ts
484
331
  import React10 from "react";
485
332
  import { render } from "ink";
@@ -1635,10 +1482,9 @@ var ConfirmationPromptComponent = ({ toolCalls, preview, onDecision }) => {
1635
1482
  var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
1636
1483
 
1637
1484
  // src/app/agent/agent.ts
1638
- import OpenAI from "openai";
1639
1485
  import * as dotenv from "dotenv";
1640
- import path15 from "path";
1641
- import os8 from "os";
1486
+ import path16 from "path";
1487
+ import os9 from "os";
1642
1488
 
1643
1489
  // src/app/agent/tool_invoker.ts
1644
1490
  import { promises as fs8 } from "fs";
@@ -1987,20 +1833,20 @@ ${finalDiff}`,
1987
1833
 
1988
1834
  // src/app/agent/tools/natives/message.ts
1989
1835
  import { v4 as uuidv4 } from "uuid";
1990
- function messageNotifyuser(args) {
1991
- const { message } = args;
1992
- const notification = {
1993
- type: "message_notify_user",
1994
- id: `notify_${uuidv4()}`,
1836
+ function message(args) {
1837
+ const { content, message_type } = args;
1838
+ const result = {
1839
+ type: "message",
1840
+ message_type,
1841
+ id: `msg_${uuidv4()}`,
1995
1842
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1996
1843
  content: {
1997
- format: "markdown",
1998
- body: message
1844
+ body: content
1999
1845
  },
2000
1846
  success: true,
2001
1847
  delivered: true
2002
1848
  };
2003
- return Promise.resolve(notification);
1849
+ return Promise.resolve(result);
2004
1850
  }
2005
1851
 
2006
1852
  // src/app/agent/tools/natives/ls.ts
@@ -2203,10 +2049,10 @@ function validateDescription(desc) {
2203
2049
  }
2204
2050
  return null;
2205
2051
  }
2206
- function createResult(success, message) {
2052
+ function createResult(success, message2) {
2207
2053
  return {
2208
2054
  success,
2209
- message,
2055
+ message: message2,
2210
2056
  tasks: taskStore,
2211
2057
  stats: calculateStats()
2212
2058
  };
@@ -3409,6 +3255,42 @@ async function searchWeb(args) {
3409
3255
  }
3410
3256
  }
3411
3257
 
3258
+ // src/app/agent/tools/natives/load_skill.ts
3259
+ var globalContext = null;
3260
+ function initializeSkillContext(ctx) {
3261
+ globalContext = ctx;
3262
+ }
3263
+ async function loadSkill(args) {
3264
+ const { skill_name } = args;
3265
+ if (!skill_name || typeof skill_name !== "string") {
3266
+ return {
3267
+ success: false,
3268
+ message: "skill_name is required"
3269
+ };
3270
+ }
3271
+ if (!globalContext?.skillLoader) {
3272
+ return {
3273
+ success: false,
3274
+ message: "Skill system not initialized. No skills available."
3275
+ };
3276
+ }
3277
+ const skill = globalContext.skillLoader.load(skill_name);
3278
+ if (!skill) {
3279
+ const available = globalContext.skillLoader.listAvailable();
3280
+ const availableNames = available.map((s) => s.name).join(", ") || "none";
3281
+ return {
3282
+ success: false,
3283
+ message: `Skill "${skill_name}" not found. Available skills: ${availableNames}`
3284
+ };
3285
+ }
3286
+ return {
3287
+ success: true,
3288
+ message: `Skill "${skill_name}" loaded.`,
3289
+ skill_name: skill.name,
3290
+ description: skill.description
3291
+ };
3292
+ }
3293
+
3412
3294
  // src/app/agent/tool_invoker.ts
3413
3295
  var ToolInvoker = class {
3414
3296
  // Mapa privado para associar nomes de ferramentas às suas funções de implementação.
@@ -3452,13 +3334,13 @@ var ToolInvoker = class {
3452
3334
  this.toolImplementations.set("command_status", commandStatus);
3453
3335
  this.toolImplementations.set("send_command_input", sendCommandInput);
3454
3336
  this.toolImplementations.set("kill_command", killCommand);
3455
- this.toolImplementations.set("message_notify_user", messageNotifyuser);
3337
+ this.toolImplementations.set("message", message);
3456
3338
  this.toolImplementations.set("todo", todo);
3457
3339
  this.toolImplementations.set("task_boundary", taskBoundary);
3458
3340
  this.toolImplementations.set("create_artifact", createArtifact);
3459
3341
  this.toolImplementations.set("read_artifact", readArtifact);
3460
3342
  this.toolImplementations.set("search_web", searchWeb);
3461
- this.toolImplementations.set("agent_end_turn", async () => ({ success: true, message: "Task ended by agent." }));
3343
+ this.toolImplementations.set("load_skill", loadSkill);
3462
3344
  }
3463
3345
  /**
3464
3346
  * Retorna a lista de definições de todas as ferramentas nativas carregadas.
@@ -3519,7 +3401,7 @@ var MCPClient = class {
3519
3401
  const __filename = fileURLToPath2(import.meta.url);
3520
3402
  const __dirname = path11.dirname(__filename);
3521
3403
  const defaultConfigPath = path11.resolve(__dirname, "config", "bluma-mcp.json");
3522
- const userConfigPath = path11.join(os4.homedir(), ".bluma-cli", "bluma-mcp.json");
3404
+ const userConfigPath = path11.join(os4.homedir(), ".bluma", "bluma-mcp.json");
3523
3405
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
3524
3406
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
3525
3407
  const mergedConfig = {
@@ -3688,7 +3570,7 @@ var AdvancedFeedbackSystem = class {
3688
3570
  };
3689
3571
 
3690
3572
  // src/app/agent/bluma/core/bluma.ts
3691
- import path14 from "path";
3573
+ import path15 from "path";
3692
3574
 
3693
3575
  // src/app/agent/session_manager/session_manager.ts
3694
3576
  import path12 from "path";
@@ -3733,7 +3615,7 @@ function expandHome(p) {
3733
3615
  return p;
3734
3616
  }
3735
3617
  function getPreferredAppDir() {
3736
- const fixed = path12.join(os5.homedir(), ".bluma-cli");
3618
+ const fixed = path12.join(os5.homedir(), ".bluma");
3737
3619
  return path12.resolve(expandHome(fixed));
3738
3620
  }
3739
3621
  async function safeRenameWithRetry(src, dest, maxRetries = 6) {
@@ -3854,10 +3736,180 @@ async function saveSessionHistory(sessionFile, history) {
3854
3736
  }
3855
3737
 
3856
3738
  // src/app/agent/core/prompt/prompt_builder.ts
3857
- import os6 from "os";
3739
+ import os7 from "os";
3740
+ import fs12 from "fs";
3741
+ import path14 from "path";
3742
+ import { execSync } from "child_process";
3743
+
3744
+ // src/app/agent/skills/skill_loader.ts
3858
3745
  import fs11 from "fs";
3859
3746
  import path13 from "path";
3860
- import { execSync } from "child_process";
3747
+ import os6 from "os";
3748
+ var SkillLoader = class {
3749
+ projectSkillsDir;
3750
+ globalSkillsDir;
3751
+ cache = /* @__PURE__ */ new Map();
3752
+ constructor(projectRoot) {
3753
+ this.projectSkillsDir = path13.join(projectRoot, ".bluma", "skills");
3754
+ this.globalSkillsDir = path13.join(os6.homedir(), ".bluma", "skills");
3755
+ }
3756
+ /**
3757
+ * Lista skills disponíveis de ambas as fontes
3758
+ * Skills de projeto têm prioridade sobre globais com mesmo nome
3759
+ */
3760
+ listAvailable() {
3761
+ const skills = /* @__PURE__ */ new Map();
3762
+ const globalSkills = this.listFromDir(this.globalSkillsDir, "global");
3763
+ for (const skill of globalSkills) {
3764
+ skills.set(skill.name, skill);
3765
+ }
3766
+ const projectSkills = this.listFromDir(this.projectSkillsDir, "project");
3767
+ for (const skill of projectSkills) {
3768
+ skills.set(skill.name, skill);
3769
+ }
3770
+ return Array.from(skills.values());
3771
+ }
3772
+ /**
3773
+ * Lista skills de um diretório específico
3774
+ */
3775
+ listFromDir(dir, source) {
3776
+ if (!fs11.existsSync(dir)) return [];
3777
+ try {
3778
+ return fs11.readdirSync(dir).filter((d) => {
3779
+ const fullPath = path13.join(dir, d);
3780
+ return fs11.statSync(fullPath).isDirectory() && fs11.existsSync(path13.join(fullPath, "SKILL.md"));
3781
+ }).map((d) => this.loadMetadataFromPath(path13.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
3782
+ } catch {
3783
+ return [];
3784
+ }
3785
+ }
3786
+ /**
3787
+ * Carrega metadata de um path específico
3788
+ */
3789
+ loadMetadataFromPath(skillPath, skillName, source) {
3790
+ if (!fs11.existsSync(skillPath)) return null;
3791
+ try {
3792
+ const raw = fs11.readFileSync(skillPath, "utf-8");
3793
+ const parsed = this.parseFrontmatter(raw);
3794
+ return {
3795
+ name: parsed.name || skillName,
3796
+ description: parsed.description || "",
3797
+ source,
3798
+ version: parsed.version,
3799
+ author: parsed.author,
3800
+ license: parsed.license
3801
+ };
3802
+ } catch {
3803
+ return null;
3804
+ }
3805
+ }
3806
+ /**
3807
+ * Carrega skill completa - procura primeiro em projeto, depois global
3808
+ */
3809
+ load(name) {
3810
+ if (this.cache.has(name)) return this.cache.get(name);
3811
+ const projectPath = path13.join(this.projectSkillsDir, name, "SKILL.md");
3812
+ if (fs11.existsSync(projectPath)) {
3813
+ const skill = this.loadFromPath(projectPath, name, "project");
3814
+ if (skill) {
3815
+ this.cache.set(name, skill);
3816
+ return skill;
3817
+ }
3818
+ }
3819
+ const globalPath = path13.join(this.globalSkillsDir, name, "SKILL.md");
3820
+ if (fs11.existsSync(globalPath)) {
3821
+ const skill = this.loadFromPath(globalPath, name, "global");
3822
+ if (skill) {
3823
+ this.cache.set(name, skill);
3824
+ return skill;
3825
+ }
3826
+ }
3827
+ return null;
3828
+ }
3829
+ /**
3830
+ * Carrega skill de um path específico
3831
+ */
3832
+ loadFromPath(skillPath, name, source) {
3833
+ try {
3834
+ const raw = fs11.readFileSync(skillPath, "utf-8");
3835
+ const parsed = this.parseFrontmatter(raw);
3836
+ return {
3837
+ name: parsed.name || name,
3838
+ description: parsed.description || "",
3839
+ content: parsed.content.trim(),
3840
+ source,
3841
+ version: parsed.version,
3842
+ author: parsed.author,
3843
+ license: parsed.license
3844
+ };
3845
+ } catch {
3846
+ return null;
3847
+ }
3848
+ }
3849
+ /**
3850
+ * Parse simples de YAML frontmatter
3851
+ */
3852
+ parseFrontmatter(raw) {
3853
+ const lines = raw.split("\n");
3854
+ if (lines[0]?.trim() !== "---") {
3855
+ return { content: raw };
3856
+ }
3857
+ let endIndex = -1;
3858
+ for (let i = 1; i < lines.length; i++) {
3859
+ if (lines[i]?.trim() === "---") {
3860
+ endIndex = i;
3861
+ break;
3862
+ }
3863
+ }
3864
+ if (endIndex === -1) {
3865
+ return { content: raw };
3866
+ }
3867
+ const frontmatter = {};
3868
+ for (let i = 1; i < endIndex; i++) {
3869
+ const line = lines[i];
3870
+ const colonIndex = line.indexOf(":");
3871
+ if (colonIndex > 0) {
3872
+ const key = line.slice(0, colonIndex).trim();
3873
+ const value = line.slice(colonIndex + 1).trim();
3874
+ frontmatter[key] = value;
3875
+ }
3876
+ }
3877
+ const content = lines.slice(endIndex + 1).join("\n");
3878
+ return {
3879
+ name: frontmatter["name"],
3880
+ description: frontmatter["description"],
3881
+ version: frontmatter["version"],
3882
+ author: frontmatter["author"],
3883
+ license: frontmatter["license"],
3884
+ content
3885
+ };
3886
+ }
3887
+ /**
3888
+ * Limpa o cache
3889
+ */
3890
+ clearCache() {
3891
+ this.cache.clear();
3892
+ }
3893
+ /**
3894
+ * Verifica se uma skill existe (em projeto ou global)
3895
+ */
3896
+ exists(name) {
3897
+ const projectPath = path13.join(this.projectSkillsDir, name, "SKILL.md");
3898
+ const globalPath = path13.join(this.globalSkillsDir, name, "SKILL.md");
3899
+ return fs11.existsSync(projectPath) || fs11.existsSync(globalPath);
3900
+ }
3901
+ /**
3902
+ * Retorna os diretórios de skills
3903
+ */
3904
+ getSkillsDirs() {
3905
+ return {
3906
+ project: this.projectSkillsDir,
3907
+ global: this.globalSkillsDir
3908
+ };
3909
+ }
3910
+ };
3911
+
3912
+ // src/app/agent/core/prompt/prompt_builder.ts
3861
3913
  function getNodeVersion() {
3862
3914
  try {
3863
3915
  return process.version;
@@ -3885,10 +3937,10 @@ function getGitBranch(dir) {
3885
3937
  }
3886
3938
  function getPackageManager(dir) {
3887
3939
  try {
3888
- if (fs11.existsSync(path13.join(dir, "pnpm-lock.yaml"))) return "pnpm";
3889
- if (fs11.existsSync(path13.join(dir, "yarn.lock"))) return "yarn";
3890
- if (fs11.existsSync(path13.join(dir, "bun.lockb"))) return "bun";
3891
- if (fs11.existsSync(path13.join(dir, "package-lock.json"))) return "npm";
3940
+ if (fs12.existsSync(path14.join(dir, "pnpm-lock.yaml"))) return "pnpm";
3941
+ if (fs12.existsSync(path14.join(dir, "yarn.lock"))) return "yarn";
3942
+ if (fs12.existsSync(path14.join(dir, "bun.lockb"))) return "bun";
3943
+ if (fs12.existsSync(path14.join(dir, "package-lock.json"))) return "npm";
3892
3944
  return "unknown";
3893
3945
  } catch {
3894
3946
  return "unknown";
@@ -3896,9 +3948,9 @@ function getPackageManager(dir) {
3896
3948
  }
3897
3949
  function getProjectType(dir) {
3898
3950
  try {
3899
- const files = fs11.readdirSync(dir);
3951
+ const files = fs12.readdirSync(dir);
3900
3952
  if (files.includes("package.json")) {
3901
- const pkg = JSON.parse(fs11.readFileSync(path13.join(dir, "package.json"), "utf-8"));
3953
+ const pkg = JSON.parse(fs12.readFileSync(path14.join(dir, "package.json"), "utf-8"));
3902
3954
  if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
3903
3955
  if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
3904
3956
  if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
@@ -3917,9 +3969,9 @@ function getProjectType(dir) {
3917
3969
  }
3918
3970
  function getTestFramework(dir) {
3919
3971
  try {
3920
- const pkgPath = path13.join(dir, "package.json");
3921
- if (fs11.existsSync(pkgPath)) {
3922
- const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3972
+ const pkgPath = path14.join(dir, "package.json");
3973
+ if (fs12.existsSync(pkgPath)) {
3974
+ const pkg = JSON.parse(fs12.readFileSync(pkgPath, "utf-8"));
3923
3975
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3924
3976
  if (deps.jest) return "jest";
3925
3977
  if (deps.vitest) return "vitest";
@@ -3928,7 +3980,7 @@ function getTestFramework(dir) {
3928
3980
  if (deps["@playwright/test"]) return "playwright";
3929
3981
  if (deps.cypress) return "cypress";
3930
3982
  }
3931
- if (fs11.existsSync(path13.join(dir, "pytest.ini")) || fs11.existsSync(path13.join(dir, "conftest.py"))) return "pytest";
3983
+ if (fs12.existsSync(path14.join(dir, "pytest.ini")) || fs12.existsSync(path14.join(dir, "conftest.py"))) return "pytest";
3932
3984
  return "unknown";
3933
3985
  } catch {
3934
3986
  return "unknown";
@@ -3936,9 +3988,9 @@ function getTestFramework(dir) {
3936
3988
  }
3937
3989
  function getTestCommand(dir) {
3938
3990
  try {
3939
- const pkgPath = path13.join(dir, "package.json");
3940
- if (fs11.existsSync(pkgPath)) {
3941
- const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3991
+ const pkgPath = path14.join(dir, "package.json");
3992
+ if (fs12.existsSync(pkgPath)) {
3993
+ const pkg = JSON.parse(fs12.readFileSync(pkgPath, "utf-8"));
3942
3994
  if (pkg.scripts?.test) return `npm test`;
3943
3995
  if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
3944
3996
  }
@@ -4006,7 +4058,7 @@ You MUST adapt all commands to this environment. Use the correct package manager
4006
4058
  ## How You Work
4007
4059
 
4008
4060
  ### For Multi-Step Tasks:
4009
- 1. **Acknowledge** - Confirm the request immediately via message_notify_user
4061
+ 1. **Acknowledge** - Confirm the request immediately via message
4010
4062
  2. **Plan** - Use the TODO tool to create a structured plan
4011
4063
  3. **Execute** - Implement with clear narration of each step
4012
4064
  4. **Test** - Run tests after any code changes (MANDATORY for production code)
@@ -4017,25 +4069,11 @@ You MUST adapt all commands to this environment. Use the correct package manager
4017
4069
  - Quick acknowledgment + immediate action
4018
4070
  - No TODO needed for single operations
4019
4071
 
4020
- ### Testing Philosophy (CRITICAL):
4021
- You are a **teammate who writes tests**, not an assistant who skips them.
4022
-
4023
- **ALWAYS run tests when:**
4024
- - You modify any function or module
4025
- - You add new functionality
4026
- - You fix a bug (write a regression test first)
4027
- - The user asks for a feature
4072
+ ### Testing Philosophy:
4073
+ Run tests when modifying code. Use the \`testing\` skill for detailed best practices.
4028
4074
 
4029
- **Test workflow:**
4030
- 1. Before editing: Run existing tests to ensure baseline passes
4031
- 2. After editing: Run tests to verify nothing broke
4032
- 3. For new features: Write tests alongside implementation
4033
- 4. For bugs: Write failing test \u2192 fix \u2192 verify test passes
4034
-
4035
- **Commands you should know:**
4075
+ **Commands:**
4036
4076
  - Run tests: {test_command}
4037
- - Run single test: Check project conventions
4038
- - Watch mode: Usually \`npm run test:watch\` or \`npm test -- --watch\`
4039
4077
  </workflow>
4040
4078
 
4041
4079
  ---
@@ -4050,8 +4088,8 @@ You are a **teammate who writes tests**, not an assistant who skips them.
4050
4088
  - **Check file exists** before attempting edits
4051
4089
 
4052
4090
  ### Safe Auto-Approved Tools (no confirmation needed):
4053
- - message_notify_user
4054
- - ls_tool
4091
+ - message
4092
+ - ls_tool
4055
4093
  - read_file_lines
4056
4094
  - count_file_lines
4057
4095
  - todo
@@ -4102,7 +4140,7 @@ You MUST use \`command_status\` to get the output.
4102
4140
  [Step 2] command_status({ command_id: "cmd_001", wait_seconds: 120 })
4103
4141
  \u2192 { status: "completed", exit_code: 0, stdout: "added 150 packages..." }
4104
4142
 
4105
- [Step 3] message_notify_user("Dependencies installed successfully.")
4143
+ [Step 3] message({ content: "Dependencies installed.", message_type: "info" })
4106
4144
  \`\`\`
4107
4145
 
4108
4146
  **[OK] Run tests:**
@@ -4131,7 +4169,7 @@ You MUST use \`command_status\` to get the output.
4131
4169
  [Step 2] command_status({ command_id: "cmd_004", wait_seconds: 10 })
4132
4170
  \u2192 { status: "running" }
4133
4171
 
4134
- [Step 3] message_notify_user("Building Docker image, please wait...")
4172
+ [Step 3] message({ content: "Building Docker image...", message_type: "info" })
4135
4173
 
4136
4174
  [Step 4] command_status({ command_id: "cmd_004", wait_seconds: 10 })
4137
4175
  \u2192 { status: "completed", exit_code: 0, stdout: "Successfully built abc123" }
@@ -4140,7 +4178,7 @@ You MUST use \`command_status\` to get the output.
4140
4178
  **[WRONG] Never forget command_status:**
4141
4179
  \`\`\`
4142
4180
  shell_command({ command: "npm install" })
4143
- message_notify_user("Installed!") // WRONG: You don't know if it succeeded!
4181
+ message({ content: "Installed!", message_type: "info" }) // WRONG: You don't know if it succeeded!
4144
4182
  \`\`\`
4145
4183
 
4146
4184
  **[WRONG] Never use long waits (blocks user):**
@@ -4185,39 +4223,29 @@ Examples:
4185
4223
  - Celebrate wins, acknowledge mistakes
4186
4224
 
4187
4225
  **Message via tool only:**
4188
- - Use message_notify_user for ALL communication
4189
- - Never assume the user sees internal state
4190
- - Report progress proactively
4226
+ - message with \`result\` = END your turn (after questions/completions)
4227
+ - message with \`info\` = progress update, you continue working
4228
+
4229
+ **CRITICAL: After asking questions, ALWAYS use result!**
4191
4230
 
4192
- **Example flow:**
4231
+ **Asking question (result):**
4193
4232
  \`\`\`
4194
- message_notify_user("Alex, understood. Adding the auth middleware. Let me check the current implementation first.")
4195
- [uses read_file_lines]
4196
- message_notify_user("Found the router at /src/routes/index.ts. I'll add the middleware before the protected routes.")
4197
- [uses edit_tool]
4198
- message_notify_user("Done. Running tests to verify...")
4199
- [uses shell_command with {test_command}]
4200
- message_notify_user("All 47 tests pass. The middleware is working correctly.")
4233
+ message({ content: "Project or global?", message_type: "result" })
4234
+ // STOP and wait for user
4201
4235
  \`\`\`
4202
- </communication_style>
4203
-
4204
- ---
4205
4236
 
4206
- <git_guidelines>
4207
- ## Git Best Practices
4208
-
4209
- When in a git repository:
4210
- - **Review changes**: \`git diff HEAD\` before any commit
4211
- - **Commit format**: Conventional Commits (feat:, fix:, chore:, docs:, test:, refactor:)
4212
- - **NEVER push** unless explicitly asked
4213
- - **NEVER use destructive commands**: reset --hard, rebase -i, force push
4237
+ **Progress then continue (info):**
4238
+ \`\`\`
4239
+ message({ content: "Installing...", message_type: "info" })
4240
+ // continue with next tool
4241
+ \`\`\`
4214
4242
 
4215
- Good commit messages:
4216
- - feat: add user authentication middleware
4217
- - fix: resolve race condition in payment processing
4218
- - test: add unit tests for auth module
4219
- - chore: update dependencies
4220
- </git_guidelines>
4243
+ **Task done (result):**
4244
+ \`\`\`
4245
+ message({ content: "Done. All tests pass.", message_type: "result" })
4246
+ // STOP - finished
4247
+ \`\`\`
4248
+ </communication_style>
4221
4249
 
4222
4250
  ---
4223
4251
 
@@ -4254,15 +4282,15 @@ You communicate because you respect your teammate.
4254
4282
  Let's build something great, {username}.
4255
4283
  </personality>
4256
4284
  `;
4257
- function getUnifiedSystemPrompt() {
4285
+ function getUnifiedSystemPrompt(availableSkills) {
4258
4286
  const cwd = process.cwd();
4259
4287
  const env = {
4260
- os_type: os6.type(),
4261
- os_version: os6.release(),
4262
- architecture: os6.arch(),
4288
+ os_type: os7.type(),
4289
+ os_version: os7.release(),
4290
+ architecture: os7.arch(),
4263
4291
  workdir: cwd,
4264
4292
  shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
4265
- username: os6.userInfo().username,
4293
+ username: os7.userInfo().username,
4266
4294
  current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
4267
4295
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
4268
4296
  is_git_repo: isGitRepo(cwd) ? "yes" : "no",
@@ -4274,15 +4302,170 @@ function getUnifiedSystemPrompt() {
4274
4302
  test_framework: getTestFramework(cwd),
4275
4303
  test_command: getTestCommand(cwd)
4276
4304
  };
4277
- return Object.entries(env).reduce(
4278
- (prompt, [key, value]) => prompt.replaceAll(`{${key}}`, value),
4305
+ let prompt = Object.entries(env).reduce(
4306
+ (p, [key, value]) => p.replaceAll(`{${key}}`, value),
4279
4307
  SYSTEM_PROMPT
4280
4308
  );
4309
+ if (availableSkills && availableSkills.length > 0) {
4310
+ const skillsList = availableSkills.map((s) => `- ${s.name}: ${s.description}`).join("\n");
4311
+ prompt += `
4312
+
4313
+ ---
4314
+
4315
+ <available_skills>
4316
+ ## Specialized Skills
4317
+
4318
+ You have access to specialized knowledge modules called **skills**. Skills extend your capabilities with domain-specific expertise, workflows, and best practices.
4319
+
4320
+ **Available skills:**
4321
+ ${skillsList}
4322
+
4323
+ **Skill Lifecycle:**
4324
+ - Skills are **ephemeral** - they exist only for the current task
4325
+ - After completing a task, skill context is discarded
4326
+ - You MUST reload a skill with \`load_skill\` each time you need it
4327
+ - Never assume a skill is still loaded from a previous task
4328
+
4329
+ ---
4330
+
4331
+ ### Skill Selection Process
4332
+
4333
+ **BEFORE starting any task, follow this decision process:**
4334
+
4335
+ 1. **Understand the task** - What is the user trying to accomplish?
4336
+ 2. **Identify the domain** - What area of expertise does this task require? (e.g., version control, testing, deployment, documentation)
4337
+ 3. **Match against skills** - Read each skill's description above. Does any skill's domain match the task?
4338
+ 4. **Load if matched** - If a skill matches, load it FIRST with \`load_skill\` before taking any action
4339
+ 5. **Follow skill instructions** - Once loaded, the skill provides specific workflows to follow
4340
+
4341
+ **Decision examples:**
4342
+ \`\`\`
4343
+ Task: "Save my changes with a good message"
4344
+ \u2192 Domain: Version control, commits
4345
+ \u2192 Check skills: Is there a skill for git/commits/version control?
4346
+ \u2192 If yes: load_skill({ skill_name: "<matching-skill>" })
4347
+
4348
+ Task: "Make sure this code works correctly"
4349
+ \u2192 Domain: Testing, validation, quality assurance
4350
+ \u2192 Check skills: Is there a skill for testing?
4351
+ \u2192 If yes: load_skill({ skill_name: "<matching-skill>" })
4352
+
4353
+ Task: "Release this to users"
4354
+ \u2192 Domain: Publishing, deployment, release
4355
+ \u2192 Check skills: Is there a skill for publishing/releasing?
4356
+ \u2192 If yes: load_skill({ skill_name: "<matching-skill>" })
4357
+ \`\`\`
4358
+
4359
+ **Key principle:** Match the TASK DOMAIN to the SKILL DESCRIPTION, not specific words.
4360
+
4361
+ ---
4362
+
4363
+ ### Skill Interpretation Rules (CRITICAL)
4364
+
4365
+ When you load a skill with \`load_skill\`, you receive a SKILL.md file. **ALWAYS read the YAML frontmatter header FIRST** before reading the body.
4366
+
4367
+ **Frontmatter Structure:**
4368
+ \`\`\`yaml
4369
+ ---
4370
+ # IDENTITY
4371
+ name: skill-name # Unique identifier
4372
+ description: ... # Purpose of the skill
4373
+ version: 1.0.0
4374
+ author: ...
4375
+ license: ...
4376
+
4377
+ # DEPENDENCIES
4378
+ depends_on: # Other skills this skill may delegate to
4379
+ - other-skill-name
4380
+
4381
+ # TOOLS
4382
+ tools:
4383
+ required: # Tools you MUST use for this skill
4384
+ - shell_command
4385
+ - command_status
4386
+ recommended: # Tools that enhance execution
4387
+ - read_file_lines
4388
+ ---
4389
+ \`\`\`
4390
+
4391
+ **Interpretation Flow:**
4392
+ \`\`\`
4393
+ 1. LOAD SKILL
4394
+ load_skill({ skill_name: "npm-publish" })
4395
+
4396
+ 2. READ FRONTMATTER FIRST
4397
+ - name: npm-publish
4398
+ - depends_on: [git-conventional]
4399
+ - tools.required: [shell_command, command_status]
4400
+ - tools.recommended: [read_file_lines, message]
4401
+
4402
+ 3. VERIFY TOOLS
4403
+ Are required tools available? If not, inform user.
4404
+
4405
+ 4. READ SKILL BODY
4406
+ Workflow instructions, examples, rules
4407
+
4408
+ 5. EXECUTE WORKFLOW
4409
+ - Use the REQUIRED tools from frontmatter
4410
+ - When body says "delegate to X":
4411
+ a. Verify X is in depends_on
4412
+ b. load_skill({ skill_name: "X" })
4413
+ c. Execute X workflow
4414
+ d. Return to primary skill
4415
+ - Continue until workflow complete
4416
+ \`\`\`
4417
+
4418
+ **Field Meanings:**
4419
+ | Field | Purpose | Your Action |
4420
+ |-------|---------|-------------|
4421
+ | \`name\` | Skill identifier | Confirm correct skill loaded |
4422
+ | \`description\` | Skill purpose | Understand what this skill does |
4423
+ | \`depends_on\` | Skill dependencies | Know which skills can be delegated to |
4424
+ | \`tools.required\` | Mandatory tools | MUST use these tools for execution |
4425
+ | \`tools.recommended\` | Optional tools | Use if helpful |
4426
+
4427
+ **Orchestration Example:**
4428
+ \`\`\`
4429
+ User: "Publish the package"
4430
+
4431
+ 1. Match domain -> npm-publish skill
4432
+ 2. load_skill({ skill_name: "npm-publish" })
4433
+
4434
+ 3. Read frontmatter:
4435
+ - depends_on: [git-conventional]
4436
+ - tools.required: [shell_command, command_status]
4437
+
4438
+ 4. Read body -> Workflow says:
4439
+ "Step 1: If uncommitted changes, delegate to git-conventional"
4440
+
4441
+ 5. Check git status with shell_command (required tool)
4442
+ Found changes
4443
+
4444
+ 6. Delegate: load_skill({ skill_name: "git-conventional" })
4445
+ - Read git-conventional frontmatter
4446
+ - Follow its workflow for commit
4447
+ - Commit done
4448
+
4449
+ 7. Return to npm-publish Step 2: npm version patch
4450
+ 8. Step 3: npm publish
4451
+ 9. Done
4452
+ \`\`\`
4453
+
4454
+ **Rules:**
4455
+ - Frontmatter is the SPECIFICATION - read it first
4456
+ - Body is the WORKFLOW - execute after understanding spec
4457
+ - \`depends_on\` authorizes delegation - only delegate to listed skills
4458
+ - \`tools.required\` are mandatory - use them for execution
4459
+ - The skill body tells you WHEN to delegate, not the frontmatter
4460
+ </available_skills>
4461
+ `;
4462
+ }
4463
+ return prompt;
4281
4464
  }
4282
4465
  function isGitRepo(dir) {
4283
4466
  try {
4284
- const gitPath = path13.join(dir, ".git");
4285
- return fs11.existsSync(gitPath) && fs11.lstatSync(gitPath).isDirectory();
4467
+ const gitPath = path14.join(dir, ".git");
4468
+ return fs12.existsSync(gitPath) && fs12.lstatSync(gitPath).isDirectory();
4286
4469
  } catch {
4287
4470
  return false;
4288
4471
  }
@@ -4338,8 +4521,155 @@ function createApiContextWindow(fullHistory, maxTurns) {
4338
4521
  return finalContext;
4339
4522
  }
4340
4523
 
4524
+ // src/app/agent/core/llm/tool_call_normalizer.ts
4525
+ import { randomUUID } from "crypto";
4526
+ var ToolCallNormalizer = class {
4527
+ /**
4528
+ * Normaliza a mensagem do assistant, convertendo diferentes formatos de tool calls
4529
+ */
4530
+ static normalizeAssistantMessage(message2) {
4531
+ if (message2.tool_calls && this.isOpenAIFormat(message2.tool_calls)) {
4532
+ return message2;
4533
+ }
4534
+ const toolCalls = this.extractToolCalls(message2);
4535
+ if (toolCalls.length > 0) {
4536
+ return {
4537
+ role: message2.role || "assistant",
4538
+ content: message2.content || null,
4539
+ tool_calls: toolCalls
4540
+ };
4541
+ }
4542
+ return message2;
4543
+ }
4544
+ /**
4545
+ * Verifica se já está no formato OpenAI
4546
+ */
4547
+ static isOpenAIFormat(toolCalls) {
4548
+ if (!Array.isArray(toolCalls) || toolCalls.length === 0) return false;
4549
+ const firstCall = toolCalls[0];
4550
+ return typeof firstCall.id === "string" && firstCall.type === "function" && typeof firstCall.function?.name === "string" && typeof firstCall.function?.arguments === "string";
4551
+ }
4552
+ /**
4553
+ * Extrai tool calls de diversos formatos possíveis
4554
+ */
4555
+ static extractToolCalls(message2) {
4556
+ const results = [];
4557
+ if (message2.tool_calls && Array.isArray(message2.tool_calls)) {
4558
+ for (const call of message2.tool_calls) {
4559
+ const normalized = this.normalizeToolCall(call);
4560
+ if (normalized) results.push(normalized);
4561
+ }
4562
+ }
4563
+ if (typeof message2.content === "string" && message2.content.trim()) {
4564
+ const extracted = this.extractFromContent(message2.content);
4565
+ results.push(...extracted);
4566
+ }
4567
+ if (message2.function_call) {
4568
+ const normalized = this.normalizeToolCall(message2.function_call);
4569
+ if (normalized) results.push(normalized);
4570
+ }
4571
+ return results;
4572
+ }
4573
+ /**
4574
+ * Normaliza um único tool call para o formato OpenAI
4575
+ */
4576
+ static normalizeToolCall(call) {
4577
+ try {
4578
+ if (call.id && call.function?.name) {
4579
+ return {
4580
+ id: call.id,
4581
+ type: "function",
4582
+ function: {
4583
+ name: call.function.name,
4584
+ arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments)
4585
+ }
4586
+ };
4587
+ }
4588
+ if (call.name) {
4589
+ return {
4590
+ id: call.id || randomUUID(),
4591
+ type: "function",
4592
+ function: {
4593
+ name: call.name,
4594
+ arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments || {})
4595
+ }
4596
+ };
4597
+ }
4598
+ if (call.function && typeof call.function === "object") {
4599
+ return {
4600
+ id: call.id || randomUUID(),
4601
+ type: "function",
4602
+ function: {
4603
+ name: call.function.name,
4604
+ arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments || {})
4605
+ }
4606
+ };
4607
+ }
4608
+ return null;
4609
+ } catch (error) {
4610
+ console.error("Error normalizing tool call:", error, call);
4611
+ return null;
4612
+ }
4613
+ }
4614
+ /**
4615
+ * Extrai tool calls do content (pode estar em markdown, JSON, etc)
4616
+ */
4617
+ static extractFromContent(content) {
4618
+ const results = [];
4619
+ const cleanContent = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
4620
+ const jsonMatches = this.extractJsonObjects(cleanContent);
4621
+ for (const jsonStr of jsonMatches) {
4622
+ try {
4623
+ const parsed = JSON.parse(jsonStr);
4624
+ if (Array.isArray(parsed)) {
4625
+ for (const call of parsed) {
4626
+ const normalized = this.normalizeToolCall(call);
4627
+ if (normalized) results.push(normalized);
4628
+ }
4629
+ } else if (parsed.name || parsed.function) {
4630
+ const normalized = this.normalizeToolCall(parsed);
4631
+ if (normalized) results.push(normalized);
4632
+ } else if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
4633
+ for (const call of parsed.tool_calls) {
4634
+ const normalized = this.normalizeToolCall(call);
4635
+ if (normalized) results.push(normalized);
4636
+ }
4637
+ }
4638
+ } catch (e) {
4639
+ }
4640
+ }
4641
+ return results;
4642
+ }
4643
+ /**
4644
+ * Extrai objetos JSON de uma string (suporta múltiplos objetos)
4645
+ */
4646
+ static extractJsonObjects(text) {
4647
+ const results = [];
4648
+ let depth = 0;
4649
+ let start = -1;
4650
+ for (let i = 0; i < text.length; i++) {
4651
+ if (text[i] === "{") {
4652
+ if (depth === 0) start = i;
4653
+ depth++;
4654
+ } else if (text[i] === "}") {
4655
+ depth--;
4656
+ if (depth === 0 && start !== -1) {
4657
+ results.push(text.substring(start, i + 1));
4658
+ start = -1;
4659
+ }
4660
+ }
4661
+ }
4662
+ return results;
4663
+ }
4664
+ /**
4665
+ * Valida se um tool call normalizado é válido
4666
+ */
4667
+ static isValidToolCall(call) {
4668
+ return !!(call.id && call.type === "function" && call.function?.name && typeof call.function.arguments === "string");
4669
+ }
4670
+ };
4671
+
4341
4672
  // src/app/agent/bluma/core/bluma.ts
4342
- init_tool_call_normalizer();
4343
4673
  var BluMaAgent = class {
4344
4674
  llm;
4345
4675
  deploymentName;
@@ -4349,6 +4679,7 @@ var BluMaAgent = class {
4349
4679
  eventBus;
4350
4680
  mcpClient;
4351
4681
  feedbackSystem;
4682
+ skillLoader;
4352
4683
  maxContextTurns = 30;
4353
4684
  isInterrupted = false;
4354
4685
  constructor(sessionId2, eventBus2, llm, deploymentName, mcpClient, feedbackSystem) {
@@ -4358,6 +4689,7 @@ var BluMaAgent = class {
4358
4689
  this.deploymentName = deploymentName;
4359
4690
  this.mcpClient = mcpClient;
4360
4691
  this.feedbackSystem = feedbackSystem;
4692
+ this.skillLoader = new SkillLoader(process.cwd());
4361
4693
  this.eventBus.on("user_interrupt", () => {
4362
4694
  this.isInterrupted = true;
4363
4695
  this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
@@ -4369,6 +4701,10 @@ var BluMaAgent = class {
4369
4701
  try {
4370
4702
  if (this.sessionFile) {
4371
4703
  await saveSessionHistory(this.sessionFile, this.history);
4704
+ } else {
4705
+ const [sessionFile, _] = await loadOrcreateSession(this.sessionId);
4706
+ this.sessionFile = sessionFile;
4707
+ await saveSessionHistory(this.sessionFile, this.history);
4372
4708
  }
4373
4709
  } catch (e) {
4374
4710
  this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
@@ -4381,8 +4717,13 @@ var BluMaAgent = class {
4381
4717
  const [sessionFile, history] = await loadOrcreateSession(this.sessionId);
4382
4718
  this.sessionFile = sessionFile;
4383
4719
  this.history = history;
4720
+ initializeSkillContext({
4721
+ history: this.history,
4722
+ skillLoader: this.skillLoader
4723
+ });
4384
4724
  if (this.history.length === 0) {
4385
- const systemPrompt = getUnifiedSystemPrompt();
4725
+ const availableSkills = this.skillLoader.listAvailable();
4726
+ const systemPrompt = getUnifiedSystemPrompt(availableSkills);
4386
4727
  this.history.push({ role: "system", content: systemPrompt });
4387
4728
  await saveSessionHistory(this.sessionFile, this.history);
4388
4729
  }
@@ -4397,6 +4738,9 @@ var BluMaAgent = class {
4397
4738
  this.isInterrupted = false;
4398
4739
  const inputText = String(userInput.content || "").trim();
4399
4740
  this.history.push({ role: "user", content: inputText });
4741
+ if (inputText === "/init") {
4742
+ this.eventBus.emit("dispatch", inputText);
4743
+ }
4400
4744
  await this._continueConversation();
4401
4745
  }
4402
4746
  async handleToolResponse(decisionData) {
@@ -4495,9 +4839,15 @@ var BluMaAgent = class {
4495
4839
  });
4496
4840
  }
4497
4841
  this.eventBus.emit("backend_message", { type: "tool_result", tool_name: toolName, result: toolResultContent });
4498
- if (toolName.includes("agent_end_turn")) {
4499
- shouldContinueConversation = false;
4500
- this.eventBus.emit("backend_message", { type: "done", status: "completed" });
4842
+ if (toolName === "message") {
4843
+ try {
4844
+ const resultObj = typeof toolResultContent === "string" ? JSON.parse(toolResultContent) : toolResultContent;
4845
+ if (resultObj.message_type === "result") {
4846
+ shouldContinueConversation = false;
4847
+ this.eventBus.emit("backend_message", { type: "done", status: "completed" });
4848
+ }
4849
+ } catch {
4850
+ }
4501
4851
  }
4502
4852
  } else {
4503
4853
  toolResultContent = "The system rejected this action. Verify that the command you are executing contributes to the tasks intent and try again.";
@@ -4524,7 +4874,7 @@ var BluMaAgent = class {
4524
4874
 
4525
4875
  ${editData.error.display}`;
4526
4876
  }
4527
- const filename = path14.basename(toolArgs.file_path);
4877
+ const filename = path15.basename(toolArgs.file_path);
4528
4878
  return createDiff(filename, editData.currentContent || "", editData.newContent);
4529
4879
  } catch (e) {
4530
4880
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -4550,18 +4900,18 @@ ${editData.error.display}`;
4550
4900
  this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
4551
4901
  return;
4552
4902
  }
4553
- let message = response.choices[0].message;
4554
- message = ToolCallNormalizer.normalizeAssistantMessage(message);
4555
- if (message.reasoning_content || message.reasoning) {
4556
- const reasoningText = message.reasoning_content || message.reasoning;
4903
+ let message2 = response.choices[0].message;
4904
+ message2 = ToolCallNormalizer.normalizeAssistantMessage(message2);
4905
+ if (message2.reasoning_content || message2.reasoning) {
4906
+ const reasoningText = message2.reasoning_content || message2.reasoning;
4557
4907
  this.eventBus.emit("backend_message", {
4558
4908
  type: "reasoning",
4559
4909
  content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
4560
4910
  });
4561
4911
  }
4562
- this.history.push(message);
4563
- if (message.tool_calls && message.tool_calls.length > 0) {
4564
- const validToolCalls = message.tool_calls.filter(
4912
+ this.history.push(message2);
4913
+ if (message2.tool_calls && message2.tool_calls.length > 0) {
4914
+ const validToolCalls = message2.tool_calls.filter(
4565
4915
  (call) => ToolCallNormalizer.isValidToolCall(call)
4566
4916
  );
4567
4917
  if (validToolCalls.length === 0) {
@@ -4573,12 +4923,12 @@ ${editData.error.display}`;
4573
4923
  return;
4574
4924
  }
4575
4925
  const autoApprovedTools = [
4576
- "agent_end_turn",
4577
- "message_notify_user",
4926
+ "message",
4578
4927
  "ls_tool",
4579
4928
  "count_file_lines",
4580
4929
  "read_file_lines",
4581
- "todo"
4930
+ "todo",
4931
+ "load_skill"
4582
4932
  ];
4583
4933
  const toolToCall = validToolCalls[0];
4584
4934
  const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
@@ -4594,13 +4944,13 @@ ${editData.error.display}`;
4594
4944
  this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls });
4595
4945
  }
4596
4946
  }
4597
- } else if (message.content) {
4598
- this.eventBus.emit("backend_message", { type: "assistant_message", content: message.content });
4947
+ } else if (message2.content) {
4948
+ this.eventBus.emit("backend_message", { type: "assistant_message", content: message2.content });
4599
4949
  const feedback = this.feedbackSystem.generateFeedback({
4600
4950
  event: "protocol_violation_direct_text",
4601
- details: { violationContent: message.content }
4951
+ details: { violationContent: message2.content }
4602
4952
  });
4603
- this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: message.content });
4953
+ this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: message2.content });
4604
4954
  this.history.push({ role: "system", content: feedback.correction });
4605
4955
  await this._continueConversation();
4606
4956
  } else {
@@ -4617,14 +4967,20 @@ ${editData.error.display}`;
4617
4967
  };
4618
4968
 
4619
4969
  // src/app/agent/core/llm/llm.ts
4620
- var OpenAIAdapter = class {
4970
+ import OpenAI from "openai";
4971
+ var LLMService = class {
4621
4972
  client;
4622
- constructor(client) {
4623
- this.client = client;
4973
+ defaultModel;
4974
+ constructor(config2) {
4975
+ this.client = new OpenAI({
4976
+ apiKey: config2.apiKey,
4977
+ baseURL: config2.baseUrl || "http://109.51.171.144/mistrall/v1"
4978
+ });
4979
+ this.defaultModel = config2.model || "";
4624
4980
  }
4625
4981
  async chatCompletion(params) {
4626
4982
  const resp = await this.client.chat.completions.create({
4627
- model: params.model,
4983
+ model: params.model || this.defaultModel,
4628
4984
  messages: params.messages,
4629
4985
  tools: params.tools,
4630
4986
  tool_choice: params.tool_choice,
@@ -4634,6 +4990,12 @@ var OpenAIAdapter = class {
4634
4990
  });
4635
4991
  return resp;
4636
4992
  }
4993
+ /**
4994
+ * Retorna o modelo padrão configurado
4995
+ */
4996
+ getDefaultModel() {
4997
+ return this.defaultModel;
4998
+ }
4637
4999
  };
4638
5000
 
4639
5001
  // src/app/agent/subagents/registry.ts
@@ -4648,7 +5010,7 @@ function getSubAgentByCommand(cmd) {
4648
5010
  }
4649
5011
 
4650
5012
  // src/app/agent/subagents/init/init_system_prompt.ts
4651
- import os7 from "os";
5013
+ import os8 from "os";
4652
5014
  var SYSTEM_PROMPT2 = `
4653
5015
 
4654
5016
  ### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
@@ -4662,7 +5024,7 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
4662
5024
  You are BluMa InitSubAgent. Maintain professionalism and technical language.
4663
5025
 
4664
5026
  - Communication:
4665
- ALL messages must be sent via 'message_notify_user'.
5027
+ ALL messages must be sent via 'message'.
4666
5028
  No direct text replies to the user.
4667
5029
 
4668
5030
  - Task Completion:
@@ -4787,8 +5149,8 @@ Rule Summary:
4787
5149
  - Never invent file content. Read files via tools to confirm.
4788
5150
 
4789
5151
  ## OUTPUT
4790
- - Emit 'backend_message' events through tools only (message_notify_user) for progress updates.
4791
- - Before writing BluMa.md, propose structure via message_notify_user and proceed using edit_tool.
5152
+ - Emit 'backend_message' events through tools only (message) for progress updates.
5153
+ - Before writing BluMa.md, propose structure via message and proceed using edit_tool.
4792
5154
  - If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless user policy indicates auto-approval.
4793
5155
  - 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.
4794
5156
  - On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_turn.
@@ -4802,7 +5164,7 @@ Rule Summary:
4802
5164
  ## EXEMPLAR FLOW (GUIDELINE)
4803
5165
  1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
4804
5166
  2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
4805
- 3) Draft BluMa.md structure (message_notify_user)
5167
+ 3) Draft BluMa.md structure (message)
4806
5168
  4) Write BluMa.md via edit_tool
4807
5169
  5) Announce completion and agent_end_turn
4808
5170
 
@@ -4811,12 +5173,12 @@ Rule Summary:
4811
5173
  function getInitPrompt() {
4812
5174
  const now = /* @__PURE__ */ new Date();
4813
5175
  const collectedData = {
4814
- os_type: os7.type(),
4815
- os_version: os7.release(),
4816
- architecture: os7.arch(),
5176
+ os_type: os8.type(),
5177
+ os_version: os8.release(),
5178
+ architecture: os8.arch(),
4817
5179
  workdir: process.cwd(),
4818
5180
  shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
4819
- username: os7.userInfo().username || "Unknown",
5181
+ username: os8.userInfo().username || "Unknown",
4820
5182
  current_date: now.toISOString().split("T")[0],
4821
5183
  // Formato YYYY-MM-DD
4822
5184
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
@@ -4907,17 +5269,17 @@ ${editData.error.display}`;
4907
5269
  this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
4908
5270
  return;
4909
5271
  }
4910
- const message = response.choices[0].message;
4911
- this.history.push(message);
4912
- if (message.tool_calls) {
4913
- await this._handleToolExecution({ type: "user_decision_execute", tool_calls: message.tool_calls });
4914
- const lastToolName = message.tool_calls[0].function.name;
5272
+ const message2 = response.choices[0].message;
5273
+ this.history.push(message2);
5274
+ if (message2.tool_calls) {
5275
+ await this._handleToolExecution({ type: "user_decision_execute", tool_calls: message2.tool_calls });
5276
+ const lastToolName = message2.tool_calls[0].function.name;
4915
5277
  if (!lastToolName.includes("agent_end_turn") && !this.isInterrupted) {
4916
5278
  await this._continueConversation();
4917
5279
  }
4918
- } else if (message.content) {
4919
- this.emitEvent("assistant_message", { content: message.content });
4920
- this.emitEvent("protocol_violation", { message: "Direct text emission detected from subagent.", content: message.content });
5280
+ } else if (message2.content) {
5281
+ this.emitEvent("assistant_message", { content: message2.content });
5282
+ this.emitEvent("protocol_violation", { message: "Direct text emission detected from subagent.", content: message2.content });
4921
5283
  } else {
4922
5284
  this.emitEvent("info", { message: "SubAgent is thinking... continuing reasoning cycle." });
4923
5285
  }
@@ -5068,21 +5430,43 @@ var SubAgentsBluMa = class {
5068
5430
  }
5069
5431
  };
5070
5432
 
5433
+ // src/app/agent/routeManager.ts
5434
+ var RouteManager = class {
5435
+ routeHandlers;
5436
+ subAgents;
5437
+ core;
5438
+ constructor(subAgents, core) {
5439
+ this.routeHandlers = /* @__PURE__ */ new Map();
5440
+ this.subAgents = subAgents;
5441
+ this.core = core;
5442
+ }
5443
+ registerRoute(path18, handler) {
5444
+ this.routeHandlers.set(path18, handler);
5445
+ }
5446
+ async handleRoute(payload) {
5447
+ const inputText = String(payload.content || "").trim();
5448
+ for (const [path18, handler] of this.routeHandlers) {
5449
+ if (inputText === path18 || inputText.startsWith(`${path18} `)) {
5450
+ return handler({ content: inputText });
5451
+ }
5452
+ }
5453
+ await this.core.processTurn({ content: inputText });
5454
+ }
5455
+ };
5456
+
5071
5457
  // src/app/agent/agent.ts
5072
- var globalEnvPath = path15.join(os8.homedir(), ".bluma-cli", ".env");
5458
+ var globalEnvPath = path16.join(os9.homedir(), ".bluma", ".env");
5073
5459
  dotenv.config({ path: globalEnvPath });
5074
5460
  var Agent = class {
5075
5461
  sessionId;
5076
5462
  eventBus;
5077
5463
  mcpClient;
5078
5464
  feedbackSystem;
5079
- // Mantido caso UI dependa de eventos
5080
5465
  llm;
5081
- deploymentName;
5466
+ routeManager;
5467
+ model;
5082
5468
  core;
5083
- // Delegado
5084
5469
  subAgents;
5085
- // Orquestrador de subagentes
5086
5470
  toolInvoker;
5087
5471
  constructor(sessionId2, eventBus2) {
5088
5472
  this.sessionId = sessionId2;
@@ -5091,75 +5475,53 @@ var Agent = class {
5091
5475
  this.toolInvoker = nativeToolInvoker;
5092
5476
  this.mcpClient = new MCPClient(nativeToolInvoker, eventBus2);
5093
5477
  this.feedbackSystem = new AdvancedFeedbackSystem();
5094
- const endpoint = process.env.AZURE_OPENAI_ENDPOINT;
5095
- const apiKey = process.env.OPENROUTER_API_KEY;
5096
- const apiVersion = process.env.AZURE_OPENAI_API_VERSION;
5097
- this.deploymentName = process.env.AZURE_OPENAI_DEPLOYMENT || "";
5098
- const missing = [];
5099
- if (!apiKey) missing.push("OPENROUTER_API_KEY");
5100
- if (missing.length > 0) {
5478
+ const apiKey = process.env.NOMAD_API_KEY;
5479
+ const baseUrl = process.env.NOMAD_BASE_URL;
5480
+ this.model = process.env.MODEL_NOMAD || "";
5481
+ if (!apiKey) {
5101
5482
  const platform = process.platform;
5102
- const varList = missing.join(", ");
5103
5483
  let guidance = "";
5104
5484
  if (platform === "win32") {
5105
5485
  guidance = [
5106
5486
  "Windows (PowerShell):",
5107
- ` $env:${missing[0]}="<value>" # sess\xE3o atual`,
5108
- ...missing.slice(1).map((v) => ` $env:${v}="<value>"`),
5109
- " # Persistir para o utilizador:",
5110
- ...missing.map((v) => ` [System.Environment]::SetEnvironmentVariable("${v}", "<value>", "User")`),
5111
- "",
5112
- "Windows (cmd.exe):",
5113
- ...missing.map((v) => ` setx ${v} "<value>"`)
5114
- ].join("");
5487
+ ' $env:NOMAD_API_KEY="<your-key>"',
5488
+ " # Para persistir:",
5489
+ ' [System.Environment]::SetEnvironmentVariable("NOMAD_API_KEY", "<your-key>", "User")'
5490
+ ].join("\n");
5115
5491
  } else if (platform === "darwin" || platform === "linux") {
5116
5492
  guidance = [
5117
5493
  "macOS/Linux (bash/zsh):",
5118
- ...missing.map((v) => ` echo 'export ${v}="<value>"' >> ~/.bashrc # ou ~/.zshrc`),
5119
- " source ~/.bashrc # ou: source ~/.zshrc"
5120
- ].join("");
5494
+ ` echo 'export NOMAD_API_KEY="<your-key>"' >> ~/.bashrc`,
5495
+ " source ~/.bashrc"
5496
+ ].join("\n");
5121
5497
  } else {
5122
- guidance = [
5123
- "Generic POSIX:",
5124
- ...missing.map((v) => ` export ${v}="<value>"`)
5125
- ].join("");
5498
+ guidance = ' export NOMAD_API_KEY="<your-key>"';
5126
5499
  }
5127
- const message = [
5128
- `Missing required environment variables: ${varList}.`,
5129
- `Configure them globally using the commands below (based on your OS), or set them in the config file: ${globalEnvPath}.`,
5500
+ const message2 = [
5501
+ "Missing required environment variable: NOMAD_API_KEY.",
5502
+ `Configure it globally using the commands below, or set it in: ${globalEnvPath}`,
5130
5503
  "",
5131
5504
  guidance
5132
- ].join("");
5505
+ ].join("\n");
5133
5506
  this.eventBus.emit("backend_message", {
5134
5507
  type: "error",
5135
5508
  code: "missing_env",
5136
- missing,
5509
+ missing: ["NOMAD_API_KEY", "MODEL_NOMAD"],
5137
5510
  path: globalEnvPath,
5138
- message
5511
+ message: message2
5139
5512
  });
5140
- throw new Error(message);
5141
- }
5142
- const ollama = new OpenAI({
5143
- //baseURL: "http://127.0.0.1:11434/v1",
5144
- //baseURL: "http://localhost:8000/v1",
5145
- //baseURL: "https://api.groq.com/openai/v1",
5146
- //baseURL: "https://api.cerebras.ai/v1",
5147
- baseURL: "https://openrouter.ai/api/v1",
5148
- apiKey: apiKey || "",
5149
- // Buscar do environment do sistema
5150
- defaultHeaders: {
5151
- "HTTP-Referer": "<YOUR_SITE_URL>",
5152
- // Optional. Site URL for rankings on openrouter.ai.
5153
- "X-Title": "<YOUR_SITE_NAME>"
5154
- // Optional. Site title for rankings on openrouter.ai.
5155
- }
5513
+ throw new Error(message2);
5514
+ }
5515
+ this.llm = new LLMService({
5516
+ apiKey,
5517
+ baseUrl,
5518
+ model: this.model
5156
5519
  });
5157
- this.llm = new OpenAIAdapter(ollama);
5158
5520
  this.core = new BluMaAgent(
5159
5521
  this.sessionId,
5160
5522
  this.eventBus,
5161
5523
  this.llm,
5162
- this.deploymentName,
5524
+ this.model,
5163
5525
  this.mcpClient,
5164
5526
  this.feedbackSystem
5165
5527
  );
@@ -5168,9 +5530,10 @@ var Agent = class {
5168
5530
  mcpClient: this.mcpClient,
5169
5531
  toolInvoker: this.toolInvoker,
5170
5532
  llm: this.llm,
5171
- deploymentName: this.deploymentName,
5533
+ deploymentName: this.model,
5172
5534
  projectRoot: process.cwd()
5173
5535
  });
5536
+ this.routeManager = new RouteManager(this.subAgents, this.core);
5174
5537
  }
5175
5538
  async initialize() {
5176
5539
  await this.core.initialize();
@@ -5184,10 +5547,9 @@ var Agent = class {
5184
5547
  async processTurn(userInput) {
5185
5548
  const inputText = String(userInput.content || "").trim();
5186
5549
  if (inputText === "/init" || inputText.startsWith("/init ")) {
5187
- await this.dispatchToSubAgent({ command: "/init", content: inputText });
5188
- return;
5550
+ this.routeManager.registerRoute("/init", this.dispatchToSubAgent);
5189
5551
  }
5190
- await this.core.processTurn({ content: inputText });
5552
+ await this.routeManager.handleRoute({ content: inputText });
5191
5553
  }
5192
5554
  async handleToolResponse(decisionData) {
5193
5555
  await this.core.handleToolResponse(decisionData);
@@ -5286,12 +5648,12 @@ var renderShellCommand2 = ({ args }) => {
5286
5648
  };
5287
5649
  var renderLsTool2 = ({ args }) => {
5288
5650
  const parsed = parseArgs(args);
5289
- const path17 = parsed.directory_path || ".";
5651
+ const path18 = parsed.directory_path || ".";
5290
5652
  return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
5291
5653
  /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "ls" }),
5292
5654
  /* @__PURE__ */ jsxs8(Text8, { children: [
5293
5655
  " ",
5294
- path17
5656
+ path18
5295
5657
  ] })
5296
5658
  ] });
5297
5659
  };
@@ -5422,7 +5784,7 @@ var renderFindByName = ({ args }) => {
5422
5784
  var renderGrepSearch = ({ args }) => {
5423
5785
  const parsed = parseArgs(args);
5424
5786
  const query = parsed.query || "";
5425
- const path17 = parsed.path || ".";
5787
+ const path18 = parsed.path || ".";
5426
5788
  return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
5427
5789
  /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "grep" }),
5428
5790
  /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
@@ -5432,7 +5794,7 @@ var renderGrepSearch = ({ args }) => {
5432
5794
  ] }),
5433
5795
  /* @__PURE__ */ jsxs8(Text8, { children: [
5434
5796
  " ",
5435
- path17
5797
+ path18
5436
5798
  ] })
5437
5799
  ] });
5438
5800
  };
@@ -5500,6 +5862,17 @@ var renderSearchWeb = ({ args }) => {
5500
5862
  ] })
5501
5863
  ] });
5502
5864
  };
5865
+ var renderLoadSkill = ({ args }) => {
5866
+ const parsed = parseArgs(args);
5867
+ const skillName = parsed.skill_name || "[no skill]";
5868
+ return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
5869
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "load skill" }),
5870
+ /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
5871
+ " ",
5872
+ skillName
5873
+ ] })
5874
+ ] });
5875
+ };
5503
5876
  var renderGeneric2 = ({ toolName, args }) => {
5504
5877
  const parsed = parseArgs(args);
5505
5878
  const keys = Object.keys(parsed).slice(0, 2);
@@ -5524,13 +5897,14 @@ var ToolRenderDisplay = {
5524
5897
  view_file_outline: renderViewFileOutline,
5525
5898
  command_status: renderCommandStatus,
5526
5899
  task_boundary: renderTaskBoundary,
5527
- search_web: renderSearchWeb
5900
+ search_web: renderSearchWeb,
5901
+ load_skill: renderLoadSkill
5528
5902
  };
5529
5903
 
5530
5904
  // src/app/ui/components/ToolCallDisplay.tsx
5531
5905
  import { jsx as jsx9 } from "react/jsx-runtime";
5532
5906
  var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
5533
- if (toolName.includes("message_notify_user") || toolName.includes("agent_end_turn")) {
5907
+ if (toolName.includes("message")) {
5534
5908
  return null;
5535
5909
  }
5536
5910
  const Renderer = ToolRenderDisplay[toolName] || ((props2) => renderGeneric2({ ...props2, toolName }));
@@ -5731,11 +6105,11 @@ var truncateLines = (text, max) => {
5731
6105
  };
5732
6106
  };
5733
6107
  var ToolResultDisplayComponent = ({ toolName, result }) => {
5734
- if (toolName.includes("agent_end_turn") || toolName.includes("task_boundary")) {
6108
+ if (toolName.includes("task_boundary")) {
5735
6109
  return null;
5736
6110
  }
5737
6111
  const parsed = parseResult(result);
5738
- if (toolName.includes("message_notify_user")) {
6112
+ if (toolName.includes("message")) {
5739
6113
  const body = parsed?.content?.body || parsed?.body || parsed?.message;
5740
6114
  if (!body) return null;
5741
6115
  return /* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(MarkdownRenderer, { markdown: body }) });
@@ -5818,6 +6192,23 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
5818
6192
  ] })
5819
6193
  ] });
5820
6194
  }
6195
+ if (toolName.includes("load_skill") && parsed) {
6196
+ if (!parsed.success) {
6197
+ return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "red", children: [
6198
+ "\u2717 Skill not found: ",
6199
+ parsed.message
6200
+ ] }) });
6201
+ }
6202
+ return /* @__PURE__ */ jsxs10(Box11, { paddingLeft: 3, marginBottom: 2, children: [
6203
+ /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "green", children: "Skill loaded: " }),
6204
+ /* @__PURE__ */ jsx11(Text10, { color: "green", bold: true, children: parsed.skill_name }),
6205
+ /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
6206
+ " \u2014 ",
6207
+ parsed.description?.slice(0, 50),
6208
+ parsed.description?.length > 100 ? "..." : ""
6209
+ ] })
6210
+ ] });
6211
+ }
5821
6212
  if (toolName.includes("todo")) {
5822
6213
  return null;
5823
6214
  }
@@ -6019,16 +6410,16 @@ var SlashCommands_default = SlashCommands;
6019
6410
  // src/app/agent/utils/update_check.ts
6020
6411
  import updateNotifier from "update-notifier";
6021
6412
  import { fileURLToPath as fileURLToPath3 } from "url";
6022
- import path16 from "path";
6023
- import fs12 from "fs";
6413
+ import path17 from "path";
6414
+ import fs13 from "fs";
6024
6415
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
6025
6416
  function findBlumaPackageJson(startDir) {
6026
6417
  let dir = startDir;
6027
6418
  for (let i = 0; i < 10; i++) {
6028
- const candidate = path16.join(dir, "package.json");
6029
- if (fs12.existsSync(candidate)) {
6419
+ const candidate = path17.join(dir, "package.json");
6420
+ if (fs13.existsSync(candidate)) {
6030
6421
  try {
6031
- const raw = fs12.readFileSync(candidate, "utf8");
6422
+ const raw = fs13.readFileSync(candidate, "utf8");
6032
6423
  const parsed = JSON.parse(raw);
6033
6424
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
6034
6425
  return { name: parsed.name, version: parsed.version };
@@ -6036,7 +6427,7 @@ function findBlumaPackageJson(startDir) {
6036
6427
  } catch {
6037
6428
  }
6038
6429
  }
6039
- const parent = path16.dirname(dir);
6430
+ const parent = path17.dirname(dir);
6040
6431
  if (parent === dir) break;
6041
6432
  dir = parent;
6042
6433
  }
@@ -6049,12 +6440,12 @@ async function checkForUpdates() {
6049
6440
  }
6050
6441
  const binPath = process.argv?.[1];
6051
6442
  let pkg = null;
6052
- if (binPath && fs12.existsSync(binPath)) {
6053
- pkg = findBlumaPackageJson(path16.dirname(binPath));
6443
+ if (binPath && fs13.existsSync(binPath)) {
6444
+ pkg = findBlumaPackageJson(path17.dirname(binPath));
6054
6445
  }
6055
6446
  if (!pkg) {
6056
6447
  const __filename = fileURLToPath3(import.meta.url);
6057
- const __dirname = path16.dirname(__filename);
6448
+ const __dirname = path17.dirname(__filename);
6058
6449
  pkg = findBlumaPackageJson(__dirname);
6059
6450
  }
6060
6451
  if (!pkg) {
@@ -6100,11 +6491,11 @@ function parseUpdateMessage(msg) {
6100
6491
  hint: hintLine || void 0
6101
6492
  };
6102
6493
  }
6103
- var UpdateNotice = ({ message }) => {
6104
- const { name, current, latest, hint } = parseUpdateMessage(message);
6494
+ var UpdateNotice = ({ message: message2 }) => {
6495
+ const { name, current, latest, hint } = parseUpdateMessage(message2);
6105
6496
  return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", marginBottom: 1, children: [
6106
6497
  /* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: "Update Available" }),
6107
- name && current && latest ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: `${name}: ${current} \u2192 ${latest}` }) : /* @__PURE__ */ jsx13(Text12, { color: "gray", children: message }),
6498
+ name && current && latest ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: `${name}: ${current} \u2192 ${latest}` }) : /* @__PURE__ */ jsx13(Text12, { color: "gray", children: message2 }),
6108
6499
  hint ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: hint }) : null
6109
6500
  ] });
6110
6501
  };
@@ -6113,13 +6504,13 @@ var UpdateNotice_default = UpdateNotice;
6113
6504
  // src/app/ui/components/ErrorMessage.tsx
6114
6505
  import { Box as Box14, Text as Text13 } from "ink";
6115
6506
  import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
6116
- var ErrorMessage = ({ message, details, hint }) => {
6507
+ var ErrorMessage = ({ message: message2, details, hint }) => {
6117
6508
  return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
6118
6509
  /* @__PURE__ */ jsxs13(Box14, { children: [
6119
6510
  /* @__PURE__ */ jsx14(Text13, { color: "red", children: "\u2717" }),
6120
6511
  /* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: " Error" })
6121
6512
  ] }),
6122
- /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message }) }),
6513
+ /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message2 }) }),
6123
6514
  details && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: details }) }),
6124
6515
  hint && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
6125
6516
  "hint: ",
@@ -6155,7 +6546,7 @@ var ReasoningDisplay = memo9(ReasoningDisplayComponent);
6155
6546
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6156
6547
  var SAFE_AUTO_APPROVE_TOOLS = [
6157
6548
  // Comunicação/UI
6158
- "message_notify_user",
6549
+ "message",
6159
6550
  "agent_end_turn",
6160
6551
  "todo",
6161
6552
  "task_boundary",