bimmo-cli 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/interface.js +59 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimmo-cli",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "description": "🌿 Plataforma de IA universal com Modo Normal, Agentes e Swarms. Suporte a Diffs coloridos, Auto-Edit (Auto/Manual) e Contexto Inteligente.",
5
5
  "bin": {
6
6
  "bimmo": "bin/bimmo"
package/src/interface.js CHANGED
@@ -21,12 +21,18 @@ const __dirname = path.dirname(__filename);
21
21
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
22
22
  const version = pkg.version;
23
23
 
24
- // Configuração do renderizador
24
+ // Configuração do renderizador - Forçamos o escape de HTML para garantir que tags não apareçam
25
25
  marked.use(new TerminalRenderer({
26
26
  heading: chalk.hex('#c084fc').bold,
27
27
  code: chalk.hex('#00ff9d'),
28
28
  }));
29
29
 
30
+ marked.setOptions({
31
+ sanitize: true, // Depreciado mas ajuda em versões antigas
32
+ headerIds: false,
33
+ mangle: false
34
+ });
35
+
30
36
  const green = chalk.hex('#00ff9d');
31
37
  const lavender = chalk.hex('#c084fc');
32
38
  const gray = chalk.gray;
@@ -98,21 +104,27 @@ function getModeStyle() {
98
104
  }
99
105
 
100
106
  /**
101
- * LIMPEZA BRUTA DE HTML
102
- * Remove tags como <p>, <br>, <div> e qualquer outra tag que venha da IA.
107
+ * LIMPEZA ABSOLUTA DE HTML
108
+ * Remove tags HTML antes da renderização Markdown.
103
109
  */
104
110
  function cleanAIResponse(text) {
105
111
  if (!text) return "";
106
- return text
107
- .replace(/<br\s*\/?>/gi, '\n') // <br> vira nova linha
108
- .replace(/<\/p>/gi, '\n\n') // </p> vira duas novas linhas
109
- .replace(/<\/div>/gi, '\n') // </div> vira nova linha
110
- .replace(/<[^>]*>?/gm, '') // REMOVE QUALQUER TAG RESTANTE (Regex robusta)
111
- .replace(/&nbsp;/g, ' ') // Limpa espaços HTML
112
- .replace(/&lt;/g, '<') // Desescapa <
113
- .replace(/&gt;/g, '>') // Desescapa >
114
- .replace(/&amp;/g, '&') // Desescapa &
115
- .trim();
112
+
113
+ let cleaned = text
114
+ .replace(/<br\s*\/?>/gi, '\n')
115
+ .replace(/<\/p>/gi, '\n\n')
116
+ .replace(/<\/div>/gi, '\n')
117
+ .replace(/<li>/gi, '* ')
118
+ .replace(/<\/li>/gi, '\n');
119
+
120
+ // Regex extremamente agressiva para remover qualquer tag restante
121
+ cleaned = cleaned.replace(/<[^>]*>?/gm, '');
122
+
123
+ // Decodifica entidades comuns
124
+ const entities = {
125
+ '&nbsp;': ' ', '&lt;': '<', '&gt;': '>', '&amp;': '&', '&quot;': '"', '&apos;': "'"
126
+ };
127
+ return cleaned.replace(/&[a-z0-9#]+;/gi, (match) => entities[match] || match).trim();
116
128
  }
117
129
 
118
130
  export async function startInteractive() {
@@ -179,13 +191,33 @@ export async function startInteractive() {
179
191
  const rawInput = input.trim();
180
192
  const cmd = rawInput.toLowerCase();
181
193
 
182
- if (cmd === '/exit' || cmd === 'exit' || cmd === 'sair') { console.log(lavender('\n👋 BIMMO encerrando sessão. Até logo!\n')); process.exit(0); }
194
+ // COMANDOS CLI (Tratados antes de enviar para IA)
195
+ if (cmd === '/exit' || cmd === 'exit' || cmd === 'sair') { process.exit(0); }
196
+
183
197
  if (cmd === '/chat') { currentMode = 'chat'; console.log(lavender('✓ Modo CHAT.\n')); continue; }
184
198
  if (cmd === '/plan') { currentMode = 'plan'; console.log(yellow('✓ Modo PLAN.\n')); continue; }
199
+
185
200
  if (cmd === '/edit') { currentMode = 'edit'; console.log(chalk.red(`⚠️ Modo EDIT ativado.\n`)); continue; }
186
201
  if (cmd === '/edit auto') { currentMode = 'edit'; editState.autoAccept = true; console.log(chalk.red('⚠️ Modo EDIT (AUTO) ativado.\n')); continue; }
187
202
  if (cmd === '/edit manual') { currentMode = 'edit'; editState.autoAccept = false; console.log(chalk.red('⚠️ Modo EDIT (MANUAL) ativado.\n')); continue; }
188
203
 
204
+ if (cmd === '/init') {
205
+ const bimmoRcPath = path.join(process.cwd(), '.bimmorc.json');
206
+ if (fs.existsSync(bimmoRcPath)) {
207
+ const { overwrite } = await inquirer.prompt([{ type: 'confirm', name: 'overwrite', message: 'O arquivo .bimmorc.json já existe. Sobrescrever?', default: false }]);
208
+ if (!overwrite) continue;
209
+ }
210
+ const initialConfig = {
211
+ projectName: path.basename(process.cwd()),
212
+ rules: ["Siga as convenções existentes.", "Prefira código modular."],
213
+ ignorePatterns: ["node_modules", ".git"]
214
+ };
215
+ fs.writeFileSync(bimmoRcPath, JSON.stringify(initialConfig, null, 2));
216
+ console.log(green(`\n✅ .bimmorc.json criado com sucesso.\n`));
217
+ resetMessages();
218
+ continue;
219
+ }
220
+
189
221
  if (cmd.startsWith('/switch ')) {
190
222
  const profileName = rawInput.split(' ')[1];
191
223
  if (profileName && switchProfile(profileName)) {
@@ -199,28 +231,22 @@ export async function startInteractive() {
199
231
  if (cmd.startsWith('/use ')) {
200
232
  const agentName = rawInput.split(' ')[1];
201
233
  const agents = config.agents || {};
202
- if (agentName === 'normal' || agentName === 'default') { activePersona = null; console.log(lavender(`\n✓ Voltando para o Modo Normal.\n`)); resetMessages(); continue; }
234
+ if (agentName === 'normal' || agentName === 'default') { activePersona = null; resetMessages(); continue; }
203
235
  if (agents[agentName]) {
204
236
  activePersona = agentName;
205
237
  const agent = agents[agentName];
206
238
  if (switchProfile(agent.profile)) { config = getConfig(); provider = createProvider(config); }
207
239
  currentMode = agent.mode || 'chat';
208
- console.log(green(`\n✓ Agora você está falando com o Agente: ${bold(agentName)}`));
240
+ console.log(green(`\n✓ Ativado Agente: ${bold(agentName)}`));
209
241
  resetMessages();
210
- } else { console.log(chalk.red(`\n✖ Agente "${agentName}" não encontrado.\n`)); }
242
+ } else { console.log(chalk.red(`\n✖ Agente não encontrado.\n`)); }
211
243
  continue;
212
244
  }
213
245
 
214
- if (cmd === '/clear') { resetMessages(); console.clear(); console.log(lavender('✓ Histórico limpo, contexto preservado.\n')); continue; }
246
+ if (cmd === '/clear') { resetMessages(); console.clear(); continue; }
215
247
 
216
248
  if (cmd === '/help') {
217
- console.log(gray(`
218
- Comandos:
219
- /chat /plan /edit [auto/manual] → Mudar modo
220
- /use [agente] | /use normal → Usar Agentes
221
- /switch [nome] | /model [nome] → Mudar IA/Modelo
222
- /config | /init | @arquivo → Configurações
223
- `));
249
+ console.log(gray(`\nComandos:\n /chat | /plan | /edit [auto/manual] | /init\n /switch [nome] | /model [nome] | /use [agente]\n /config | /clear | @arquivo\n`));
224
250
  continue;
225
251
  }
226
252
 
@@ -228,6 +254,7 @@ Comandos:
228
254
 
229
255
  if (rawInput === '') continue;
230
256
 
257
+ // Enviar para a IA
231
258
  const controller = new AbortController();
232
259
  const localInterruptHandler = () => controller.abort();
233
260
  process.removeListener('SIGINT', globalSigIntHandler);
@@ -246,18 +273,21 @@ Comandos:
246
273
  let responseText = await provider.sendMessage(messages, { signal: controller.signal });
247
274
  spinner.stop();
248
275
 
249
- // EXECUTA LIMPEZA BRUTA
250
276
  const cleanedText = cleanAIResponse(responseText);
251
-
252
277
  messages.push({ role: 'assistant', content: responseText });
278
+
253
279
  console.log('\n' + lavender('bimmo ') + getModeStyle());
254
280
  console.log(lavender('─'.repeat(50)));
255
281
  console.log(marked(cleanedText));
256
282
  console.log(gray('─'.repeat(50)) + '\n');
257
283
  } catch (err) {
258
284
  spinner.stop();
259
- if (controller.signal.aborted || err.name === 'AbortError') { console.log(yellow('\n\n⚠️ Interrompido.\n')); messages.pop(); }
260
- else { console.error(chalk.red('\n✖ Erro:') + ' ' + err.message + '\n'); }
285
+ if (controller.signal.aborted || err.name === 'AbortError') {
286
+ console.log(yellow('\n⚠️ Interrompido.\n'));
287
+ messages.pop();
288
+ } else {
289
+ console.error(chalk.red('\n✖ Erro:') + ' ' + err.message + '\n');
290
+ }
261
291
  } finally {
262
292
  process.removeListener('SIGINT', localInterruptHandler);
263
293
  process.on('SIGINT', globalSigIntHandler);