@nomad-e/bluma-cli 0.0.109 → 0.0.110

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
@@ -2152,7 +1998,7 @@ import * as path5 from "path";
2152
1998
  var taskStore = [];
2153
1999
  var nextId = 1;
2154
2000
  function getTodoFilePath() {
2155
- return path5.join(process.cwd(), ".bluma", "todo.json");
2001
+ return path5.join(process.cwd(), ".bluma-cli", "todo.json");
2156
2002
  }
2157
2003
  function loadTasksFromFile() {
2158
2004
  try {
@@ -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
  };
@@ -3087,7 +2933,7 @@ var artifactsDir = null;
3087
2933
  async function getArtifactsDir() {
3088
2934
  if (artifactsDir) return artifactsDir;
3089
2935
  const homeDir = os3.homedir();
3090
- const baseDir = path9.join(homeDir, ".bluma", "artifacts");
2936
+ const baseDir = path9.join(homeDir, ".bluma-cli", "artifacts");
3091
2937
  const sessionId2 = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
3092
2938
  artifactsDir = path9.join(baseDir, sessionId2);
3093
2939
  await fs7.mkdir(artifactsDir, { recursive: true });
@@ -3409,6 +3255,44 @@ 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. Follow these instructions:
3289
+
3290
+ ${skill.content}`,
3291
+ skill_name: skill.name,
3292
+ description: skill.description
3293
+ };
3294
+ }
3295
+
3412
3296
  // src/app/agent/tool_invoker.ts
3413
3297
  var ToolInvoker = class {
3414
3298
  // Mapa privado para associar nomes de ferramentas às suas funções de implementação.
@@ -3452,13 +3336,13 @@ var ToolInvoker = class {
3452
3336
  this.toolImplementations.set("command_status", commandStatus);
3453
3337
  this.toolImplementations.set("send_command_input", sendCommandInput);
3454
3338
  this.toolImplementations.set("kill_command", killCommand);
3455
- this.toolImplementations.set("message_notify_user", messageNotifyuser);
3339
+ this.toolImplementations.set("message", message);
3456
3340
  this.toolImplementations.set("todo", todo);
3457
3341
  this.toolImplementations.set("task_boundary", taskBoundary);
3458
3342
  this.toolImplementations.set("create_artifact", createArtifact);
3459
3343
  this.toolImplementations.set("read_artifact", readArtifact);
3460
3344
  this.toolImplementations.set("search_web", searchWeb);
3461
- this.toolImplementations.set("agent_end_turn", async () => ({ success: true, message: "Task ended by agent." }));
3345
+ this.toolImplementations.set("load_skill", loadSkill);
3462
3346
  }
3463
3347
  /**
3464
3348
  * Retorna a lista de definições de todas as ferramentas nativas carregadas.
@@ -3688,7 +3572,7 @@ var AdvancedFeedbackSystem = class {
3688
3572
  };
3689
3573
 
3690
3574
  // src/app/agent/bluma/core/bluma.ts
3691
- import path14 from "path";
3575
+ import path15 from "path";
3692
3576
 
3693
3577
  // src/app/agent/session_manager/session_manager.ts
3694
3578
  import path12 from "path";
@@ -3854,10 +3738,180 @@ async function saveSessionHistory(sessionFile, history) {
3854
3738
  }
3855
3739
 
3856
3740
  // src/app/agent/core/prompt/prompt_builder.ts
3857
- import os6 from "os";
3741
+ import os7 from "os";
3742
+ import fs12 from "fs";
3743
+ import path14 from "path";
3744
+ import { execSync } from "child_process";
3745
+
3746
+ // src/app/agent/skills/skill_loader.ts
3858
3747
  import fs11 from "fs";
3859
3748
  import path13 from "path";
3860
- import { execSync } from "child_process";
3749
+ import os6 from "os";
3750
+ var SkillLoader = class {
3751
+ projectSkillsDir;
3752
+ globalSkillsDir;
3753
+ cache = /* @__PURE__ */ new Map();
3754
+ constructor(projectRoot) {
3755
+ this.projectSkillsDir = path13.join(projectRoot, ".bluma", "skills");
3756
+ this.globalSkillsDir = path13.join(os6.homedir(), ".bluma", "skills");
3757
+ }
3758
+ /**
3759
+ * Lista skills disponíveis de ambas as fontes
3760
+ * Skills de projeto têm prioridade sobre globais com mesmo nome
3761
+ */
3762
+ listAvailable() {
3763
+ const skills = /* @__PURE__ */ new Map();
3764
+ const globalSkills = this.listFromDir(this.globalSkillsDir, "global");
3765
+ for (const skill of globalSkills) {
3766
+ skills.set(skill.name, skill);
3767
+ }
3768
+ const projectSkills = this.listFromDir(this.projectSkillsDir, "project");
3769
+ for (const skill of projectSkills) {
3770
+ skills.set(skill.name, skill);
3771
+ }
3772
+ return Array.from(skills.values());
3773
+ }
3774
+ /**
3775
+ * Lista skills de um diretório específico
3776
+ */
3777
+ listFromDir(dir, source) {
3778
+ if (!fs11.existsSync(dir)) return [];
3779
+ try {
3780
+ return fs11.readdirSync(dir).filter((d) => {
3781
+ const fullPath = path13.join(dir, d);
3782
+ return fs11.statSync(fullPath).isDirectory() && fs11.existsSync(path13.join(fullPath, "SKILL.md"));
3783
+ }).map((d) => this.loadMetadataFromPath(path13.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
3784
+ } catch {
3785
+ return [];
3786
+ }
3787
+ }
3788
+ /**
3789
+ * Carrega metadata de um path específico
3790
+ */
3791
+ loadMetadataFromPath(skillPath, skillName, source) {
3792
+ if (!fs11.existsSync(skillPath)) return null;
3793
+ try {
3794
+ const raw = fs11.readFileSync(skillPath, "utf-8");
3795
+ const parsed = this.parseFrontmatter(raw);
3796
+ return {
3797
+ name: parsed.name || skillName,
3798
+ description: parsed.description || "",
3799
+ source,
3800
+ version: parsed.version,
3801
+ author: parsed.author,
3802
+ license: parsed.license
3803
+ };
3804
+ } catch {
3805
+ return null;
3806
+ }
3807
+ }
3808
+ /**
3809
+ * Carrega skill completa - procura primeiro em projeto, depois global
3810
+ */
3811
+ load(name) {
3812
+ if (this.cache.has(name)) return this.cache.get(name);
3813
+ const projectPath = path13.join(this.projectSkillsDir, name, "SKILL.md");
3814
+ if (fs11.existsSync(projectPath)) {
3815
+ const skill = this.loadFromPath(projectPath, name, "project");
3816
+ if (skill) {
3817
+ this.cache.set(name, skill);
3818
+ return skill;
3819
+ }
3820
+ }
3821
+ const globalPath = path13.join(this.globalSkillsDir, name, "SKILL.md");
3822
+ if (fs11.existsSync(globalPath)) {
3823
+ const skill = this.loadFromPath(globalPath, name, "global");
3824
+ if (skill) {
3825
+ this.cache.set(name, skill);
3826
+ return skill;
3827
+ }
3828
+ }
3829
+ return null;
3830
+ }
3831
+ /**
3832
+ * Carrega skill de um path específico
3833
+ */
3834
+ loadFromPath(skillPath, name, source) {
3835
+ try {
3836
+ const raw = fs11.readFileSync(skillPath, "utf-8");
3837
+ const parsed = this.parseFrontmatter(raw);
3838
+ return {
3839
+ name: parsed.name || name,
3840
+ description: parsed.description || "",
3841
+ content: parsed.content.trim(),
3842
+ source,
3843
+ version: parsed.version,
3844
+ author: parsed.author,
3845
+ license: parsed.license
3846
+ };
3847
+ } catch {
3848
+ return null;
3849
+ }
3850
+ }
3851
+ /**
3852
+ * Parse simples de YAML frontmatter
3853
+ */
3854
+ parseFrontmatter(raw) {
3855
+ const lines = raw.split("\n");
3856
+ if (lines[0]?.trim() !== "---") {
3857
+ return { content: raw };
3858
+ }
3859
+ let endIndex = -1;
3860
+ for (let i = 1; i < lines.length; i++) {
3861
+ if (lines[i]?.trim() === "---") {
3862
+ endIndex = i;
3863
+ break;
3864
+ }
3865
+ }
3866
+ if (endIndex === -1) {
3867
+ return { content: raw };
3868
+ }
3869
+ const frontmatter = {};
3870
+ for (let i = 1; i < endIndex; i++) {
3871
+ const line = lines[i];
3872
+ const colonIndex = line.indexOf(":");
3873
+ if (colonIndex > 0) {
3874
+ const key = line.slice(0, colonIndex).trim();
3875
+ const value = line.slice(colonIndex + 1).trim();
3876
+ frontmatter[key] = value;
3877
+ }
3878
+ }
3879
+ const content = lines.slice(endIndex + 1).join("\n");
3880
+ return {
3881
+ name: frontmatter["name"],
3882
+ description: frontmatter["description"],
3883
+ version: frontmatter["version"],
3884
+ author: frontmatter["author"],
3885
+ license: frontmatter["license"],
3886
+ content
3887
+ };
3888
+ }
3889
+ /**
3890
+ * Limpa o cache
3891
+ */
3892
+ clearCache() {
3893
+ this.cache.clear();
3894
+ }
3895
+ /**
3896
+ * Verifica se uma skill existe (em projeto ou global)
3897
+ */
3898
+ exists(name) {
3899
+ const projectPath = path13.join(this.projectSkillsDir, name, "SKILL.md");
3900
+ const globalPath = path13.join(this.globalSkillsDir, name, "SKILL.md");
3901
+ return fs11.existsSync(projectPath) || fs11.existsSync(globalPath);
3902
+ }
3903
+ /**
3904
+ * Retorna os diretórios de skills
3905
+ */
3906
+ getSkillsDirs() {
3907
+ return {
3908
+ project: this.projectSkillsDir,
3909
+ global: this.globalSkillsDir
3910
+ };
3911
+ }
3912
+ };
3913
+
3914
+ // src/app/agent/core/prompt/prompt_builder.ts
3861
3915
  function getNodeVersion() {
3862
3916
  try {
3863
3917
  return process.version;
@@ -3885,10 +3939,10 @@ function getGitBranch(dir) {
3885
3939
  }
3886
3940
  function getPackageManager(dir) {
3887
3941
  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";
3942
+ if (fs12.existsSync(path14.join(dir, "pnpm-lock.yaml"))) return "pnpm";
3943
+ if (fs12.existsSync(path14.join(dir, "yarn.lock"))) return "yarn";
3944
+ if (fs12.existsSync(path14.join(dir, "bun.lockb"))) return "bun";
3945
+ if (fs12.existsSync(path14.join(dir, "package-lock.json"))) return "npm";
3892
3946
  return "unknown";
3893
3947
  } catch {
3894
3948
  return "unknown";
@@ -3896,9 +3950,9 @@ function getPackageManager(dir) {
3896
3950
  }
3897
3951
  function getProjectType(dir) {
3898
3952
  try {
3899
- const files = fs11.readdirSync(dir);
3953
+ const files = fs12.readdirSync(dir);
3900
3954
  if (files.includes("package.json")) {
3901
- const pkg = JSON.parse(fs11.readFileSync(path13.join(dir, "package.json"), "utf-8"));
3955
+ const pkg = JSON.parse(fs12.readFileSync(path14.join(dir, "package.json"), "utf-8"));
3902
3956
  if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
3903
3957
  if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
3904
3958
  if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
@@ -3917,9 +3971,9 @@ function getProjectType(dir) {
3917
3971
  }
3918
3972
  function getTestFramework(dir) {
3919
3973
  try {
3920
- const pkgPath = path13.join(dir, "package.json");
3921
- if (fs11.existsSync(pkgPath)) {
3922
- const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3974
+ const pkgPath = path14.join(dir, "package.json");
3975
+ if (fs12.existsSync(pkgPath)) {
3976
+ const pkg = JSON.parse(fs12.readFileSync(pkgPath, "utf-8"));
3923
3977
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3924
3978
  if (deps.jest) return "jest";
3925
3979
  if (deps.vitest) return "vitest";
@@ -3928,7 +3982,7 @@ function getTestFramework(dir) {
3928
3982
  if (deps["@playwright/test"]) return "playwright";
3929
3983
  if (deps.cypress) return "cypress";
3930
3984
  }
3931
- if (fs11.existsSync(path13.join(dir, "pytest.ini")) || fs11.existsSync(path13.join(dir, "conftest.py"))) return "pytest";
3985
+ if (fs12.existsSync(path14.join(dir, "pytest.ini")) || fs12.existsSync(path14.join(dir, "conftest.py"))) return "pytest";
3932
3986
  return "unknown";
3933
3987
  } catch {
3934
3988
  return "unknown";
@@ -3936,9 +3990,9 @@ function getTestFramework(dir) {
3936
3990
  }
3937
3991
  function getTestCommand(dir) {
3938
3992
  try {
3939
- const pkgPath = path13.join(dir, "package.json");
3940
- if (fs11.existsSync(pkgPath)) {
3941
- const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3993
+ const pkgPath = path14.join(dir, "package.json");
3994
+ if (fs12.existsSync(pkgPath)) {
3995
+ const pkg = JSON.parse(fs12.readFileSync(pkgPath, "utf-8"));
3942
3996
  if (pkg.scripts?.test) return `npm test`;
3943
3997
  if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
3944
3998
  }
@@ -4006,7 +4060,7 @@ You MUST adapt all commands to this environment. Use the correct package manager
4006
4060
  ## How You Work
4007
4061
 
4008
4062
  ### For Multi-Step Tasks:
4009
- 1. **Acknowledge** - Confirm the request immediately via message_notify_user
4063
+ 1. **Acknowledge** - Confirm the request immediately via message
4010
4064
  2. **Plan** - Use the TODO tool to create a structured plan
4011
4065
  3. **Execute** - Implement with clear narration of each step
4012
4066
  4. **Test** - Run tests after any code changes (MANDATORY for production code)
@@ -4017,25 +4071,11 @@ You MUST adapt all commands to this environment. Use the correct package manager
4017
4071
  - Quick acknowledgment + immediate action
4018
4072
  - No TODO needed for single operations
4019
4073
 
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
4028
-
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
4074
+ ### Testing Philosophy:
4075
+ Run tests when modifying code. Use the \`testing\` skill for detailed best practices.
4034
4076
 
4035
- **Commands you should know:**
4077
+ **Commands:**
4036
4078
  - Run tests: {test_command}
4037
- - Run single test: Check project conventions
4038
- - Watch mode: Usually \`npm run test:watch\` or \`npm test -- --watch\`
4039
4079
  </workflow>
4040
4080
 
4041
4081
  ---
@@ -4050,8 +4090,8 @@ You are a **teammate who writes tests**, not an assistant who skips them.
4050
4090
  - **Check file exists** before attempting edits
4051
4091
 
4052
4092
  ### Safe Auto-Approved Tools (no confirmation needed):
4053
- - message_notify_user
4054
- - ls_tool
4093
+ - message
4094
+ - ls_tool
4055
4095
  - read_file_lines
4056
4096
  - count_file_lines
4057
4097
  - todo
@@ -4102,7 +4142,7 @@ You MUST use \`command_status\` to get the output.
4102
4142
  [Step 2] command_status({ command_id: "cmd_001", wait_seconds: 120 })
4103
4143
  \u2192 { status: "completed", exit_code: 0, stdout: "added 150 packages..." }
4104
4144
 
4105
- [Step 3] message_notify_user("Dependencies installed successfully.")
4145
+ [Step 3] message({ content: "Dependencies installed.", message_type: "info" })
4106
4146
  \`\`\`
4107
4147
 
4108
4148
  **[OK] Run tests:**
@@ -4131,7 +4171,7 @@ You MUST use \`command_status\` to get the output.
4131
4171
  [Step 2] command_status({ command_id: "cmd_004", wait_seconds: 10 })
4132
4172
  \u2192 { status: "running" }
4133
4173
 
4134
- [Step 3] message_notify_user("Building Docker image, please wait...")
4174
+ [Step 3] message({ content: "Building Docker image...", message_type: "info" })
4135
4175
 
4136
4176
  [Step 4] command_status({ command_id: "cmd_004", wait_seconds: 10 })
4137
4177
  \u2192 { status: "completed", exit_code: 0, stdout: "Successfully built abc123" }
@@ -4140,7 +4180,7 @@ You MUST use \`command_status\` to get the output.
4140
4180
  **[WRONG] Never forget command_status:**
4141
4181
  \`\`\`
4142
4182
  shell_command({ command: "npm install" })
4143
- message_notify_user("Installed!") // WRONG: You don't know if it succeeded!
4183
+ message({ content: "Installed!", message_type: "info" }) // WRONG: You don't know if it succeeded!
4144
4184
  \`\`\`
4145
4185
 
4146
4186
  **[WRONG] Never use long waits (blocks user):**
@@ -4185,39 +4225,29 @@ Examples:
4185
4225
  - Celebrate wins, acknowledge mistakes
4186
4226
 
4187
4227
  **Message via tool only:**
4188
- - Use message_notify_user for ALL communication
4189
- - Never assume the user sees internal state
4190
- - Report progress proactively
4228
+ - message with \`result\` = END your turn (after questions/completions)
4229
+ - message with \`info\` = progress update, you continue working
4191
4230
 
4192
- **Example flow:**
4231
+ **CRITICAL: After asking questions, ALWAYS use result!**
4232
+
4233
+ **Asking question (result):**
4193
4234
  \`\`\`
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.")
4235
+ message({ content: "Project or global?", message_type: "result" })
4236
+ // STOP and wait for user
4201
4237
  \`\`\`
4202
- </communication_style>
4203
-
4204
- ---
4205
-
4206
- <git_guidelines>
4207
- ## Git Best Practices
4208
4238
 
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
4239
+ **Progress then continue (info):**
4240
+ \`\`\`
4241
+ message({ content: "Installing...", message_type: "info" })
4242
+ // continue with next tool
4243
+ \`\`\`
4214
4244
 
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>
4245
+ **Task done (result):**
4246
+ \`\`\`
4247
+ message({ content: "Done. All tests pass.", message_type: "result" })
4248
+ // STOP - finished
4249
+ \`\`\`
4250
+ </communication_style>
4221
4251
 
4222
4252
  ---
4223
4253
 
@@ -4254,15 +4284,15 @@ You communicate because you respect your teammate.
4254
4284
  Let's build something great, {username}.
4255
4285
  </personality>
4256
4286
  `;
4257
- function getUnifiedSystemPrompt() {
4287
+ function getUnifiedSystemPrompt(availableSkills) {
4258
4288
  const cwd = process.cwd();
4259
4289
  const env = {
4260
- os_type: os6.type(),
4261
- os_version: os6.release(),
4262
- architecture: os6.arch(),
4290
+ os_type: os7.type(),
4291
+ os_version: os7.release(),
4292
+ architecture: os7.arch(),
4263
4293
  workdir: cwd,
4264
4294
  shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
4265
- username: os6.userInfo().username,
4295
+ username: os7.userInfo().username,
4266
4296
  current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
4267
4297
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
4268
4298
  is_git_repo: isGitRepo(cwd) ? "yes" : "no",
@@ -4274,15 +4304,164 @@ function getUnifiedSystemPrompt() {
4274
4304
  test_framework: getTestFramework(cwd),
4275
4305
  test_command: getTestCommand(cwd)
4276
4306
  };
4277
- return Object.entries(env).reduce(
4278
- (prompt, [key, value]) => prompt.replaceAll(`{${key}}`, value),
4307
+ let prompt = Object.entries(env).reduce(
4308
+ (p, [key, value]) => p.replaceAll(`{${key}}`, value),
4279
4309
  SYSTEM_PROMPT
4280
4310
  );
4311
+ if (availableSkills && availableSkills.length > 0) {
4312
+ const skillsList = availableSkills.map((s) => `- ${s.name}: ${s.description}`).join("\n");
4313
+ prompt += `
4314
+
4315
+ ---
4316
+
4317
+ <available_skills>
4318
+ ## Specialized Skills
4319
+
4320
+ You have access to specialized knowledge modules called **skills**. Skills extend your capabilities with domain-specific expertise, workflows, and best practices.
4321
+
4322
+ **Available skills:**
4323
+ ${skillsList}
4324
+
4325
+ ---
4326
+
4327
+ ### Skill Selection Process
4328
+
4329
+ **BEFORE starting any task, follow this decision process:**
4330
+
4331
+ 1. **Understand the task** - What is the user trying to accomplish?
4332
+ 2. **Identify the domain** - What area of expertise does this task require? (e.g., version control, testing, deployment, documentation)
4333
+ 3. **Match against skills** - Read each skill's description above. Does any skill's domain match the task?
4334
+ 4. **Load if matched** - If a skill matches, load it FIRST with \`load_skill\` before taking any action
4335
+ 5. **Follow skill instructions** - Once loaded, the skill provides specific workflows to follow
4336
+
4337
+ **Decision examples:**
4338
+ \`\`\`
4339
+ Task: "Save my changes with a good message"
4340
+ \u2192 Domain: Version control, commits
4341
+ \u2192 Check skills: Is there a skill for git/commits/version control?
4342
+ \u2192 If yes: load_skill({ skill_name: "<matching-skill>" })
4343
+
4344
+ Task: "Make sure this code works correctly"
4345
+ \u2192 Domain: Testing, validation, quality assurance
4346
+ \u2192 Check skills: Is there a skill for testing?
4347
+ \u2192 If yes: load_skill({ skill_name: "<matching-skill>" })
4348
+
4349
+ Task: "Release this to users"
4350
+ \u2192 Domain: Publishing, deployment, release
4351
+ \u2192 Check skills: Is there a skill for publishing/releasing?
4352
+ \u2192 If yes: load_skill({ skill_name: "<matching-skill>" })
4353
+ \`\`\`
4354
+
4355
+ **Key principle:** Match the TASK DOMAIN to the SKILL DESCRIPTION, not specific words.
4356
+
4357
+ ---
4358
+
4359
+ ### Skill Interpretation Rules (CRITICAL)
4360
+
4361
+ 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.
4362
+
4363
+ **Frontmatter Structure:**
4364
+ \`\`\`yaml
4365
+ ---
4366
+ # IDENTITY
4367
+ name: skill-name # Unique identifier
4368
+ description: ... # Purpose of the skill
4369
+ version: 1.0.0
4370
+ author: ...
4371
+ license: ...
4372
+
4373
+ # DEPENDENCIES
4374
+ depends_on: # Other skills this skill may delegate to
4375
+ - other-skill-name
4376
+
4377
+ # TOOLS
4378
+ tools:
4379
+ required: # Tools you MUST use for this skill
4380
+ - shell_command
4381
+ - command_status
4382
+ recommended: # Tools that enhance execution
4383
+ - read_file_lines
4384
+ ---
4385
+ \`\`\`
4386
+
4387
+ **Interpretation Flow:**
4388
+ \`\`\`
4389
+ 1. LOAD SKILL
4390
+ load_skill({ skill_name: "npm-publish" })
4391
+
4392
+ 2. READ FRONTMATTER FIRST
4393
+ - name: npm-publish
4394
+ - depends_on: [git-conventional]
4395
+ - tools.required: [shell_command, command_status]
4396
+ - tools.recommended: [read_file_lines, message]
4397
+
4398
+ 3. VERIFY TOOLS
4399
+ Are required tools available? If not, inform user.
4400
+
4401
+ 4. READ SKILL BODY
4402
+ Workflow instructions, examples, rules
4403
+
4404
+ 5. EXECUTE WORKFLOW
4405
+ - Use the REQUIRED tools from frontmatter
4406
+ - When body says "delegate to X":
4407
+ a. Verify X is in depends_on
4408
+ b. load_skill({ skill_name: "X" })
4409
+ c. Execute X workflow
4410
+ d. Return to primary skill
4411
+ - Continue until workflow complete
4412
+ \`\`\`
4413
+
4414
+ **Field Meanings:**
4415
+ | Field | Purpose | Your Action |
4416
+ |-------|---------|-------------|
4417
+ | \`name\` | Skill identifier | Confirm correct skill loaded |
4418
+ | \`description\` | Skill purpose | Understand what this skill does |
4419
+ | \`depends_on\` | Skill dependencies | Know which skills can be delegated to |
4420
+ | \`tools.required\` | Mandatory tools | MUST use these tools for execution |
4421
+ | \`tools.recommended\` | Optional tools | Use if helpful |
4422
+
4423
+ **Orchestration Example:**
4424
+ \`\`\`
4425
+ User: "Publish the package"
4426
+
4427
+ 1. Match domain -> npm-publish skill
4428
+ 2. load_skill({ skill_name: "npm-publish" })
4429
+
4430
+ 3. Read frontmatter:
4431
+ - depends_on: [git-conventional]
4432
+ - tools.required: [shell_command, command_status]
4433
+
4434
+ 4. Read body -> Workflow says:
4435
+ "Step 1: If uncommitted changes, delegate to git-conventional"
4436
+
4437
+ 5. Check git status with shell_command (required tool)
4438
+ Found changes
4439
+
4440
+ 6. Delegate: load_skill({ skill_name: "git-conventional" })
4441
+ - Read git-conventional frontmatter
4442
+ - Follow its workflow for commit
4443
+ - Commit done
4444
+
4445
+ 7. Return to npm-publish Step 2: npm version patch
4446
+ 8. Step 3: npm publish
4447
+ 9. Done
4448
+ \`\`\`
4449
+
4450
+ **Rules:**
4451
+ - Frontmatter is the SPECIFICATION - read it first
4452
+ - Body is the WORKFLOW - execute after understanding spec
4453
+ - \`depends_on\` authorizes delegation - only delegate to listed skills
4454
+ - \`tools.required\` are mandatory - use them for execution
4455
+ - The skill body tells you WHEN to delegate, not the frontmatter
4456
+ </available_skills>
4457
+ `;
4458
+ }
4459
+ return prompt;
4281
4460
  }
4282
4461
  function isGitRepo(dir) {
4283
4462
  try {
4284
- const gitPath = path13.join(dir, ".git");
4285
- return fs11.existsSync(gitPath) && fs11.lstatSync(gitPath).isDirectory();
4463
+ const gitPath = path14.join(dir, ".git");
4464
+ return fs12.existsSync(gitPath) && fs12.lstatSync(gitPath).isDirectory();
4286
4465
  } catch {
4287
4466
  return false;
4288
4467
  }
@@ -4338,8 +4517,155 @@ function createApiContextWindow(fullHistory, maxTurns) {
4338
4517
  return finalContext;
4339
4518
  }
4340
4519
 
4520
+ // src/app/agent/core/llm/tool_call_normalizer.ts
4521
+ import { randomUUID } from "crypto";
4522
+ var ToolCallNormalizer = class {
4523
+ /**
4524
+ * Normaliza a mensagem do assistant, convertendo diferentes formatos de tool calls
4525
+ */
4526
+ static normalizeAssistantMessage(message2) {
4527
+ if (message2.tool_calls && this.isOpenAIFormat(message2.tool_calls)) {
4528
+ return message2;
4529
+ }
4530
+ const toolCalls = this.extractToolCalls(message2);
4531
+ if (toolCalls.length > 0) {
4532
+ return {
4533
+ role: message2.role || "assistant",
4534
+ content: message2.content || null,
4535
+ tool_calls: toolCalls
4536
+ };
4537
+ }
4538
+ return message2;
4539
+ }
4540
+ /**
4541
+ * Verifica se já está no formato OpenAI
4542
+ */
4543
+ static isOpenAIFormat(toolCalls) {
4544
+ if (!Array.isArray(toolCalls) || toolCalls.length === 0) return false;
4545
+ const firstCall = toolCalls[0];
4546
+ return typeof firstCall.id === "string" && firstCall.type === "function" && typeof firstCall.function?.name === "string" && typeof firstCall.function?.arguments === "string";
4547
+ }
4548
+ /**
4549
+ * Extrai tool calls de diversos formatos possíveis
4550
+ */
4551
+ static extractToolCalls(message2) {
4552
+ const results = [];
4553
+ if (message2.tool_calls && Array.isArray(message2.tool_calls)) {
4554
+ for (const call of message2.tool_calls) {
4555
+ const normalized = this.normalizeToolCall(call);
4556
+ if (normalized) results.push(normalized);
4557
+ }
4558
+ }
4559
+ if (typeof message2.content === "string" && message2.content.trim()) {
4560
+ const extracted = this.extractFromContent(message2.content);
4561
+ results.push(...extracted);
4562
+ }
4563
+ if (message2.function_call) {
4564
+ const normalized = this.normalizeToolCall(message2.function_call);
4565
+ if (normalized) results.push(normalized);
4566
+ }
4567
+ return results;
4568
+ }
4569
+ /**
4570
+ * Normaliza um único tool call para o formato OpenAI
4571
+ */
4572
+ static normalizeToolCall(call) {
4573
+ try {
4574
+ if (call.id && call.function?.name) {
4575
+ return {
4576
+ id: call.id,
4577
+ type: "function",
4578
+ function: {
4579
+ name: call.function.name,
4580
+ arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments)
4581
+ }
4582
+ };
4583
+ }
4584
+ if (call.name) {
4585
+ return {
4586
+ id: call.id || randomUUID(),
4587
+ type: "function",
4588
+ function: {
4589
+ name: call.name,
4590
+ arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments || {})
4591
+ }
4592
+ };
4593
+ }
4594
+ if (call.function && typeof call.function === "object") {
4595
+ return {
4596
+ id: call.id || randomUUID(),
4597
+ type: "function",
4598
+ function: {
4599
+ name: call.function.name,
4600
+ arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments || {})
4601
+ }
4602
+ };
4603
+ }
4604
+ return null;
4605
+ } catch (error) {
4606
+ console.error("Error normalizing tool call:", error, call);
4607
+ return null;
4608
+ }
4609
+ }
4610
+ /**
4611
+ * Extrai tool calls do content (pode estar em markdown, JSON, etc)
4612
+ */
4613
+ static extractFromContent(content) {
4614
+ const results = [];
4615
+ const cleanContent = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
4616
+ const jsonMatches = this.extractJsonObjects(cleanContent);
4617
+ for (const jsonStr of jsonMatches) {
4618
+ try {
4619
+ const parsed = JSON.parse(jsonStr);
4620
+ if (Array.isArray(parsed)) {
4621
+ for (const call of parsed) {
4622
+ const normalized = this.normalizeToolCall(call);
4623
+ if (normalized) results.push(normalized);
4624
+ }
4625
+ } else if (parsed.name || parsed.function) {
4626
+ const normalized = this.normalizeToolCall(parsed);
4627
+ if (normalized) results.push(normalized);
4628
+ } else if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
4629
+ for (const call of parsed.tool_calls) {
4630
+ const normalized = this.normalizeToolCall(call);
4631
+ if (normalized) results.push(normalized);
4632
+ }
4633
+ }
4634
+ } catch (e) {
4635
+ }
4636
+ }
4637
+ return results;
4638
+ }
4639
+ /**
4640
+ * Extrai objetos JSON de uma string (suporta múltiplos objetos)
4641
+ */
4642
+ static extractJsonObjects(text) {
4643
+ const results = [];
4644
+ let depth = 0;
4645
+ let start = -1;
4646
+ for (let i = 0; i < text.length; i++) {
4647
+ if (text[i] === "{") {
4648
+ if (depth === 0) start = i;
4649
+ depth++;
4650
+ } else if (text[i] === "}") {
4651
+ depth--;
4652
+ if (depth === 0 && start !== -1) {
4653
+ results.push(text.substring(start, i + 1));
4654
+ start = -1;
4655
+ }
4656
+ }
4657
+ }
4658
+ return results;
4659
+ }
4660
+ /**
4661
+ * Valida se um tool call normalizado é válido
4662
+ */
4663
+ static isValidToolCall(call) {
4664
+ return !!(call.id && call.type === "function" && call.function?.name && typeof call.function.arguments === "string");
4665
+ }
4666
+ };
4667
+
4341
4668
  // src/app/agent/bluma/core/bluma.ts
4342
- init_tool_call_normalizer();
4343
4669
  var BluMaAgent = class {
4344
4670
  llm;
4345
4671
  deploymentName;
@@ -4349,6 +4675,7 @@ var BluMaAgent = class {
4349
4675
  eventBus;
4350
4676
  mcpClient;
4351
4677
  feedbackSystem;
4678
+ skillLoader;
4352
4679
  maxContextTurns = 30;
4353
4680
  isInterrupted = false;
4354
4681
  constructor(sessionId2, eventBus2, llm, deploymentName, mcpClient, feedbackSystem) {
@@ -4358,6 +4685,7 @@ var BluMaAgent = class {
4358
4685
  this.deploymentName = deploymentName;
4359
4686
  this.mcpClient = mcpClient;
4360
4687
  this.feedbackSystem = feedbackSystem;
4688
+ this.skillLoader = new SkillLoader(process.cwd());
4361
4689
  this.eventBus.on("user_interrupt", () => {
4362
4690
  this.isInterrupted = true;
4363
4691
  this.eventBus.emit("backend_message", { type: "done", status: "interrupted" });
@@ -4369,6 +4697,10 @@ var BluMaAgent = class {
4369
4697
  try {
4370
4698
  if (this.sessionFile) {
4371
4699
  await saveSessionHistory(this.sessionFile, this.history);
4700
+ } else {
4701
+ const [sessionFile, _] = await loadOrcreateSession(this.sessionId);
4702
+ this.sessionFile = sessionFile;
4703
+ await saveSessionHistory(this.sessionFile, this.history);
4372
4704
  }
4373
4705
  } catch (e) {
4374
4706
  this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
@@ -4381,8 +4713,13 @@ var BluMaAgent = class {
4381
4713
  const [sessionFile, history] = await loadOrcreateSession(this.sessionId);
4382
4714
  this.sessionFile = sessionFile;
4383
4715
  this.history = history;
4716
+ initializeSkillContext({
4717
+ history: this.history,
4718
+ skillLoader: this.skillLoader
4719
+ });
4384
4720
  if (this.history.length === 0) {
4385
- const systemPrompt = getUnifiedSystemPrompt();
4721
+ const availableSkills = this.skillLoader.listAvailable();
4722
+ const systemPrompt = getUnifiedSystemPrompt(availableSkills);
4386
4723
  this.history.push({ role: "system", content: systemPrompt });
4387
4724
  await saveSessionHistory(this.sessionFile, this.history);
4388
4725
  }
@@ -4397,6 +4734,9 @@ var BluMaAgent = class {
4397
4734
  this.isInterrupted = false;
4398
4735
  const inputText = String(userInput.content || "").trim();
4399
4736
  this.history.push({ role: "user", content: inputText });
4737
+ if (inputText === "/init") {
4738
+ this.eventBus.emit("dispatch", inputText);
4739
+ }
4400
4740
  await this._continueConversation();
4401
4741
  }
4402
4742
  async handleToolResponse(decisionData) {
@@ -4495,9 +4835,15 @@ var BluMaAgent = class {
4495
4835
  });
4496
4836
  }
4497
4837
  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" });
4838
+ if (toolName === "message") {
4839
+ try {
4840
+ const resultObj = typeof toolResultContent === "string" ? JSON.parse(toolResultContent) : toolResultContent;
4841
+ if (resultObj.message_type === "result") {
4842
+ shouldContinueConversation = false;
4843
+ this.eventBus.emit("backend_message", { type: "done", status: "completed" });
4844
+ }
4845
+ } catch {
4846
+ }
4501
4847
  }
4502
4848
  } else {
4503
4849
  toolResultContent = "The system rejected this action. Verify that the command you are executing contributes to the tasks intent and try again.";
@@ -4524,7 +4870,7 @@ var BluMaAgent = class {
4524
4870
 
4525
4871
  ${editData.error.display}`;
4526
4872
  }
4527
- const filename = path14.basename(toolArgs.file_path);
4873
+ const filename = path15.basename(toolArgs.file_path);
4528
4874
  return createDiff(filename, editData.currentContent || "", editData.newContent);
4529
4875
  } catch (e) {
4530
4876
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -4550,18 +4896,18 @@ ${editData.error.display}`;
4550
4896
  this.eventBus.emit("backend_message", { type: "info", message: "Agent task cancelled by user." });
4551
4897
  return;
4552
4898
  }
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;
4899
+ let message2 = response.choices[0].message;
4900
+ message2 = ToolCallNormalizer.normalizeAssistantMessage(message2);
4901
+ if (message2.reasoning_content || message2.reasoning) {
4902
+ const reasoningText = message2.reasoning_content || message2.reasoning;
4557
4903
  this.eventBus.emit("backend_message", {
4558
4904
  type: "reasoning",
4559
4905
  content: typeof reasoningText === "string" ? reasoningText : JSON.stringify(reasoningText)
4560
4906
  });
4561
4907
  }
4562
- this.history.push(message);
4563
- if (message.tool_calls && message.tool_calls.length > 0) {
4564
- const validToolCalls = message.tool_calls.filter(
4908
+ this.history.push(message2);
4909
+ if (message2.tool_calls && message2.tool_calls.length > 0) {
4910
+ const validToolCalls = message2.tool_calls.filter(
4565
4911
  (call) => ToolCallNormalizer.isValidToolCall(call)
4566
4912
  );
4567
4913
  if (validToolCalls.length === 0) {
@@ -4573,12 +4919,12 @@ ${editData.error.display}`;
4573
4919
  return;
4574
4920
  }
4575
4921
  const autoApprovedTools = [
4576
- "agent_end_turn",
4577
- "message_notify_user",
4922
+ "message",
4578
4923
  "ls_tool",
4579
4924
  "count_file_lines",
4580
4925
  "read_file_lines",
4581
- "todo"
4926
+ "todo",
4927
+ "load_skill"
4582
4928
  ];
4583
4929
  const toolToCall = validToolCalls[0];
4584
4930
  const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
@@ -4594,13 +4940,13 @@ ${editData.error.display}`;
4594
4940
  this.eventBus.emit("backend_message", { type: "confirmation_request", tool_calls: validToolCalls });
4595
4941
  }
4596
4942
  }
4597
- } else if (message.content) {
4598
- this.eventBus.emit("backend_message", { type: "assistant_message", content: message.content });
4943
+ } else if (message2.content) {
4944
+ this.eventBus.emit("backend_message", { type: "assistant_message", content: message2.content });
4599
4945
  const feedback = this.feedbackSystem.generateFeedback({
4600
4946
  event: "protocol_violation_direct_text",
4601
- details: { violationContent: message.content }
4947
+ details: { violationContent: message2.content }
4602
4948
  });
4603
- this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: message.content });
4949
+ this.eventBus.emit("backend_message", { type: "protocol_violation", message: feedback.message, content: message2.content });
4604
4950
  this.history.push({ role: "system", content: feedback.correction });
4605
4951
  await this._continueConversation();
4606
4952
  } else {
@@ -4617,14 +4963,20 @@ ${editData.error.display}`;
4617
4963
  };
4618
4964
 
4619
4965
  // src/app/agent/core/llm/llm.ts
4620
- var OpenAIAdapter = class {
4966
+ import OpenAI from "openai";
4967
+ var LLMService = class {
4621
4968
  client;
4622
- constructor(client) {
4623
- this.client = client;
4969
+ defaultModel;
4970
+ constructor(config2) {
4971
+ this.client = new OpenAI({
4972
+ apiKey: config2.apiKey,
4973
+ baseURL: config2.baseUrl || "http://109.51.171.144/mistrall/v1"
4974
+ });
4975
+ this.defaultModel = config2.model || "";
4624
4976
  }
4625
4977
  async chatCompletion(params) {
4626
4978
  const resp = await this.client.chat.completions.create({
4627
- model: params.model,
4979
+ model: params.model || this.defaultModel,
4628
4980
  messages: params.messages,
4629
4981
  tools: params.tools,
4630
4982
  tool_choice: params.tool_choice,
@@ -4634,6 +4986,12 @@ var OpenAIAdapter = class {
4634
4986
  });
4635
4987
  return resp;
4636
4988
  }
4989
+ /**
4990
+ * Retorna o modelo padrão configurado
4991
+ */
4992
+ getDefaultModel() {
4993
+ return this.defaultModel;
4994
+ }
4637
4995
  };
4638
4996
 
4639
4997
  // src/app/agent/subagents/registry.ts
@@ -4648,7 +5006,7 @@ function getSubAgentByCommand(cmd) {
4648
5006
  }
4649
5007
 
4650
5008
  // src/app/agent/subagents/init/init_system_prompt.ts
4651
- import os7 from "os";
5009
+ import os8 from "os";
4652
5010
  var SYSTEM_PROMPT2 = `
4653
5011
 
4654
5012
  ### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
@@ -4662,7 +5020,7 @@ You extend the BluMa multi-agent architecture and handle the project bootstrappi
4662
5020
  You are BluMa InitSubAgent. Maintain professionalism and technical language.
4663
5021
 
4664
5022
  - Communication:
4665
- ALL messages must be sent via 'message_notify_user'.
5023
+ ALL messages must be sent via 'message'.
4666
5024
  No direct text replies to the user.
4667
5025
 
4668
5026
  - Task Completion:
@@ -4787,8 +5145,8 @@ Rule Summary:
4787
5145
  - Never invent file content. Read files via tools to confirm.
4788
5146
 
4789
5147
  ## 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.
5148
+ - Emit 'backend_message' events through tools only (message) for progress updates.
5149
+ - Before writing BluMa.md, propose structure via message and proceed using edit_tool.
4792
5150
  - If an irreversible operation is needed (e.g., overwriting an existing BluMa.md), issue 'confirmation_request' unless user policy indicates auto-approval.
4793
5151
  - 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
5152
  - On successful generation of BluMa.md, emit 'done' with status 'completed' and call agent_end_turn.
@@ -4802,7 +5160,7 @@ Rule Summary:
4802
5160
  ## EXEMPLAR FLOW (GUIDELINE)
4803
5161
  1) Explore repo: ls + targeted readLines for key files (package.json, tsconfig.json, README, etc.)
4804
5162
  2) Synthesize stack and structure with citations of evidence (file paths) in the notebook
4805
- 3) Draft BluMa.md structure (message_notify_user)
5163
+ 3) Draft BluMa.md structure (message)
4806
5164
  4) Write BluMa.md via edit_tool
4807
5165
  5) Announce completion and agent_end_turn
4808
5166
 
@@ -4811,12 +5169,12 @@ Rule Summary:
4811
5169
  function getInitPrompt() {
4812
5170
  const now = /* @__PURE__ */ new Date();
4813
5171
  const collectedData = {
4814
- os_type: os7.type(),
4815
- os_version: os7.release(),
4816
- architecture: os7.arch(),
5172
+ os_type: os8.type(),
5173
+ os_version: os8.release(),
5174
+ architecture: os8.arch(),
4817
5175
  workdir: process.cwd(),
4818
5176
  shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
4819
- username: os7.userInfo().username || "Unknown",
5177
+ username: os8.userInfo().username || "Unknown",
4820
5178
  current_date: now.toISOString().split("T")[0],
4821
5179
  // Formato YYYY-MM-DD
4822
5180
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
@@ -4907,17 +5265,17 @@ ${editData.error.display}`;
4907
5265
  this.emitEvent("info", { message: "SubAgent task cancelled byuserr." });
4908
5266
  return;
4909
5267
  }
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;
5268
+ const message2 = response.choices[0].message;
5269
+ this.history.push(message2);
5270
+ if (message2.tool_calls) {
5271
+ await this._handleToolExecution({ type: "user_decision_execute", tool_calls: message2.tool_calls });
5272
+ const lastToolName = message2.tool_calls[0].function.name;
4915
5273
  if (!lastToolName.includes("agent_end_turn") && !this.isInterrupted) {
4916
5274
  await this._continueConversation();
4917
5275
  }
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 });
5276
+ } else if (message2.content) {
5277
+ this.emitEvent("assistant_message", { content: message2.content });
5278
+ this.emitEvent("protocol_violation", { message: "Direct text emission detected from subagent.", content: message2.content });
4921
5279
  } else {
4922
5280
  this.emitEvent("info", { message: "SubAgent is thinking... continuing reasoning cycle." });
4923
5281
  }
@@ -5068,21 +5426,43 @@ var SubAgentsBluMa = class {
5068
5426
  }
5069
5427
  };
5070
5428
 
5429
+ // src/app/agent/routeManager.ts
5430
+ var RouteManager = class {
5431
+ routeHandlers;
5432
+ subAgents;
5433
+ core;
5434
+ constructor(subAgents, core) {
5435
+ this.routeHandlers = /* @__PURE__ */ new Map();
5436
+ this.subAgents = subAgents;
5437
+ this.core = core;
5438
+ }
5439
+ registerRoute(path18, handler) {
5440
+ this.routeHandlers.set(path18, handler);
5441
+ }
5442
+ async handleRoute(payload) {
5443
+ const inputText = String(payload.content || "").trim();
5444
+ for (const [path18, handler] of this.routeHandlers) {
5445
+ if (inputText === path18 || inputText.startsWith(`${path18} `)) {
5446
+ return handler({ content: inputText });
5447
+ }
5448
+ }
5449
+ await this.core.processTurn({ content: inputText });
5450
+ }
5451
+ };
5452
+
5071
5453
  // src/app/agent/agent.ts
5072
- var globalEnvPath = path15.join(os8.homedir(), ".bluma-cli", ".env");
5454
+ var globalEnvPath = path16.join(os9.homedir(), ".bluma-cli", ".env");
5073
5455
  dotenv.config({ path: globalEnvPath });
5074
5456
  var Agent = class {
5075
5457
  sessionId;
5076
5458
  eventBus;
5077
5459
  mcpClient;
5078
5460
  feedbackSystem;
5079
- // Mantido caso UI dependa de eventos
5080
5461
  llm;
5081
- deploymentName;
5462
+ routeManager;
5463
+ model;
5082
5464
  core;
5083
- // Delegado
5084
5465
  subAgents;
5085
- // Orquestrador de subagentes
5086
5466
  toolInvoker;
5087
5467
  constructor(sessionId2, eventBus2) {
5088
5468
  this.sessionId = sessionId2;
@@ -5091,75 +5471,53 @@ var Agent = class {
5091
5471
  this.toolInvoker = nativeToolInvoker;
5092
5472
  this.mcpClient = new MCPClient(nativeToolInvoker, eventBus2);
5093
5473
  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) {
5474
+ const apiKey = process.env.NOMAD_API_KEY;
5475
+ const baseUrl = process.env.NOMAD_BASE_URL;
5476
+ this.model = process.env.MODEL_NOMAD || "";
5477
+ if (!apiKey) {
5101
5478
  const platform = process.platform;
5102
- const varList = missing.join(", ");
5103
5479
  let guidance = "";
5104
5480
  if (platform === "win32") {
5105
5481
  guidance = [
5106
5482
  "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("");
5483
+ ' $env:NOMAD_API_KEY="<your-key>"',
5484
+ " # Para persistir:",
5485
+ ' [System.Environment]::SetEnvironmentVariable("NOMAD_API_KEY", "<your-key>", "User")'
5486
+ ].join("\n");
5115
5487
  } else if (platform === "darwin" || platform === "linux") {
5116
5488
  guidance = [
5117
5489
  "macOS/Linux (bash/zsh):",
5118
- ...missing.map((v) => ` echo 'export ${v}="<value>"' >> ~/.bashrc # ou ~/.zshrc`),
5119
- " source ~/.bashrc # ou: source ~/.zshrc"
5120
- ].join("");
5490
+ ` echo 'export NOMAD_API_KEY="<your-key>"' >> ~/.bashrc`,
5491
+ " source ~/.bashrc"
5492
+ ].join("\n");
5121
5493
  } else {
5122
- guidance = [
5123
- "Generic POSIX:",
5124
- ...missing.map((v) => ` export ${v}="<value>"`)
5125
- ].join("");
5494
+ guidance = ' export NOMAD_API_KEY="<your-key>"';
5126
5495
  }
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}.`,
5496
+ const message2 = [
5497
+ "Missing required environment variable: NOMAD_API_KEY.",
5498
+ `Configure it globally using the commands below, or set it in: ${globalEnvPath}`,
5130
5499
  "",
5131
5500
  guidance
5132
- ].join("");
5501
+ ].join("\n");
5133
5502
  this.eventBus.emit("backend_message", {
5134
5503
  type: "error",
5135
5504
  code: "missing_env",
5136
- missing,
5505
+ missing: ["NOMAD_API_KEY", "MODEL_NOMAD"],
5137
5506
  path: globalEnvPath,
5138
- message
5507
+ message: message2
5139
5508
  });
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
- }
5509
+ throw new Error(message2);
5510
+ }
5511
+ this.llm = new LLMService({
5512
+ apiKey,
5513
+ baseUrl,
5514
+ model: this.model
5156
5515
  });
5157
- this.llm = new OpenAIAdapter(ollama);
5158
5516
  this.core = new BluMaAgent(
5159
5517
  this.sessionId,
5160
5518
  this.eventBus,
5161
5519
  this.llm,
5162
- this.deploymentName,
5520
+ this.model,
5163
5521
  this.mcpClient,
5164
5522
  this.feedbackSystem
5165
5523
  );
@@ -5168,9 +5526,10 @@ var Agent = class {
5168
5526
  mcpClient: this.mcpClient,
5169
5527
  toolInvoker: this.toolInvoker,
5170
5528
  llm: this.llm,
5171
- deploymentName: this.deploymentName,
5529
+ deploymentName: this.model,
5172
5530
  projectRoot: process.cwd()
5173
5531
  });
5532
+ this.routeManager = new RouteManager(this.subAgents, this.core);
5174
5533
  }
5175
5534
  async initialize() {
5176
5535
  await this.core.initialize();
@@ -5184,10 +5543,9 @@ var Agent = class {
5184
5543
  async processTurn(userInput) {
5185
5544
  const inputText = String(userInput.content || "").trim();
5186
5545
  if (inputText === "/init" || inputText.startsWith("/init ")) {
5187
- await this.dispatchToSubAgent({ command: "/init", content: inputText });
5188
- return;
5546
+ this.routeManager.registerRoute("/init", this.dispatchToSubAgent);
5189
5547
  }
5190
- await this.core.processTurn({ content: inputText });
5548
+ await this.routeManager.handleRoute({ content: inputText });
5191
5549
  }
5192
5550
  async handleToolResponse(decisionData) {
5193
5551
  await this.core.handleToolResponse(decisionData);
@@ -5286,12 +5644,12 @@ var renderShellCommand2 = ({ args }) => {
5286
5644
  };
5287
5645
  var renderLsTool2 = ({ args }) => {
5288
5646
  const parsed = parseArgs(args);
5289
- const path17 = parsed.directory_path || ".";
5647
+ const path18 = parsed.directory_path || ".";
5290
5648
  return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
5291
5649
  /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "ls" }),
5292
5650
  /* @__PURE__ */ jsxs8(Text8, { children: [
5293
5651
  " ",
5294
- path17
5652
+ path18
5295
5653
  ] })
5296
5654
  ] });
5297
5655
  };
@@ -5422,7 +5780,7 @@ var renderFindByName = ({ args }) => {
5422
5780
  var renderGrepSearch = ({ args }) => {
5423
5781
  const parsed = parseArgs(args);
5424
5782
  const query = parsed.query || "";
5425
- const path17 = parsed.path || ".";
5783
+ const path18 = parsed.path || ".";
5426
5784
  return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
5427
5785
  /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "grep" }),
5428
5786
  /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
@@ -5432,7 +5790,7 @@ var renderGrepSearch = ({ args }) => {
5432
5790
  ] }),
5433
5791
  /* @__PURE__ */ jsxs8(Text8, { children: [
5434
5792
  " ",
5435
- path17
5793
+ path18
5436
5794
  ] })
5437
5795
  ] });
5438
5796
  };
@@ -5500,6 +5858,17 @@ var renderSearchWeb = ({ args }) => {
5500
5858
  ] })
5501
5859
  ] });
5502
5860
  };
5861
+ var renderLoadSkill = ({ args }) => {
5862
+ const parsed = parseArgs(args);
5863
+ const skillName = parsed.skill_name || "[no skill]";
5864
+ return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
5865
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "load skill" }),
5866
+ /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
5867
+ " ",
5868
+ skillName
5869
+ ] })
5870
+ ] });
5871
+ };
5503
5872
  var renderGeneric2 = ({ toolName, args }) => {
5504
5873
  const parsed = parseArgs(args);
5505
5874
  const keys = Object.keys(parsed).slice(0, 2);
@@ -5524,13 +5893,14 @@ var ToolRenderDisplay = {
5524
5893
  view_file_outline: renderViewFileOutline,
5525
5894
  command_status: renderCommandStatus,
5526
5895
  task_boundary: renderTaskBoundary,
5527
- search_web: renderSearchWeb
5896
+ search_web: renderSearchWeb,
5897
+ load_skill: renderLoadSkill
5528
5898
  };
5529
5899
 
5530
5900
  // src/app/ui/components/ToolCallDisplay.tsx
5531
5901
  import { jsx as jsx9 } from "react/jsx-runtime";
5532
5902
  var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
5533
- if (toolName.includes("message_notify_user") || toolName.includes("agent_end_turn")) {
5903
+ if (toolName.includes("message")) {
5534
5904
  return null;
5535
5905
  }
5536
5906
  const Renderer = ToolRenderDisplay[toolName] || ((props2) => renderGeneric2({ ...props2, toolName }));
@@ -5731,11 +6101,11 @@ var truncateLines = (text, max) => {
5731
6101
  };
5732
6102
  };
5733
6103
  var ToolResultDisplayComponent = ({ toolName, result }) => {
5734
- if (toolName.includes("agent_end_turn") || toolName.includes("task_boundary")) {
6104
+ if (toolName.includes("task_boundary")) {
5735
6105
  return null;
5736
6106
  }
5737
6107
  const parsed = parseResult(result);
5738
- if (toolName.includes("message_notify_user")) {
6108
+ if (toolName.includes("message")) {
5739
6109
  const body = parsed?.content?.body || parsed?.body || parsed?.message;
5740
6110
  if (!body) return null;
5741
6111
  return /* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(MarkdownRenderer, { markdown: body }) });
@@ -5818,6 +6188,23 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
5818
6188
  ] })
5819
6189
  ] });
5820
6190
  }
6191
+ if (toolName.includes("load_skill") && parsed) {
6192
+ if (!parsed.success) {
6193
+ return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "red", children: [
6194
+ "\u2717 Skill not found: ",
6195
+ parsed.message
6196
+ ] }) });
6197
+ }
6198
+ return /* @__PURE__ */ jsxs10(Box11, { paddingLeft: 3, marginBottom: 2, children: [
6199
+ /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "green", children: "Skill loaded: " }),
6200
+ /* @__PURE__ */ jsx11(Text10, { color: "green", bold: true, children: parsed.skill_name }),
6201
+ /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
6202
+ " \u2014 ",
6203
+ parsed.description?.slice(0, 50),
6204
+ parsed.description?.length > 100 ? "..." : ""
6205
+ ] })
6206
+ ] });
6207
+ }
5821
6208
  if (toolName.includes("todo")) {
5822
6209
  return null;
5823
6210
  }
@@ -6019,16 +6406,16 @@ var SlashCommands_default = SlashCommands;
6019
6406
  // src/app/agent/utils/update_check.ts
6020
6407
  import updateNotifier from "update-notifier";
6021
6408
  import { fileURLToPath as fileURLToPath3 } from "url";
6022
- import path16 from "path";
6023
- import fs12 from "fs";
6409
+ import path17 from "path";
6410
+ import fs13 from "fs";
6024
6411
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
6025
6412
  function findBlumaPackageJson(startDir) {
6026
6413
  let dir = startDir;
6027
6414
  for (let i = 0; i < 10; i++) {
6028
- const candidate = path16.join(dir, "package.json");
6029
- if (fs12.existsSync(candidate)) {
6415
+ const candidate = path17.join(dir, "package.json");
6416
+ if (fs13.existsSync(candidate)) {
6030
6417
  try {
6031
- const raw = fs12.readFileSync(candidate, "utf8");
6418
+ const raw = fs13.readFileSync(candidate, "utf8");
6032
6419
  const parsed = JSON.parse(raw);
6033
6420
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
6034
6421
  return { name: parsed.name, version: parsed.version };
@@ -6036,7 +6423,7 @@ function findBlumaPackageJson(startDir) {
6036
6423
  } catch {
6037
6424
  }
6038
6425
  }
6039
- const parent = path16.dirname(dir);
6426
+ const parent = path17.dirname(dir);
6040
6427
  if (parent === dir) break;
6041
6428
  dir = parent;
6042
6429
  }
@@ -6049,12 +6436,12 @@ async function checkForUpdates() {
6049
6436
  }
6050
6437
  const binPath = process.argv?.[1];
6051
6438
  let pkg = null;
6052
- if (binPath && fs12.existsSync(binPath)) {
6053
- pkg = findBlumaPackageJson(path16.dirname(binPath));
6439
+ if (binPath && fs13.existsSync(binPath)) {
6440
+ pkg = findBlumaPackageJson(path17.dirname(binPath));
6054
6441
  }
6055
6442
  if (!pkg) {
6056
6443
  const __filename = fileURLToPath3(import.meta.url);
6057
- const __dirname = path16.dirname(__filename);
6444
+ const __dirname = path17.dirname(__filename);
6058
6445
  pkg = findBlumaPackageJson(__dirname);
6059
6446
  }
6060
6447
  if (!pkg) {
@@ -6100,11 +6487,11 @@ function parseUpdateMessage(msg) {
6100
6487
  hint: hintLine || void 0
6101
6488
  };
6102
6489
  }
6103
- var UpdateNotice = ({ message }) => {
6104
- const { name, current, latest, hint } = parseUpdateMessage(message);
6490
+ var UpdateNotice = ({ message: message2 }) => {
6491
+ const { name, current, latest, hint } = parseUpdateMessage(message2);
6105
6492
  return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", marginBottom: 1, children: [
6106
6493
  /* @__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 }),
6494
+ name && current && latest ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: `${name}: ${current} \u2192 ${latest}` }) : /* @__PURE__ */ jsx13(Text12, { color: "gray", children: message2 }),
6108
6495
  hint ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: hint }) : null
6109
6496
  ] });
6110
6497
  };
@@ -6113,13 +6500,13 @@ var UpdateNotice_default = UpdateNotice;
6113
6500
  // src/app/ui/components/ErrorMessage.tsx
6114
6501
  import { Box as Box14, Text as Text13 } from "ink";
6115
6502
  import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
6116
- var ErrorMessage = ({ message, details, hint }) => {
6503
+ var ErrorMessage = ({ message: message2, details, hint }) => {
6117
6504
  return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
6118
6505
  /* @__PURE__ */ jsxs13(Box14, { children: [
6119
6506
  /* @__PURE__ */ jsx14(Text13, { color: "red", children: "\u2717" }),
6120
6507
  /* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: " Error" })
6121
6508
  ] }),
6122
- /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message }) }),
6509
+ /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message2 }) }),
6123
6510
  details && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: details }) }),
6124
6511
  hint && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
6125
6512
  "hint: ",
@@ -6155,7 +6542,7 @@ var ReasoningDisplay = memo9(ReasoningDisplayComponent);
6155
6542
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6156
6543
  var SAFE_AUTO_APPROVE_TOOLS = [
6157
6544
  // Comunicação/UI
6158
- "message_notify_user",
6545
+ "message",
6159
6546
  "agent_end_turn",
6160
6547
  "todo",
6161
6548
  "task_boundary",