bimmo-cli 2.2.1 → 2.2.5
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/package.json +4 -18
- package/src/interface.js +143 -193
package/package.json
CHANGED
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bimmo-cli",
|
|
3
|
-
"version": "2.2.
|
|
4
|
-
"description": "🌿 Plataforma de IA universal com
|
|
3
|
+
"version": "2.2.5",
|
|
4
|
+
"description": "🌿 Plataforma de IA universal profissional com Agentes e Swarms. Suporte a Autocomplete, Diffs e Contexto Inteligente.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bimmo": "bin/bimmo"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"keywords": [
|
|
10
|
-
"ai",
|
|
11
|
-
"cli",
|
|
12
|
-
"openai",
|
|
13
|
-
"anthropic",
|
|
14
|
-
"gemini",
|
|
15
|
-
"grok",
|
|
16
|
-
"deepseek",
|
|
17
|
-
"openrouter",
|
|
18
|
-
"zai",
|
|
19
|
-
"agent",
|
|
20
|
-
"swarm",
|
|
21
|
-
"autocomplete",
|
|
22
|
-
"terminal"
|
|
10
|
+
"ai", "cli", "openai", "anthropic", "gemini", "grok", "deepseek", "openrouter", "zai", "agent", "swarm", "terminal"
|
|
23
11
|
],
|
|
24
12
|
"author": "Judah",
|
|
25
13
|
"license": "MIT",
|
|
@@ -41,9 +29,7 @@
|
|
|
41
29
|
"conf": "^13.0.0",
|
|
42
30
|
"diff": "^7.0.0",
|
|
43
31
|
"figlet": "^1.7.0",
|
|
44
|
-
"
|
|
45
|
-
"inquirer": "^10.1.0",
|
|
46
|
-
"inquirer-autocomplete-prompt": "^3.0.1",
|
|
32
|
+
"inquirer": "^9.3.8",
|
|
47
33
|
"marked": "^14.0.0",
|
|
48
34
|
"marked-terminal": "^7.0.0",
|
|
49
35
|
"mime-types": "^2.1.35",
|
package/src/interface.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import figlet from 'figlet';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
|
-
import autocompletePrompt from 'inquirer-autocomplete-prompt';
|
|
5
|
-
import fuzzy from 'fuzzy';
|
|
6
4
|
import { marked } from 'marked';
|
|
7
5
|
import TerminalRenderer from 'marked-terminal';
|
|
8
6
|
import ora from 'ora';
|
|
@@ -18,18 +16,20 @@ import { getProjectContext } from './project-context.js';
|
|
|
18
16
|
import { SwarmOrchestrator } from './orchestrator.js';
|
|
19
17
|
import { editState } from './agent.js';
|
|
20
18
|
|
|
21
|
-
// Registrar plugin de autocomplete
|
|
22
|
-
inquirer.registerPrompt('autocomplete', autocompletePrompt);
|
|
23
|
-
|
|
24
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
25
20
|
const __dirname = path.dirname(__filename);
|
|
26
21
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
27
22
|
const version = pkg.version;
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
// CONFIGURAÇÃO DO RENDERIZADOR (CORREÇÃO DEFINITIVA DO <P>)
|
|
25
|
+
const terminalRenderer = new TerminalRenderer({
|
|
30
26
|
heading: chalk.hex('#c084fc').bold,
|
|
31
27
|
code: chalk.hex('#00ff9d'),
|
|
32
|
-
|
|
28
|
+
strong: chalk.bold,
|
|
29
|
+
em: chalk.italic,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
marked.setOptions({ renderer: terminalRenderer });
|
|
33
33
|
|
|
34
34
|
const green = chalk.hex('#00ff9d');
|
|
35
35
|
const lavender = chalk.hex('#c084fc');
|
|
@@ -42,84 +42,39 @@ let activePersona = null;
|
|
|
42
42
|
let exitCounter = 0;
|
|
43
43
|
let exitTimer = null;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function processInput(input) {
|
|
68
|
-
const parts = input.split(' ');
|
|
69
|
-
const processedContent = [];
|
|
70
|
-
|
|
71
|
-
for (const part of parts) {
|
|
72
|
-
if (part.startsWith('@')) {
|
|
73
|
-
const filePath = part.slice(1);
|
|
74
|
-
try {
|
|
75
|
-
if (fs.existsSync(filePath)) {
|
|
76
|
-
const stats = fs.statSync(filePath);
|
|
77
|
-
if (stats.isFile()) {
|
|
78
|
-
const mimeType = mime.lookup(filePath) || 'application/octet-stream';
|
|
79
|
-
if (mimeType.startsWith('image/')) {
|
|
80
|
-
const base64Image = fs.readFileSync(filePath, { encoding: 'base64' });
|
|
81
|
-
processedContent.push({ type: 'image', mimeType, data: base64Image, fileName: path.basename(filePath) });
|
|
82
|
-
} else {
|
|
83
|
-
const textContent = fs.readFileSync(filePath, 'utf-8');
|
|
84
|
-
processedContent.push({ type: 'text', text: `\n--- Arquivo: ${path.basename(filePath)} ---\n${textContent}\n--- Fim do arquivo ---\n` });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
processedContent.push({ type: 'text', text: part });
|
|
89
|
-
}
|
|
90
|
-
} catch (err) {
|
|
91
|
-
processedContent.push({ type: 'text', text: part });
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
processedContent.push({ type: 'text', text: part });
|
|
95
|
-
}
|
|
45
|
+
const i18n = {
|
|
46
|
+
'pt-BR': {
|
|
47
|
+
welcome: 'Olá! Estou pronto. No que posso ajudar?',
|
|
48
|
+
thinking: 'bimmo pensando...',
|
|
49
|
+
interrupted: 'Operação interrompida.',
|
|
50
|
+
exitHint: '(Pressione Ctrl+C novamente para sair)',
|
|
51
|
+
switchOk: 'Perfil ativado!',
|
|
52
|
+
agentOk: 'Agente ativado:',
|
|
53
|
+
modeEdit: 'Modo EDIT ativado.',
|
|
54
|
+
help: '\nComandos:\n /chat | /plan | /edit | /init\n /switch [nome] | /model [nome]\n /use [agente] | /use normal\n /config | /clear | @arquivo\n'
|
|
55
|
+
},
|
|
56
|
+
'en-US': {
|
|
57
|
+
welcome: 'Hello! I am ready. How can I help you?',
|
|
58
|
+
thinking: 'bimmo thinking...',
|
|
59
|
+
interrupted: 'Operation interrupted.',
|
|
60
|
+
exitHint: '(Press Ctrl+C again to exit)',
|
|
61
|
+
switchOk: 'Profile activated!',
|
|
62
|
+
agentOk: 'Agent activated:',
|
|
63
|
+
modeEdit: 'EDIT mode activated.',
|
|
64
|
+
help: '\nCommands:\n /chat | /plan | /edit | /init\n /switch [name] | /model [name]\n /use [agent] | /use normal\n /config | /clear | @file\n'
|
|
96
65
|
}
|
|
66
|
+
};
|
|
97
67
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
finalContent.push(item);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (currentText) finalContent.push({ type: 'text', text: currentText });
|
|
112
|
-
return finalContent;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function getModeStyle() {
|
|
116
|
-
const personaLabel = activePersona ? `[${activePersona.toUpperCase()}]` : '';
|
|
117
|
-
switch (currentMode) {
|
|
118
|
-
case 'plan': return yellow.bold(`${personaLabel}[PLAN] `);
|
|
119
|
-
case 'edit':
|
|
120
|
-
const editSubMode = editState.autoAccept ? '(AUTO)' : '(MANUAL)';
|
|
121
|
-
return chalk.red.bold(`${personaLabel}[EDIT${editSubMode}] `);
|
|
122
|
-
default: return lavender.bold(`${personaLabel}[CHAT] `);
|
|
68
|
+
function getFilesForCompletion(partialPath) {
|
|
69
|
+
try {
|
|
70
|
+
const dir = path.dirname(partialPath.startsWith('@') ? partialPath.slice(1) : partialPath) || '.';
|
|
71
|
+
const base = path.basename(partialPath.startsWith('@') ? partialPath.slice(1) : partialPath);
|
|
72
|
+
const files = fs.readdirSync(path.resolve(process.cwd(), dir));
|
|
73
|
+
return files
|
|
74
|
+
.filter(f => f.startsWith(base) && !f.startsWith('.') && f !== 'node_modules')
|
|
75
|
+
.map(f => path.join(dir, f));
|
|
76
|
+
} catch (e) {
|
|
77
|
+
return [];
|
|
123
78
|
}
|
|
124
79
|
}
|
|
125
80
|
|
|
@@ -127,22 +82,19 @@ function cleanAIResponse(text) {
|
|
|
127
82
|
if (!text) return "";
|
|
128
83
|
return text
|
|
129
84
|
.replace(/<br\s*\/?>/gi, '\n')
|
|
85
|
+
.replace(/<p>/gi, '')
|
|
130
86
|
.replace(/<\/p>/gi, '\n\n')
|
|
131
|
-
.replace(/<\/div>/gi, '\n')
|
|
132
87
|
.replace(/<[^>]*>?/gm, '')
|
|
133
|
-
.replace(/ /g, ' ')
|
|
134
|
-
.replace(/</g, '<')
|
|
135
|
-
.replace(/>/g, '>')
|
|
136
|
-
.replace(/&/g, '&')
|
|
137
88
|
.trim();
|
|
138
89
|
}
|
|
139
90
|
|
|
140
91
|
export async function startInteractive() {
|
|
141
92
|
let config = getConfig();
|
|
93
|
+
const lang = config.language || 'pt-BR';
|
|
94
|
+
const t = i18n[lang] || i18n['pt-BR'];
|
|
142
95
|
|
|
143
96
|
if (!config.provider || !config.apiKey) {
|
|
144
97
|
console.log(lavender(figlet.textSync('bimmo')));
|
|
145
|
-
console.log(gray('\nBem-vindo! Vamos configurar seus perfis de IA.\n'));
|
|
146
98
|
await configure();
|
|
147
99
|
return startInteractive();
|
|
148
100
|
}
|
|
@@ -153,11 +105,10 @@ export async function startInteractive() {
|
|
|
153
105
|
|
|
154
106
|
const resetMessages = () => {
|
|
155
107
|
messages = [];
|
|
156
|
-
|
|
157
|
-
messages.push({ role: 'system', content: projectContext });
|
|
108
|
+
messages.push({ role: 'system', content: getProjectContext() });
|
|
158
109
|
if (activePersona) {
|
|
159
110
|
const agent = (config.agents || {})[activePersona];
|
|
160
|
-
if (agent) messages.push({ role: 'system', content: `
|
|
111
|
+
if (agent) messages.push({ role: 'system', content: `Persona: ${agent.name}. Task: ${agent.role}` });
|
|
161
112
|
}
|
|
162
113
|
};
|
|
163
114
|
|
|
@@ -168,157 +119,156 @@ export async function startInteractive() {
|
|
|
168
119
|
console.log(lavender(` v${version} `.padStart(60, '─')));
|
|
169
120
|
console.log(green(` Perfil: ${bold(config.activeProfile || 'Padrão')} • IA: ${bold(config.provider.toUpperCase())}`));
|
|
170
121
|
console.log(green(` Modelo: ${bold(config.model)}`));
|
|
171
|
-
console.log(gray(` 📁 ${process.cwd()}`));
|
|
172
|
-
console.log(gray(' /chat | /plan | /edit | /swarm | /use [agente] | /help'));
|
|
173
122
|
console.log(lavender('─'.repeat(60)) + '\n');
|
|
174
123
|
|
|
175
|
-
console.log(lavender(
|
|
124
|
+
console.log(lavender(`👋 ${t.welcome}\n`));
|
|
125
|
+
|
|
126
|
+
const rl = readline.createInterface({
|
|
127
|
+
input: process.stdin,
|
|
128
|
+
output: process.stdout,
|
|
129
|
+
terminal: true,
|
|
130
|
+
historySize: 100,
|
|
131
|
+
completer: (line) => {
|
|
132
|
+
const words = line.split(' ');
|
|
133
|
+
const lastWord = words[words.length - 1];
|
|
134
|
+
if (lastWord.startsWith('@')) {
|
|
135
|
+
const hits = getFilesForCompletion(lastWord);
|
|
136
|
+
return [hits.map(h => `@${h}`), lastWord];
|
|
137
|
+
}
|
|
138
|
+
return [[], line];
|
|
139
|
+
}
|
|
140
|
+
});
|
|
176
141
|
|
|
177
|
-
|
|
142
|
+
// Handler de saída
|
|
143
|
+
rl.on('SIGINT', () => {
|
|
178
144
|
exitCounter++;
|
|
179
145
|
if (exitCounter === 1) {
|
|
180
|
-
|
|
146
|
+
console.log(`\n${gray(t.exitHint)}`);
|
|
181
147
|
if (exitTimer) clearTimeout(exitTimer);
|
|
182
148
|
exitTimer = setTimeout(() => { exitCounter = 0; }, 2000);
|
|
149
|
+
displayPrompt();
|
|
183
150
|
} else {
|
|
184
|
-
|
|
151
|
+
console.log(lavender('\n👋 BIMMO encerrando sessão.\n'));
|
|
185
152
|
process.exit(0);
|
|
186
153
|
}
|
|
187
|
-
};
|
|
154
|
+
});
|
|
188
155
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
while (true) {
|
|
194
|
-
const modeIndicator = getModeStyle();
|
|
195
|
-
let input;
|
|
156
|
+
const displayPrompt = () => {
|
|
157
|
+
const personaLabel = activePersona ? `[${activePersona.toUpperCase()}]` : '';
|
|
158
|
+
let modeLabel = `[${currentMode.toUpperCase()}]`;
|
|
159
|
+
if (currentMode === 'edit') modeLabel = editState.autoAccept ? '[EDIT(AUTO)]' : '[EDIT(MANUAL)]';
|
|
196
160
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
type: 'autocomplete',
|
|
202
|
-
name: 'input',
|
|
203
|
-
message: modeIndicator + green('>'),
|
|
204
|
-
prefix: '',
|
|
205
|
-
source: async (answersSoFar, inputSearch) => {
|
|
206
|
-
const currentInput = inputSearch || '';
|
|
207
|
-
|
|
208
|
-
// Se o usuário digitar @, oferecemos arquivos
|
|
209
|
-
if (currentInput.includes('@')) {
|
|
210
|
-
const lastWord = currentInput.split(' ').pop();
|
|
211
|
-
if (lastWord.startsWith('@')) {
|
|
212
|
-
const searchPath = lastWord.slice(1);
|
|
213
|
-
const files = getFiles(process.cwd(), searchPath);
|
|
214
|
-
return files.map(f => ({
|
|
215
|
-
name: `@${f} ${fs.statSync(f).isDirectory() ? '(DIR)' : '(FILE)'}`,
|
|
216
|
-
value: currentInput.substring(0, currentInput.lastIndexOf('@')) + '@' + f
|
|
217
|
-
}));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Caso contrário, apenas retorna o que ele está digitando como única opção
|
|
222
|
-
// para não atrapalhar o chat normal
|
|
223
|
-
return [currentInput];
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
]);
|
|
227
|
-
input = answers.input;
|
|
228
|
-
} catch (e) { continue; }
|
|
229
|
-
|
|
230
|
-
// Diretório constante
|
|
231
|
-
console.log(gray(` 📁 ${process.cwd()}`));
|
|
161
|
+
console.log(`\n${gray(`📁 ${process.cwd()}`)}`);
|
|
162
|
+
rl.setPrompt(lavender.bold(personaLabel) + (currentMode === 'edit' ? chalk.red.bold(modeLabel) : lavender.bold(modeLabel)) + green(' > '));
|
|
163
|
+
rl.prompt();
|
|
164
|
+
};
|
|
232
165
|
|
|
166
|
+
displayPrompt();
|
|
167
|
+
|
|
168
|
+
rl.on('line', async (input) => {
|
|
233
169
|
const rawInput = input.trim();
|
|
234
170
|
const cmd = rawInput.toLowerCase();
|
|
235
171
|
|
|
236
|
-
if (
|
|
237
|
-
if (cmd === '/chat') { currentMode = 'chat'; console.log(lavender('✓ Modo CHAT.\n')); continue; }
|
|
238
|
-
if (cmd === '/plan') { currentMode = 'plan'; console.log(yellow('✓ Modo PLAN.\n')); continue; }
|
|
239
|
-
if (cmd === '/edit') { currentMode = 'edit'; console.log(chalk.red(`⚠️ Modo EDIT ativado.\n`)); continue; }
|
|
240
|
-
if (cmd === '/edit auto') { currentMode = 'edit'; editState.autoAccept = true; console.log(chalk.red('⚠️ Modo EDIT (AUTO) ativado.\n')); continue; }
|
|
241
|
-
if (cmd === '/edit manual') { currentMode = 'edit'; editState.autoAccept = false; console.log(chalk.red('⚠️ Modo EDIT (MANUAL) ativado.\n')); continue; }
|
|
172
|
+
if (rawInput === '') { displayPrompt(); return; }
|
|
242
173
|
|
|
174
|
+
// COMANDOS INTERNOS
|
|
175
|
+
if (cmd === '/exit' || cmd === 'sair') process.exit(0);
|
|
176
|
+
if (cmd === '/chat') { currentMode = 'chat'; displayPrompt(); return; }
|
|
177
|
+
if (cmd === '/plan') { currentMode = 'plan'; displayPrompt(); return; }
|
|
178
|
+
if (cmd === '/edit' || cmd === '/edit manual') { currentMode = 'edit'; editState.autoAccept = false; displayPrompt(); return; }
|
|
179
|
+
if (cmd === '/edit auto') { currentMode = 'edit'; editState.autoAccept = true; displayPrompt(); return; }
|
|
180
|
+
|
|
181
|
+
if (cmd === '/clear') { resetMessages(); console.clear(); displayPrompt(); return; }
|
|
182
|
+
if (cmd === '/help') { console.log(gray(t.help)); displayPrompt(); return; }
|
|
183
|
+
|
|
243
184
|
if (cmd === '/init') {
|
|
244
185
|
const bimmoRcPath = path.join(process.cwd(), '.bimmorc.json');
|
|
245
|
-
|
|
246
|
-
const { overwrite } = await inquirer.prompt([{ type: 'confirm', name: 'overwrite', message: 'O arquivo .bimmorc.json já existe. Sobrescrever?', default: false }]);
|
|
247
|
-
if (!overwrite) continue;
|
|
248
|
-
}
|
|
249
|
-
const initialConfig = { projectName: path.basename(process.cwd()), rules: ["Siga as convenções."], ignorePatterns: ["node_modules", ".git"] };
|
|
186
|
+
const initialConfig = { projectName: path.basename(process.cwd()), rules: ["Clean code"], ignorePatterns: ["node_modules"] };
|
|
250
187
|
fs.writeFileSync(bimmoRcPath, JSON.stringify(initialConfig, null, 2));
|
|
251
|
-
console.log(green(`\n✅ .bimmorc.json criado
|
|
188
|
+
console.log(green(`\n✅ .bimmorc.json criado.`));
|
|
252
189
|
resetMessages();
|
|
253
|
-
|
|
190
|
+
displayPrompt();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (cmd === '/config') {
|
|
195
|
+
rl.pause();
|
|
196
|
+
await configure();
|
|
197
|
+
config = getConfig();
|
|
198
|
+
provider = createProvider(config);
|
|
199
|
+
rl.resume();
|
|
200
|
+
displayPrompt();
|
|
201
|
+
return;
|
|
254
202
|
}
|
|
255
203
|
|
|
256
204
|
if (cmd.startsWith('/switch ')) {
|
|
257
|
-
const
|
|
258
|
-
if (
|
|
205
|
+
const pName = rawInput.split(' ')[1];
|
|
206
|
+
if (switchProfile(pName)) {
|
|
259
207
|
config = getConfig(); provider = createProvider(config);
|
|
260
|
-
console.log(green(`\n✓
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
console.log(chalk.red(`\n✖ Perfil não encontrado.\n`)); continue;
|
|
208
|
+
console.log(green(`\n✓ ${t.switchOk}`));
|
|
209
|
+
} else { console.log(chalk.red(`\n✖ Perfil não encontrado.`)); }
|
|
210
|
+
displayPrompt(); return;
|
|
264
211
|
}
|
|
265
212
|
|
|
266
213
|
if (cmd.startsWith('/use ')) {
|
|
267
|
-
const
|
|
214
|
+
const aName = rawInput.split(' ')[1];
|
|
215
|
+
if (aName === 'normal') { activePersona = null; resetMessages(); displayPrompt(); return; }
|
|
268
216
|
const agents = config.agents || {};
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const agent = agents[agentName];
|
|
217
|
+
if (agents[aName]) {
|
|
218
|
+
activePersona = aName;
|
|
219
|
+
const agent = agents[aName];
|
|
273
220
|
if (switchProfile(agent.profile)) { config = getConfig(); provider = createProvider(config); }
|
|
274
221
|
currentMode = agent.mode || 'chat';
|
|
275
|
-
console.log(green(`\n✓
|
|
222
|
+
console.log(green(`\n✓ ${t.agentOk} ${bold(aName)}`));
|
|
276
223
|
resetMessages();
|
|
277
|
-
} else { console.log(chalk.red(`\n✖ Agente não encontrado
|
|
278
|
-
|
|
224
|
+
} else { console.log(chalk.red(`\n✖ Agente não encontrado.`)); }
|
|
225
|
+
displayPrompt(); return;
|
|
279
226
|
}
|
|
280
227
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (cmd === '/help') {
|
|
284
|
-
console.log(gray(`\nComandos:\n /chat | /plan | /edit [auto/manual] | /init\n /switch [nome] | /model [nome] | /use [agente]\n /config | /clear | @arquivo\n`));
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (cmd === '/config') { await configure(); config = getConfig(); provider = createProvider(config); continue; }
|
|
289
|
-
|
|
290
|
-
if (rawInput === '') continue;
|
|
291
|
-
|
|
228
|
+
// PROCESSAMENTO IA
|
|
292
229
|
const controller = new AbortController();
|
|
293
|
-
const
|
|
294
|
-
process.
|
|
295
|
-
process.on('SIGINT', localInterruptHandler);
|
|
230
|
+
const abortHandler = () => controller.abort();
|
|
231
|
+
process.on('SIGINT', abortHandler);
|
|
296
232
|
|
|
297
233
|
let modeInstr = "";
|
|
298
234
|
if (currentMode === 'plan') modeInstr = "\n[MODO PLAN] Apenas analise.";
|
|
299
235
|
else if (currentMode === 'edit') modeInstr = `\n[MODO EDIT] Auto-Accept: ${editState.autoAccept ? 'ON' : 'OFF'}`;
|
|
300
236
|
|
|
301
|
-
|
|
302
|
-
|
|
237
|
+
// Processar anexos @
|
|
238
|
+
const processedContent = [];
|
|
239
|
+
const words = rawInput.split(' ');
|
|
240
|
+
for (const word of words) {
|
|
241
|
+
if (word.startsWith('@')) {
|
|
242
|
+
const filePath = word.slice(1);
|
|
243
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
244
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
245
|
+
processedContent.push({ type: 'text', text: `\n[ARQUIVO: ${filePath}]\n${content}\n` });
|
|
246
|
+
} else { processedContent.push({ type: 'text', text: word }); }
|
|
247
|
+
} else { processedContent.push({ type: 'text', text: word }); }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
messages.push({ role: 'user', content: [...processedContent, { type: 'text', text: modeInstr }] });
|
|
303
251
|
|
|
304
|
-
const spinner = ora({ text: lavender(
|
|
252
|
+
const spinner = ora({ text: lavender(`${t.thinking} (Ctrl+C para parar)`), color: currentMode === 'edit' ? 'red' : 'magenta' }).start();
|
|
305
253
|
|
|
306
254
|
try {
|
|
307
255
|
let responseText = await provider.sendMessage(messages, { signal: controller.signal });
|
|
308
256
|
spinner.stop();
|
|
257
|
+
|
|
309
258
|
const cleanedText = cleanAIResponse(responseText);
|
|
310
259
|
messages.push({ role: 'assistant', content: responseText });
|
|
311
|
-
|
|
260
|
+
|
|
261
|
+
console.log(`\n${lavender('bimmo ')}${currentMode.toUpperCase()}`);
|
|
312
262
|
console.log(lavender('─'.repeat(50)));
|
|
313
|
-
console.log(marked(cleanedText));
|
|
314
|
-
console.log(gray('─'.repeat(50))
|
|
263
|
+
console.log(marked.parse(cleanedText)); // Usamos parse para garantir o renderer terminal
|
|
264
|
+
console.log(gray('─'.repeat(50)));
|
|
315
265
|
} catch (err) {
|
|
316
266
|
spinner.stop();
|
|
317
|
-
if (controller.signal.aborted
|
|
318
|
-
else { console.error(chalk.red(
|
|
267
|
+
if (controller.signal.aborted) { console.log(yellow(`\n⚠️ ${t.interrupted}`)); messages.pop(); }
|
|
268
|
+
else { console.error(chalk.red(`\n✖ Erro: ${err.message}`)); }
|
|
319
269
|
} finally {
|
|
320
|
-
process.removeListener('SIGINT',
|
|
321
|
-
|
|
270
|
+
process.removeListener('SIGINT', abortHandler);
|
|
271
|
+
displayPrompt();
|
|
322
272
|
}
|
|
323
|
-
}
|
|
273
|
+
});
|
|
324
274
|
}
|