bimmo-cli 2.2.9 → 2.2.11
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 +2 -2
- package/src/interface.js +68 -57
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bimmo-cli",
|
|
3
|
-
"version": "2.2.
|
|
4
|
-
"description": "🌿 Plataforma de IA universal profissional com Agentes e Swarms. Suporte a Autocomplete real-time
|
|
3
|
+
"version": "2.2.11",
|
|
4
|
+
"description": "🌿 Plataforma de IA universal profissional com Agentes e Swarms. Suporte a Autocomplete real-time robusto, Diffs e Contexto Inteligente.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bimmo": "bin/bimmo"
|
|
7
7
|
},
|
package/src/interface.js
CHANGED
|
@@ -21,16 +21,13 @@ 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
|
-
|
|
25
|
-
const terminalRenderer = new TerminalRenderer({
|
|
24
|
+
marked.use(new TerminalRenderer({
|
|
26
25
|
heading: chalk.hex('#c084fc').bold,
|
|
27
26
|
code: chalk.hex('#00ff9d'),
|
|
28
27
|
strong: chalk.bold,
|
|
29
28
|
em: chalk.italic,
|
|
30
|
-
html: (
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
marked.setOptions({ renderer: terminalRenderer });
|
|
29
|
+
html: () => '',
|
|
30
|
+
}));
|
|
34
31
|
|
|
35
32
|
const green = chalk.hex('#00ff9d');
|
|
36
33
|
const lavender = chalk.hex('#c084fc');
|
|
@@ -45,19 +42,28 @@ let exitTimer = null;
|
|
|
45
42
|
|
|
46
43
|
function getFilesForPreview(partialPath) {
|
|
47
44
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
let p = partialPath.startsWith('@') ? partialPath.slice(1) : partialPath;
|
|
46
|
+
let searchDir = '.';
|
|
47
|
+
let filter = '';
|
|
48
|
+
|
|
49
|
+
if (p.includes('/')) {
|
|
50
|
+
const lastSlash = p.lastIndexOf('/');
|
|
51
|
+
searchDir = p.substring(0, lastSlash) || '.';
|
|
52
|
+
filter = p.substring(lastSlash + 1);
|
|
53
|
+
} else {
|
|
54
|
+
searchDir = '.';
|
|
55
|
+
filter = p;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const absoluteSearchDir = path.resolve(process.cwd(), searchDir);
|
|
59
|
+
if (!fs.existsSync(absoluteSearchDir)) return [];
|
|
54
60
|
|
|
55
|
-
const files = fs.readdirSync(
|
|
61
|
+
const files = fs.readdirSync(absoluteSearchDir);
|
|
56
62
|
return files
|
|
57
|
-
.filter(f =>
|
|
63
|
+
.filter(f => f.startsWith(filter) && !f.startsWith('.') && f !== 'node_modules')
|
|
58
64
|
.map(f => {
|
|
59
|
-
const rel = path.join(
|
|
60
|
-
const isDir = fs.statSync(path.join(
|
|
65
|
+
const rel = path.join(searchDir === '.' ? '' : searchDir, f);
|
|
66
|
+
const isDir = fs.statSync(path.join(absoluteSearchDir, f)).isDirectory();
|
|
61
67
|
return { name: rel, isDir };
|
|
62
68
|
});
|
|
63
69
|
} catch (e) { return []; }
|
|
@@ -77,8 +83,7 @@ export async function startInteractive() {
|
|
|
77
83
|
let config = getConfig();
|
|
78
84
|
if (!config.provider || !config.apiKey) {
|
|
79
85
|
console.log(lavender(figlet.textSync('bimmo')));
|
|
80
|
-
await configure();
|
|
81
|
-
return startInteractive();
|
|
86
|
+
await configure(); return startInteractive();
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
let provider = createProvider(config);
|
|
@@ -86,14 +91,12 @@ export async function startInteractive() {
|
|
|
86
91
|
let messages = [];
|
|
87
92
|
|
|
88
93
|
const resetMessages = () => {
|
|
89
|
-
messages = [];
|
|
90
|
-
messages.push({ role: 'system', content: getProjectContext() });
|
|
94
|
+
messages = [{ role: 'system', content: getProjectContext() }];
|
|
91
95
|
if (activePersona) {
|
|
92
96
|
const agent = (config.agents || {})[activePersona];
|
|
93
97
|
if (agent) messages.push({ role: 'system', content: `Persona: ${agent.name}. Task: ${agent.role}` });
|
|
94
98
|
}
|
|
95
99
|
};
|
|
96
|
-
|
|
97
100
|
resetMessages();
|
|
98
101
|
|
|
99
102
|
console.clear();
|
|
@@ -122,39 +125,44 @@ export async function startInteractive() {
|
|
|
122
125
|
|
|
123
126
|
const clearPreview = () => {
|
|
124
127
|
if (currentPreviewLines > 0) {
|
|
125
|
-
// Move o cursor para o
|
|
126
|
-
readline.moveCursor(process.stdout, 0,
|
|
128
|
+
// Move o cursor para o início da primeira linha de preview
|
|
129
|
+
readline.moveCursor(process.stdout, 0, 1);
|
|
127
130
|
for (let i = 0; i < currentPreviewLines; i++) {
|
|
128
|
-
readline.moveCursor(process.stdout, 0, -1);
|
|
129
131
|
readline.clearLine(process.stdout, 0);
|
|
132
|
+
readline.moveCursor(process.stdout, 0, 1);
|
|
130
133
|
}
|
|
134
|
+
// Volta o cursor para a posição original
|
|
135
|
+
readline.moveCursor(process.stdout, 0, -(currentPreviewLines + 1));
|
|
131
136
|
currentPreviewLines = 0;
|
|
132
137
|
}
|
|
133
138
|
};
|
|
134
139
|
|
|
135
140
|
const showPreview = () => {
|
|
136
|
-
clearPreview();
|
|
137
141
|
const words = rl.line.split(' ');
|
|
138
142
|
const lastWord = words[words.length - 1];
|
|
139
143
|
|
|
140
144
|
if (lastWord.startsWith('@')) {
|
|
141
145
|
const files = getFilesForPreview(lastWord);
|
|
142
146
|
if (files.length > 0) {
|
|
147
|
+
clearPreview();
|
|
148
|
+
const displayFiles = files.slice(0, 10);
|
|
149
|
+
|
|
150
|
+
// Move cursor para o final da linha atual e pula linha
|
|
143
151
|
process.stdout.write('\n');
|
|
144
|
-
const displayFiles = files.slice(0, 8);
|
|
145
152
|
displayFiles.forEach(f => {
|
|
146
153
|
process.stdout.write(gray(` ${f.isDir ? '📁' : '📄'} ${f.name}\n`));
|
|
147
154
|
});
|
|
148
|
-
currentPreviewLines = displayFiles.length
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
readline.cursorTo(process.stdout, totalLen % cols);
|
|
155
|
+
currentPreviewLines = displayFiles.length;
|
|
156
|
+
|
|
157
|
+
// Retorna o cursor para a posição exata de escrita
|
|
158
|
+
readline.moveCursor(process.stdout, 0, -(currentPreviewLines + 1));
|
|
159
|
+
const promptWidth = stripVTControlCharacters(rl.getPrompt()).length;
|
|
160
|
+
readline.cursorTo(process.stdout, (promptWidth + rl.line.length) % (process.stdout.columns || 80));
|
|
161
|
+
} else {
|
|
162
|
+
clearPreview();
|
|
157
163
|
}
|
|
164
|
+
} else {
|
|
165
|
+
clearPreview();
|
|
158
166
|
}
|
|
159
167
|
};
|
|
160
168
|
|
|
@@ -163,28 +171,12 @@ export async function startInteractive() {
|
|
|
163
171
|
let modeLabel = `[${currentMode.toUpperCase()}]`;
|
|
164
172
|
if (currentMode === 'edit') modeLabel = editState.autoAccept ? '[EDIT(AUTO)]' : '[EDIT(MANUAL)]';
|
|
165
173
|
|
|
166
|
-
console.log(
|
|
174
|
+
console.log(`${gray(`📁 ${process.cwd()}`)}`);
|
|
167
175
|
rl.setPrompt(lavender.bold(personaLabel) + (currentMode === 'edit' ? chalk.red.bold(modeLabel) : lavender.bold(modeLabel)) + green(' > '));
|
|
168
176
|
rl.prompt();
|
|
169
177
|
};
|
|
170
178
|
|
|
171
|
-
// Escuta teclas
|
|
172
|
-
process.stdin.on('keypress', (s, key) => {
|
|
173
|
-
if (key && (key.name === 'return' || key.name === 'enter')) return;
|
|
174
|
-
setImmediate(() => showPreview());
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
rl.on('SIGINT', () => {
|
|
178
|
-
if (exitCounter === 0) {
|
|
179
|
-
exitCounter++;
|
|
180
|
-
process.stdout.write(`\n${gray('(Pressione novamente para sair)')}\n`);
|
|
181
|
-
exitTimer = setTimeout(() => { exitCounter = 0; }, 2000);
|
|
182
|
-
displayPrompt();
|
|
183
|
-
} else { process.exit(0); }
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
displayPrompt();
|
|
187
|
-
|
|
179
|
+
// Escuta teclas de forma segura
|
|
188
180
|
rl.on('line', async (input) => {
|
|
189
181
|
clearPreview();
|
|
190
182
|
const rawInput = input.trim();
|
|
@@ -200,7 +192,7 @@ export async function startInteractive() {
|
|
|
200
192
|
|
|
201
193
|
if (cmd === '/init') {
|
|
202
194
|
console.log(chalk.cyan('\n🚀 Gerando .bimmorc.json...\n'));
|
|
203
|
-
const initPrompt = `Analise o projeto e crie o .bimmorc.json
|
|
195
|
+
const initPrompt = `Analise o projeto e crie o .bimmorc.json estruturado. Use write_file.`;
|
|
204
196
|
const spinner = ora({ text: lavender(`bimmo pensando...`), color: 'red' }).start();
|
|
205
197
|
try {
|
|
206
198
|
const res = await provider.sendMessage([...messages, { role: 'user', content: initPrompt }]);
|
|
@@ -210,12 +202,14 @@ export async function startInteractive() {
|
|
|
210
202
|
resetMessages(); displayPrompt(); return;
|
|
211
203
|
}
|
|
212
204
|
|
|
213
|
-
if (cmd === '/config') {
|
|
205
|
+
if (cmd === '/config') {
|
|
206
|
+
rl.pause(); await configure(); config = getConfig(); provider = createProvider(config); rl.resume();
|
|
207
|
+
displayPrompt(); return;
|
|
208
|
+
}
|
|
214
209
|
|
|
215
210
|
// PROCESSAMENTO IA
|
|
216
211
|
const controller = new AbortController();
|
|
217
212
|
const abortHandler = () => controller.abort();
|
|
218
|
-
process.removeListener('SIGINT', () => {}); // Limpa handlers antigos
|
|
219
213
|
process.on('SIGINT', abortHandler);
|
|
220
214
|
|
|
221
215
|
let modeInstr = "";
|
|
@@ -234,7 +228,7 @@ export async function startInteractive() {
|
|
|
234
228
|
}
|
|
235
229
|
|
|
236
230
|
messages.push({ role: 'user', content: [...processedContent, { type: 'text', text: modeInstr }] });
|
|
237
|
-
const spinner = ora({ text: lavender(`bimmo pensando
|
|
231
|
+
const spinner = ora({ text: lavender(`bimmo pensando...`), color: currentMode === 'edit' ? 'red' : 'magenta' }).start();
|
|
238
232
|
|
|
239
233
|
try {
|
|
240
234
|
let responseText = await provider.sendMessage(messages, { signal: controller.signal });
|
|
@@ -253,4 +247,21 @@ export async function startInteractive() {
|
|
|
253
247
|
displayPrompt();
|
|
254
248
|
}
|
|
255
249
|
});
|
|
250
|
+
|
|
251
|
+
// Listener de tecla para o preview
|
|
252
|
+
process.stdin.on('keypress', (s, key) => {
|
|
253
|
+
if (key && (key.name === 'return' || key.name === 'enter')) return;
|
|
254
|
+
setImmediate(() => showPreview());
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
rl.on('SIGINT', () => {
|
|
258
|
+
if (exitCounter === 0) {
|
|
259
|
+
exitCounter++;
|
|
260
|
+
process.stdout.write(`\n${gray('(Pressione novamente para sair)')}\n`);
|
|
261
|
+
exitTimer = setTimeout(() => { exitCounter = 0; }, 2000);
|
|
262
|
+
displayPrompt();
|
|
263
|
+
} else { process.exit(0); }
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
displayPrompt();
|
|
256
267
|
}
|