bimmo-cli 2.1.4 → 2.2.1
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 +5 -4
- package/src/config.js +17 -0
- package/src/interface.js +70 -42
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bimmo-cli",
|
|
3
|
-
"version": "2.1
|
|
4
|
-
"description": "🌿 Plataforma de IA universal com Modo Normal, Agentes e Swarms. Suporte a
|
|
3
|
+
"version": "2.2.1",
|
|
4
|
+
"description": "🌿 Plataforma de IA universal com Modo Normal, Agentes e Swarms. Suporte a Autocomplete de arquivos, Diffs coloridos e Contexto Inteligente.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bimmo": "bin/bimmo"
|
|
7
7
|
},
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
"zai",
|
|
19
19
|
"agent",
|
|
20
20
|
"swarm",
|
|
21
|
-
"
|
|
22
|
-
"multimodal",
|
|
21
|
+
"autocomplete",
|
|
23
22
|
"terminal"
|
|
24
23
|
],
|
|
25
24
|
"author": "Judah",
|
|
@@ -42,7 +41,9 @@
|
|
|
42
41
|
"conf": "^13.0.0",
|
|
43
42
|
"diff": "^7.0.0",
|
|
44
43
|
"figlet": "^1.7.0",
|
|
44
|
+
"fuzzy": "^0.1.3",
|
|
45
45
|
"inquirer": "^10.1.0",
|
|
46
|
+
"inquirer-autocomplete-prompt": "^3.0.1",
|
|
46
47
|
"marked": "^14.0.0",
|
|
47
48
|
"marked-terminal": "^7.0.0",
|
|
48
49
|
"mime-types": "^2.1.35",
|
package/src/config.js
CHANGED
|
@@ -30,6 +30,7 @@ export async function configure() {
|
|
|
30
30
|
{ name: 'Criar novo perfil de IA', value: 'create' },
|
|
31
31
|
{ name: 'Selecionar perfil ativo', value: 'select' },
|
|
32
32
|
{ name: 'Gerenciar Agentes Especialistas', value: 'agents' },
|
|
33
|
+
{ name: 'Configurar Idioma', value: 'language' },
|
|
33
34
|
{ name: 'Configurar chave Tavily', value: 'tavily' },
|
|
34
35
|
{ name: 'Sair', value: 'exit' }
|
|
35
36
|
]
|
|
@@ -38,6 +39,22 @@ export async function configure() {
|
|
|
38
39
|
|
|
39
40
|
if (action === 'exit') return;
|
|
40
41
|
|
|
42
|
+
if (action === 'language') {
|
|
43
|
+
const { lang } = await inquirer.prompt([{
|
|
44
|
+
type: 'list',
|
|
45
|
+
name: 'lang',
|
|
46
|
+
message: 'Escolha o idioma do sistema:',
|
|
47
|
+
choices: [
|
|
48
|
+
{ name: 'Português (Brasil)', value: 'pt-BR' },
|
|
49
|
+
{ name: 'English', value: 'en-US' }
|
|
50
|
+
],
|
|
51
|
+
default: config.get('language') || 'pt-BR'
|
|
52
|
+
}]);
|
|
53
|
+
config.set('language', lang);
|
|
54
|
+
console.log(chalk.green(`✓ Idioma definido para: ${lang}`));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
if (action === 'agents') return configureAgents();
|
|
42
59
|
|
|
43
60
|
if (action === 'tavily') {
|
package/src/interface.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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';
|
|
4
6
|
import { marked } from 'marked';
|
|
5
7
|
import TerminalRenderer from 'marked-terminal';
|
|
6
8
|
import ora from 'ora';
|
|
@@ -16,23 +18,19 @@ import { getProjectContext } from './project-context.js';
|
|
|
16
18
|
import { SwarmOrchestrator } from './orchestrator.js';
|
|
17
19
|
import { editState } from './agent.js';
|
|
18
20
|
|
|
21
|
+
// Registrar plugin de autocomplete
|
|
22
|
+
inquirer.registerPrompt('autocomplete', autocompletePrompt);
|
|
23
|
+
|
|
19
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
25
|
const __dirname = path.dirname(__filename);
|
|
21
26
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
22
27
|
const version = pkg.version;
|
|
23
28
|
|
|
24
|
-
// Configuração do renderizador - Forçamos o escape de HTML para garantir que tags não apareçam
|
|
25
29
|
marked.use(new TerminalRenderer({
|
|
26
30
|
heading: chalk.hex('#c084fc').bold,
|
|
27
31
|
code: chalk.hex('#00ff9d'),
|
|
28
32
|
}));
|
|
29
33
|
|
|
30
|
-
marked.setOptions({
|
|
31
|
-
sanitize: true, // Depreciado mas ajuda em versões antigas
|
|
32
|
-
headerIds: false,
|
|
33
|
-
mangle: false
|
|
34
|
-
});
|
|
35
|
-
|
|
36
34
|
const green = chalk.hex('#00ff9d');
|
|
37
35
|
const lavender = chalk.hex('#c084fc');
|
|
38
36
|
const gray = chalk.gray;
|
|
@@ -44,6 +42,28 @@ let activePersona = null;
|
|
|
44
42
|
let exitCounter = 0;
|
|
45
43
|
let exitTimer = null;
|
|
46
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Procura arquivos e diretórios recursivamente para o autocomplete do @
|
|
47
|
+
*/
|
|
48
|
+
function getFiles(dir, filter = '') {
|
|
49
|
+
const results = [];
|
|
50
|
+
try {
|
|
51
|
+
const list = fs.readdirSync(dir);
|
|
52
|
+
for (const file of list) {
|
|
53
|
+
if (file === 'node_modules' || file === '.git') continue;
|
|
54
|
+
const fullPath = path.join(dir, file);
|
|
55
|
+
const relPath = path.relative(process.cwd(), fullPath);
|
|
56
|
+
if (relPath.toLowerCase().includes(filter.toLowerCase())) {
|
|
57
|
+
results.push(relPath);
|
|
58
|
+
}
|
|
59
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
60
|
+
// Limitamos profundidade para performance se necessário
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {}
|
|
64
|
+
return results;
|
|
65
|
+
}
|
|
66
|
+
|
|
47
67
|
async function processInput(input) {
|
|
48
68
|
const parts = input.split(' ');
|
|
49
69
|
const processedContent = [];
|
|
@@ -103,28 +123,18 @@ function getModeStyle() {
|
|
|
103
123
|
}
|
|
104
124
|
}
|
|
105
125
|
|
|
106
|
-
/**
|
|
107
|
-
* LIMPEZA ABSOLUTA DE HTML
|
|
108
|
-
* Remove tags HTML antes da renderização Markdown.
|
|
109
|
-
*/
|
|
110
126
|
function cleanAIResponse(text) {
|
|
111
127
|
if (!text) return "";
|
|
112
|
-
|
|
113
|
-
let cleaned = text
|
|
128
|
+
return text
|
|
114
129
|
.replace(/<br\s*\/?>/gi, '\n')
|
|
115
130
|
.replace(/<\/p>/gi, '\n\n')
|
|
116
131
|
.replace(/<\/div>/gi, '\n')
|
|
117
|
-
.replace(/<
|
|
118
|
-
.replace(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Decodifica entidades comuns
|
|
124
|
-
const entities = {
|
|
125
|
-
' ': ' ', '<': '<', '>': '>', '&': '&', '"': '"', ''': "'"
|
|
126
|
-
};
|
|
127
|
-
return cleaned.replace(/&[a-z0-9#]+;/gi, (match) => entities[match] || match).trim();
|
|
132
|
+
.replace(/<[^>]*>?/gm, '')
|
|
133
|
+
.replace(/ /g, ' ')
|
|
134
|
+
.replace(/</g, '<')
|
|
135
|
+
.replace(/>/g, '>')
|
|
136
|
+
.replace(/&/g, '&')
|
|
137
|
+
.trim();
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
export async function startInteractive() {
|
|
@@ -158,6 +168,7 @@ export async function startInteractive() {
|
|
|
158
168
|
console.log(lavender(` v${version} `.padStart(60, '─')));
|
|
159
169
|
console.log(green(` Perfil: ${bold(config.activeProfile || 'Padrão')} • IA: ${bold(config.provider.toUpperCase())}`));
|
|
160
170
|
console.log(green(` Modelo: ${bold(config.model)}`));
|
|
171
|
+
console.log(gray(` 📁 ${process.cwd()}`));
|
|
161
172
|
console.log(gray(' /chat | /plan | /edit | /swarm | /use [agente] | /help'));
|
|
162
173
|
console.log(lavender('─'.repeat(60)) + '\n');
|
|
163
174
|
|
|
@@ -182,21 +193,49 @@ export async function startInteractive() {
|
|
|
182
193
|
while (true) {
|
|
183
194
|
const modeIndicator = getModeStyle();
|
|
184
195
|
let input;
|
|
196
|
+
|
|
185
197
|
try {
|
|
186
|
-
|
|
198
|
+
// Usamos autocomplete para permitir navegação de arquivos com @
|
|
199
|
+
const answers = await inquirer.prompt([
|
|
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
|
+
]);
|
|
187
227
|
input = answers.input;
|
|
188
228
|
} catch (e) { continue; }
|
|
189
229
|
|
|
230
|
+
// Diretório constante
|
|
190
231
|
console.log(gray(` 📁 ${process.cwd()}`));
|
|
232
|
+
|
|
191
233
|
const rawInput = input.trim();
|
|
192
234
|
const cmd = rawInput.toLowerCase();
|
|
193
235
|
|
|
194
|
-
// COMANDOS CLI (Tratados antes de enviar para IA)
|
|
195
236
|
if (cmd === '/exit' || cmd === 'exit' || cmd === 'sair') { process.exit(0); }
|
|
196
|
-
|
|
197
237
|
if (cmd === '/chat') { currentMode = 'chat'; console.log(lavender('✓ Modo CHAT.\n')); continue; }
|
|
198
238
|
if (cmd === '/plan') { currentMode = 'plan'; console.log(yellow('✓ Modo PLAN.\n')); continue; }
|
|
199
|
-
|
|
200
239
|
if (cmd === '/edit') { currentMode = 'edit'; console.log(chalk.red(`⚠️ Modo EDIT ativado.\n`)); continue; }
|
|
201
240
|
if (cmd === '/edit auto') { currentMode = 'edit'; editState.autoAccept = true; console.log(chalk.red('⚠️ Modo EDIT (AUTO) ativado.\n')); continue; }
|
|
202
241
|
if (cmd === '/edit manual') { currentMode = 'edit'; editState.autoAccept = false; console.log(chalk.red('⚠️ Modo EDIT (MANUAL) ativado.\n')); continue; }
|
|
@@ -207,11 +246,7 @@ export async function startInteractive() {
|
|
|
207
246
|
const { overwrite } = await inquirer.prompt([{ type: 'confirm', name: 'overwrite', message: 'O arquivo .bimmorc.json já existe. Sobrescrever?', default: false }]);
|
|
208
247
|
if (!overwrite) continue;
|
|
209
248
|
}
|
|
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
|
-
};
|
|
249
|
+
const initialConfig = { projectName: path.basename(process.cwd()), rules: ["Siga as convenções."], ignorePatterns: ["node_modules", ".git"] };
|
|
215
250
|
fs.writeFileSync(bimmoRcPath, JSON.stringify(initialConfig, null, 2));
|
|
216
251
|
console.log(green(`\n✅ .bimmorc.json criado com sucesso.\n`));
|
|
217
252
|
resetMessages();
|
|
@@ -254,7 +289,6 @@ export async function startInteractive() {
|
|
|
254
289
|
|
|
255
290
|
if (rawInput === '') continue;
|
|
256
291
|
|
|
257
|
-
// Enviar para a IA
|
|
258
292
|
const controller = new AbortController();
|
|
259
293
|
const localInterruptHandler = () => controller.abort();
|
|
260
294
|
process.removeListener('SIGINT', globalSigIntHandler);
|
|
@@ -272,22 +306,16 @@ export async function startInteractive() {
|
|
|
272
306
|
try {
|
|
273
307
|
let responseText = await provider.sendMessage(messages, { signal: controller.signal });
|
|
274
308
|
spinner.stop();
|
|
275
|
-
|
|
276
309
|
const cleanedText = cleanAIResponse(responseText);
|
|
277
310
|
messages.push({ role: 'assistant', content: responseText });
|
|
278
|
-
|
|
279
311
|
console.log('\n' + lavender('bimmo ') + getModeStyle());
|
|
280
312
|
console.log(lavender('─'.repeat(50)));
|
|
281
313
|
console.log(marked(cleanedText));
|
|
282
314
|
console.log(gray('─'.repeat(50)) + '\n');
|
|
283
315
|
} catch (err) {
|
|
284
316
|
spinner.stop();
|
|
285
|
-
if (controller.signal.aborted || err.name === 'AbortError') {
|
|
286
|
-
|
|
287
|
-
messages.pop();
|
|
288
|
-
} else {
|
|
289
|
-
console.error(chalk.red('\n✖ Erro:') + ' ' + err.message + '\n');
|
|
290
|
-
}
|
|
317
|
+
if (controller.signal.aborted || err.name === 'AbortError') { console.log(yellow('\n⚠️ Interrompido.\n')); messages.pop(); }
|
|
318
|
+
else { console.error(chalk.red('\n✖ Erro:') + ' ' + err.message + '\n'); }
|
|
291
319
|
} finally {
|
|
292
320
|
process.removeListener('SIGINT', localInterruptHandler);
|
|
293
321
|
process.on('SIGINT', globalSigIntHandler);
|