bimmo-cli 4.1.1 → 4.1.3

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 +1 -1
  2. package/src/interface.js +131 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimmo-cli",
3
- "version": "4.1.1",
3
+ "version": "4.1.3",
4
4
  "description": "🌿 Plataforma de IA universal profissional com interface React (Ink) pura.",
5
5
  "bin": {
6
6
  "bimmo": "bin/bimmo"
package/src/interface.js CHANGED
@@ -14,7 +14,6 @@ import { getConfig, updateActiveModel, switchProfile } from './config.js';
14
14
  import { createProvider } from './providers/factory.js';
15
15
  import { getProjectContext } from './project-context.js';
16
16
  import { editState } from './agent.js';
17
- import { SwarmOrchestrator } from './orchestrator.js';
18
17
 
19
18
  // Cores e Temas
20
19
  const THEME = {
@@ -147,7 +146,7 @@ const ToolDisplay = ({ status }) => {
147
146
  })
148
147
  ),
149
148
  output && h(Box, { marginTop: 1, maxHeight: 10 },
150
- h(Text, { color: THEME.gray }, output)
149
+ h(Text, { color: THEME.gray, dimColor: true }, output)
151
150
  )
152
151
  );
153
152
  };
@@ -169,17 +168,143 @@ const BimmoApp = ({ initialConfig }) => {
169
168
  const [mode, setMode] = useState('chat');
170
169
  const [activePersona, setActivePersona] = useState(null);
171
170
  const [messages, setMessages] = useState([]);
172
- const [staticMessages, setStaticMessages] = useState([]); // Para mensagens antigas
171
+ const [staticMessages, setStaticMessages] = useState([]);
173
172
  const [input, setInput] = useState('');
174
173
  const [selectedIndex, setSelectedIndex] = useState(0);
175
174
  const [isThinking, setIsThinking] = useState(false);
176
175
  const [thinkingMessage, setThinkingMessage] = useState('bimmo pensando...');
177
- const [toolStatus, setToolStatus] = useState(null); // { type, message, diff, output }
178
- const [confirmation, setConfirmation] = useState(null); // { message, resolve }
176
+ const [toolStatus, setToolStatus] = useState(null);
177
+ const [confirmation, setConfirmation] = useState(null);
179
178
  const [exitCounter, setExitCounter] = useState(0);
179
+ const [provider, setProvider] = useState(() => createProvider(initialConfig));
180
+
181
+ useEffect(() => {
182
+ const ctx = getProjectContext();
183
+ setMessages([
184
+ { role: 'system', content: ctx },
185
+ { role: 'assistant', content: `Olá! Sou o **bimmo v${version}**. Como posso ajudar hoje?\n\nDigite \`/help\` para ver os comandos.` }
186
+ ]);
187
+ }, []);
188
+
189
+ const filePreview = useMemo(() => {
190
+ if (!input.includes('@')) return [];
191
+ const words = input.split(' ');
192
+ const lastWord = words[words.length - 1];
193
+ if (!lastWord.startsWith('@')) return [];
194
+ try {
195
+ const p = lastWord.slice(1);
196
+ const dir = p.includes('/') ? p.substring(0, p.lastIndexOf('/')) : '.';
197
+ const filter = p.includes('/') ? p.substring(p.lastIndexOf('/') + 1) : p;
198
+ const absDir = path.resolve(process.cwd(), dir);
199
+ if (!fs.existsSync(absDir) || !fs.statSync(absDir).isDirectory()) return [];
200
+
201
+ const files = fs.readdirSync(absDir)
202
+ .filter(f => f.startsWith(filter) && !f.startsWith('.') && f !== 'node_modules')
203
+ .map(f => {
204
+ const fullPath = path.join(absDir, f);
205
+ const isDir = fs.statSync(fullPath).isDirectory();
206
+ return {
207
+ name: f,
208
+ isDir,
209
+ rel: path.join(dir === '.' ? '' : dir, f)
210
+ };
211
+ });
212
+
213
+ return files.sort((a, b) => {
214
+ if (a.isDir && !b.isDir) return -1;
215
+ if (!a.isDir && b.isDir) return 1;
216
+ return a.name.localeCompare(b.name);
217
+ }).slice(0, 10);
218
+ } catch (e) { return []; }
219
+ }, [input]);
220
+
221
+ useEffect(() => {
222
+ setSelectedIndex(0);
223
+ }, [filePreview.length]);
180
224
 
181
225
  const handleSubmit = async (val) => {
182
- // ... (unchanged code)
226
+ const rawInput = val.trim();
227
+ if (!rawInput) return;
228
+ setInput('');
229
+ const parts = rawInput.split(' ');
230
+ const cmd = parts[0].toLowerCase();
231
+
232
+ if (cmd === '/exit') exit();
233
+ if (cmd === '/clear') {
234
+ setStaticMessages([]);
235
+ setMessages([{ role: 'system', content: getProjectContext() }, { role: 'assistant', content: 'Chat limpo.' }]);
236
+ return;
237
+ }
238
+ if (['/chat', '/plan', '/edit'].includes(cmd)) {
239
+ setMode(cmd.slice(1));
240
+ if (cmd === '/edit') editState.autoAccept = parts[1] === 'auto';
241
+ return;
242
+ }
243
+
244
+ if (cmd === '/model') {
245
+ const newModel = parts[1];
246
+ if (newModel) {
247
+ updateActiveModel(newModel);
248
+ const newCfg = getConfig();
249
+ setConfig(newCfg);
250
+ setProvider(createProvider(newCfg));
251
+ setMessages(prev => [...prev, { role: 'system', content: `Modelo alterado para: ${newModel}` }]);
252
+ }
253
+ return;
254
+ }
255
+
256
+ if (cmd === '/switch') {
257
+ const profile = parts[1];
258
+ if (switchProfile(profile)) {
259
+ const newCfg = getConfig();
260
+ setConfig(newCfg);
261
+ setProvider(createProvider(newCfg));
262
+ setMessages(prev => [...prev, { role: 'system', content: `Perfil alterado para: ${profile}` }]);
263
+ }
264
+ return;
265
+ }
266
+
267
+ if (cmd === '/use') {
268
+ const agentName = parts[1];
269
+ const agents = config.agents || {};
270
+ if (agentName === 'normal') {
271
+ setActivePersona(null);
272
+ } else if (agents[agentName]) {
273
+ setActivePersona(agentName);
274
+ setMode(agents[agentName].mode || 'chat');
275
+ }
276
+ return;
277
+ }
278
+
279
+ if (cmd === '/help') {
280
+ setMessages(prev => [...prev, { role: 'assistant', content: `**Comandos Disponíveis:**\n\n- \`/chat\`, \`/plan\`, \`/edit\`: Alternar modos\n- \`/model <nome>\`: Trocar modelo atual\n- \`/switch <perfil>\`: Trocar perfil de API\n- \`/use <agente>\`: Usar agente especializado\n- \`/clear\`: Limpar histórico\n- \`/exit\`: Sair do bimmo\n- \`@arquivo\`: Referenciar arquivo local` }]);
281
+ return;
282
+ }
283
+
284
+ setIsThinking(true);
285
+ let processedInput = rawInput;
286
+ const fileMatches = rawInput.match(/@[\w\.\-\/]+/g);
287
+ if (fileMatches) {
288
+ for (const match of fileMatches) {
289
+ const filePath = match.slice(1);
290
+ try {
291
+ if (fs.existsSync(filePath)) {
292
+ const content = fs.readFileSync(filePath, 'utf-8');
293
+ processedInput = processedInput.replace(match, `\n\n[Arquivo: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n`);
294
+ }
295
+ } catch (e) {}
296
+ }
297
+ }
298
+
299
+ const userMsg = { role: 'user', content: processedInput, displayContent: rawInput };
300
+
301
+ if (messages.length > 5) {
302
+ setStaticMessages(prev => [...prev, ...messages.slice(0, -5)]);
303
+ setMessages(prev => [...prev.slice(-5), userMsg]);
304
+ } else {
305
+ setMessages(prev => [...prev, userMsg]);
306
+ }
307
+
183
308
  try {
184
309
  let finalMessages = [...staticMessages, ...messages, userMsg];
185
310
  if (activePersona && config.agents[activePersona]) {
@@ -302,7 +427,6 @@ export async function startInteractive() {
302
427
  process.exit(0);
303
428
  }
304
429
 
305
- // Limpa tela e inicia
306
430
  process.stdout.write('\x1Bc');
307
431
  render(h(BimmoApp, { initialConfig: config }));
308
432
  }