bimmo-cli 4.1.0 → 4.1.2
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 +132 -8
package/package.json
CHANGED
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([]);
|
|
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);
|
|
178
|
-
const [confirmation, setConfirmation] = useState(null);
|
|
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
|
-
|
|
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]) {
|
|
@@ -224,8 +349,8 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
224
349
|
}
|
|
225
350
|
return;
|
|
226
351
|
}
|
|
227
|
-
// ... rest of useInput
|
|
228
352
|
|
|
353
|
+
if (key.ctrl && char === 'c') {
|
|
229
354
|
if (isThinking) {
|
|
230
355
|
setIsThinking(false);
|
|
231
356
|
setMessages(prev => [...prev, { role: 'system', content: 'Interrompido pelo usuário.' }]);
|
|
@@ -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
|
}
|