bimmo-cli 2.2.10 → 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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/interface.js +36 -30
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bimmo-cli",
3
- "version": "2.2.10",
4
- "description": "🌿 Plataforma de IA universal profissional com Agentes e Swarms. Suporte a Autocomplete real-time (estilo gemini-cli), Diffs e Contexto Inteligente.",
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
@@ -8,6 +8,7 @@ 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';
11
12
 
12
13
  import { getConfig, configure, updateActiveModel, switchProfile } from './config.js';
13
14
  import { createProvider } from './providers/factory.js';
@@ -39,9 +40,6 @@ let activePersona = null;
39
40
  let exitCounter = 0;
40
41
  let exitTimer = null;
41
42
 
42
- /**
43
- * Coleta arquivos para preview e completion (Nível Gemini-CLI)
44
- */
45
43
  function getFilesForPreview(partialPath) {
46
44
  try {
47
45
  let p = partialPath.startsWith('@') ? partialPath.slice(1) : partialPath;
@@ -127,37 +125,44 @@ export async function startInteractive() {
127
125
 
128
126
  const clearPreview = () => {
129
127
  if (currentPreviewLines > 0) {
130
- // Move para baixo, limpa cada linha e volta
128
+ // Move o cursor para o início da primeira linha de preview
129
+ readline.moveCursor(process.stdout, 0, 1);
131
130
  for (let i = 0; i < currentPreviewLines; i++) {
132
- process.stdout.write('\n');
133
131
  readline.clearLine(process.stdout, 0);
132
+ readline.moveCursor(process.stdout, 0, 1);
134
133
  }
135
- readline.moveCursor(process.stdout, 0, -currentPreviewLines);
134
+ // Volta o cursor para a posição original
135
+ readline.moveCursor(process.stdout, 0, -(currentPreviewLines + 1));
136
136
  currentPreviewLines = 0;
137
137
  }
138
138
  };
139
139
 
140
140
  const showPreview = () => {
141
- clearPreview();
142
141
  const words = rl.line.split(' ');
143
142
  const lastWord = words[words.length - 1];
144
143
 
145
144
  if (lastWord.startsWith('@')) {
146
145
  const files = getFilesForPreview(lastWord);
147
146
  if (files.length > 0) {
148
- // Salva a posição do cursor
149
- process.stdout.write('\u001b[s');
147
+ clearPreview();
148
+ const displayFiles = files.slice(0, 10);
150
149
 
150
+ // Move cursor para o final da linha atual e pula linha
151
151
  process.stdout.write('\n');
152
- const displayFiles = files.slice(0, 10);
153
152
  displayFiles.forEach(f => {
154
153
  process.stdout.write(gray(` ${f.isDir ? '📁' : '📄'} ${f.name}\n`));
155
154
  });
156
- currentPreviewLines = displayFiles.length + 1;
155
+ currentPreviewLines = displayFiles.length;
157
156
 
158
- // Restaura a posição do cursor
159
- process.stdout.write('\u001b[u');
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();
160
163
  }
164
+ } else {
165
+ clearPreview();
161
166
  }
162
167
  };
163
168
 
@@ -171,22 +176,7 @@ export async function startInteractive() {
171
176
  rl.prompt();
172
177
  };
173
178
 
174
- process.stdin.on('keypress', (s, key) => {
175
- if (key && (key.name === 'return' || key.name === 'enter')) return;
176
- setImmediate(() => showPreview());
177
- });
178
-
179
- rl.on('SIGINT', () => {
180
- if (exitCounter === 0) {
181
- exitCounter++;
182
- process.stdout.write(`\n${gray('(Pressione Ctrl+C novamente para sair)')}\n`);
183
- exitTimer = setTimeout(() => { exitCounter = 0; }, 2000);
184
- displayPrompt();
185
- } else { process.exit(0); }
186
- });
187
-
188
- displayPrompt();
189
-
179
+ // Escuta teclas de forma segura
190
180
  rl.on('line', async (input) => {
191
181
  clearPreview();
192
182
  const rawInput = input.trim();
@@ -220,7 +210,6 @@ export async function startInteractive() {
220
210
  // PROCESSAMENTO IA
221
211
  const controller = new AbortController();
222
212
  const abortHandler = () => controller.abort();
223
- process.removeListener('SIGINT', () => {});
224
213
  process.on('SIGINT', abortHandler);
225
214
 
226
215
  let modeInstr = "";
@@ -258,4 +247,21 @@ export async function startInteractive() {
258
247
  displayPrompt();
259
248
  }
260
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();
261
267
  }