bimmo-cli 2.2.9 → 2.2.10
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 +1 -1
- package/src/interface.js +46 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bimmo-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.10",
|
|
4
4
|
"description": "🌿 Plataforma de IA universal profissional com Agentes e Swarms. Suporte a Autocomplete real-time (estilo gemini-cli), Diffs e Contexto Inteligente.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bimmo": "bin/bimmo"
|
package/src/interface.js
CHANGED
|
@@ -8,7 +8,6 @@ import path from 'path';
|
|
|
8
8
|
import mime from 'mime-types';
|
|
9
9
|
import readline from 'readline';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import { stripVTControlCharacters } from 'util';
|
|
12
11
|
|
|
13
12
|
import { getConfig, configure, updateActiveModel, switchProfile } from './config.js';
|
|
14
13
|
import { createProvider } from './providers/factory.js';
|
|
@@ -21,16 +20,13 @@ const __dirname = path.dirname(__filename);
|
|
|
21
20
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
22
21
|
const version = pkg.version;
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
const terminalRenderer = new TerminalRenderer({
|
|
23
|
+
marked.use(new TerminalRenderer({
|
|
26
24
|
heading: chalk.hex('#c084fc').bold,
|
|
27
25
|
code: chalk.hex('#00ff9d'),
|
|
28
26
|
strong: chalk.bold,
|
|
29
27
|
em: chalk.italic,
|
|
30
|
-
html: (
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
marked.setOptions({ renderer: terminalRenderer });
|
|
28
|
+
html: () => '',
|
|
29
|
+
}));
|
|
34
30
|
|
|
35
31
|
const green = chalk.hex('#00ff9d');
|
|
36
32
|
const lavender = chalk.hex('#c084fc');
|
|
@@ -43,21 +39,33 @@ let activePersona = null;
|
|
|
43
39
|
let exitCounter = 0;
|
|
44
40
|
let exitTimer = null;
|
|
45
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Coleta arquivos para preview e completion (Nível Gemini-CLI)
|
|
44
|
+
*/
|
|
46
45
|
function getFilesForPreview(partialPath) {
|
|
47
46
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
let p = partialPath.startsWith('@') ? partialPath.slice(1) : partialPath;
|
|
48
|
+
let searchDir = '.';
|
|
49
|
+
let filter = '';
|
|
50
|
+
|
|
51
|
+
if (p.includes('/')) {
|
|
52
|
+
const lastSlash = p.lastIndexOf('/');
|
|
53
|
+
searchDir = p.substring(0, lastSlash) || '.';
|
|
54
|
+
filter = p.substring(lastSlash + 1);
|
|
55
|
+
} else {
|
|
56
|
+
searchDir = '.';
|
|
57
|
+
filter = p;
|
|
58
|
+
}
|
|
54
59
|
|
|
55
|
-
const
|
|
60
|
+
const absoluteSearchDir = path.resolve(process.cwd(), searchDir);
|
|
61
|
+
if (!fs.existsSync(absoluteSearchDir)) return [];
|
|
62
|
+
|
|
63
|
+
const files = fs.readdirSync(absoluteSearchDir);
|
|
56
64
|
return files
|
|
57
|
-
.filter(f =>
|
|
65
|
+
.filter(f => f.startsWith(filter) && !f.startsWith('.') && f !== 'node_modules')
|
|
58
66
|
.map(f => {
|
|
59
|
-
const rel = path.join(
|
|
60
|
-
const isDir = fs.statSync(path.join(
|
|
67
|
+
const rel = path.join(searchDir === '.' ? '' : searchDir, f);
|
|
68
|
+
const isDir = fs.statSync(path.join(absoluteSearchDir, f)).isDirectory();
|
|
61
69
|
return { name: rel, isDir };
|
|
62
70
|
});
|
|
63
71
|
} catch (e) { return []; }
|
|
@@ -77,8 +85,7 @@ export async function startInteractive() {
|
|
|
77
85
|
let config = getConfig();
|
|
78
86
|
if (!config.provider || !config.apiKey) {
|
|
79
87
|
console.log(lavender(figlet.textSync('bimmo')));
|
|
80
|
-
await configure();
|
|
81
|
-
return startInteractive();
|
|
88
|
+
await configure(); return startInteractive();
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
let provider = createProvider(config);
|
|
@@ -86,14 +93,12 @@ export async function startInteractive() {
|
|
|
86
93
|
let messages = [];
|
|
87
94
|
|
|
88
95
|
const resetMessages = () => {
|
|
89
|
-
messages = [];
|
|
90
|
-
messages.push({ role: 'system', content: getProjectContext() });
|
|
96
|
+
messages = [{ role: 'system', content: getProjectContext() }];
|
|
91
97
|
if (activePersona) {
|
|
92
98
|
const agent = (config.agents || {})[activePersona];
|
|
93
99
|
if (agent) messages.push({ role: 'system', content: `Persona: ${agent.name}. Task: ${agent.role}` });
|
|
94
100
|
}
|
|
95
101
|
};
|
|
96
|
-
|
|
97
102
|
resetMessages();
|
|
98
103
|
|
|
99
104
|
console.clear();
|
|
@@ -122,12 +127,12 @@ export async function startInteractive() {
|
|
|
122
127
|
|
|
123
128
|
const clearPreview = () => {
|
|
124
129
|
if (currentPreviewLines > 0) {
|
|
125
|
-
// Move
|
|
126
|
-
readline.moveCursor(process.stdout, 0, currentPreviewLines);
|
|
130
|
+
// Move para baixo, limpa cada linha e volta
|
|
127
131
|
for (let i = 0; i < currentPreviewLines; i++) {
|
|
128
|
-
|
|
132
|
+
process.stdout.write('\n');
|
|
129
133
|
readline.clearLine(process.stdout, 0);
|
|
130
134
|
}
|
|
135
|
+
readline.moveCursor(process.stdout, 0, -currentPreviewLines);
|
|
131
136
|
currentPreviewLines = 0;
|
|
132
137
|
}
|
|
133
138
|
};
|
|
@@ -140,20 +145,18 @@ export async function startInteractive() {
|
|
|
140
145
|
if (lastWord.startsWith('@')) {
|
|
141
146
|
const files = getFilesForPreview(lastWord);
|
|
142
147
|
if (files.length > 0) {
|
|
148
|
+
// Salva a posição do cursor
|
|
149
|
+
process.stdout.write('\u001b[s');
|
|
150
|
+
|
|
143
151
|
process.stdout.write('\n');
|
|
144
|
-
const displayFiles = files.slice(0,
|
|
152
|
+
const displayFiles = files.slice(0, 10);
|
|
145
153
|
displayFiles.forEach(f => {
|
|
146
154
|
process.stdout.write(gray(` ${f.isDir ? '📁' : '📄'} ${f.name}\n`));
|
|
147
155
|
});
|
|
148
156
|
currentPreviewLines = displayFiles.length + 1;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const promptText = rl.getPrompt();
|
|
153
|
-
const visualPromptLen = stripVTControlCharacters(promptText).length;
|
|
154
|
-
const totalLen = visualPromptLen + rl.line.length;
|
|
155
|
-
const cols = process.stdout.columns || 80;
|
|
156
|
-
readline.cursorTo(process.stdout, totalLen % cols);
|
|
157
|
+
|
|
158
|
+
// Restaura a posição do cursor
|
|
159
|
+
process.stdout.write('\u001b[u');
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
};
|
|
@@ -163,12 +166,11 @@ export async function startInteractive() {
|
|
|
163
166
|
let modeLabel = `[${currentMode.toUpperCase()}]`;
|
|
164
167
|
if (currentMode === 'edit') modeLabel = editState.autoAccept ? '[EDIT(AUTO)]' : '[EDIT(MANUAL)]';
|
|
165
168
|
|
|
166
|
-
console.log(
|
|
169
|
+
console.log(`${gray(`📁 ${process.cwd()}`)}`);
|
|
167
170
|
rl.setPrompt(lavender.bold(personaLabel) + (currentMode === 'edit' ? chalk.red.bold(modeLabel) : lavender.bold(modeLabel)) + green(' > '));
|
|
168
171
|
rl.prompt();
|
|
169
172
|
};
|
|
170
173
|
|
|
171
|
-
// Escuta teclas para o preview em tempo real
|
|
172
174
|
process.stdin.on('keypress', (s, key) => {
|
|
173
175
|
if (key && (key.name === 'return' || key.name === 'enter')) return;
|
|
174
176
|
setImmediate(() => showPreview());
|
|
@@ -177,7 +179,7 @@ export async function startInteractive() {
|
|
|
177
179
|
rl.on('SIGINT', () => {
|
|
178
180
|
if (exitCounter === 0) {
|
|
179
181
|
exitCounter++;
|
|
180
|
-
process.stdout.write(`\n${gray('(Pressione novamente para sair)')}\n`);
|
|
182
|
+
process.stdout.write(`\n${gray('(Pressione Ctrl+C novamente para sair)')}\n`);
|
|
181
183
|
exitTimer = setTimeout(() => { exitCounter = 0; }, 2000);
|
|
182
184
|
displayPrompt();
|
|
183
185
|
} else { process.exit(0); }
|
|
@@ -200,7 +202,7 @@ export async function startInteractive() {
|
|
|
200
202
|
|
|
201
203
|
if (cmd === '/init') {
|
|
202
204
|
console.log(chalk.cyan('\n🚀 Gerando .bimmorc.json...\n'));
|
|
203
|
-
const initPrompt = `Analise o projeto e crie o .bimmorc.json
|
|
205
|
+
const initPrompt = `Analise o projeto e crie o .bimmorc.json estruturado. Use write_file.`;
|
|
204
206
|
const spinner = ora({ text: lavender(`bimmo pensando...`), color: 'red' }).start();
|
|
205
207
|
try {
|
|
206
208
|
const res = await provider.sendMessage([...messages, { role: 'user', content: initPrompt }]);
|
|
@@ -210,12 +212,15 @@ export async function startInteractive() {
|
|
|
210
212
|
resetMessages(); displayPrompt(); return;
|
|
211
213
|
}
|
|
212
214
|
|
|
213
|
-
if (cmd === '/config') {
|
|
215
|
+
if (cmd === '/config') {
|
|
216
|
+
rl.pause(); await configure(); config = getConfig(); provider = createProvider(config); rl.resume();
|
|
217
|
+
displayPrompt(); return;
|
|
218
|
+
}
|
|
214
219
|
|
|
215
220
|
// PROCESSAMENTO IA
|
|
216
221
|
const controller = new AbortController();
|
|
217
222
|
const abortHandler = () => controller.abort();
|
|
218
|
-
process.removeListener('SIGINT', () => {});
|
|
223
|
+
process.removeListener('SIGINT', () => {});
|
|
219
224
|
process.on('SIGINT', abortHandler);
|
|
220
225
|
|
|
221
226
|
let modeInstr = "";
|
|
@@ -234,7 +239,7 @@ export async function startInteractive() {
|
|
|
234
239
|
}
|
|
235
240
|
|
|
236
241
|
messages.push({ role: 'user', content: [...processedContent, { type: 'text', text: modeInstr }] });
|
|
237
|
-
const spinner = ora({ text: lavender(`bimmo pensando
|
|
242
|
+
const spinner = ora({ text: lavender(`bimmo pensando...`), color: currentMode === 'edit' ? 'red' : 'magenta' }).start();
|
|
238
243
|
|
|
239
244
|
try {
|
|
240
245
|
let responseText = await provider.sendMessage(messages, { signal: controller.signal });
|