bimmo-cli 3.2.1 → 4.0.1
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/bin/bimmo +22 -5
- package/package.json +12 -22
- package/src/{interface.jsx → interface.js} +132 -110
- package/dist/interface.js +0 -852
package/bin/bimmo
CHANGED
|
@@ -4,16 +4,33 @@
|
|
|
4
4
|
process.removeAllListeners('warning');
|
|
5
5
|
|
|
6
6
|
import { program } from 'commander';
|
|
7
|
-
import { configure } from '../src/config.js';
|
|
8
7
|
import fs from 'fs';
|
|
9
8
|
import path from 'path';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
|
-
|
|
12
|
-
// Importa a interface compilada em dist/
|
|
13
|
-
import { startInteractive } from '../dist/interface.js';
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
14
10
|
|
|
15
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
12
|
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// Tenta carregar do dist primeiro (produção), depois do src (desenvolvimento)
|
|
15
|
+
let interfaceModule;
|
|
16
|
+
const distPath = path.join(__dirname, '../dist/interface.js');
|
|
17
|
+
const srcPath = path.join(__dirname, '../src/interface.js');
|
|
18
|
+
|
|
19
|
+
if (fs.existsSync(distPath)) {
|
|
20
|
+
interfaceModule = await import(pathToFileURL(distPath).href);
|
|
21
|
+
} else {
|
|
22
|
+
interfaceModule = await import(pathToFileURL(srcPath).href);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { startInteractive } = interfaceModule;
|
|
26
|
+
|
|
27
|
+
const configPath = fs.existsSync(path.join(__dirname, '../dist/config.js'))
|
|
28
|
+
? path.join(__dirname, '../dist/config.js')
|
|
29
|
+
: path.join(__dirname, '../src/config.js');
|
|
30
|
+
|
|
31
|
+
const configModule = await import(pathToFileURL(configPath).href);
|
|
32
|
+
|
|
33
|
+
const { configure } = configModule;
|
|
17
34
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
18
35
|
|
|
19
36
|
program
|
package/package.json
CHANGED
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bimmo-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "🌿 Plataforma de IA universal profissional com interface React (Ink)
|
|
5
|
-
"scripts": {
|
|
6
|
-
"build": "esbuild src/interface.jsx --bundle --platform=node --format=esm --outfile=dist/interface.js --packages=external"
|
|
7
|
-
},
|
|
3
|
+
"version": "4.0.1",
|
|
4
|
+
"description": "🌿 Plataforma de IA universal profissional com interface React (Ink) pura.",
|
|
8
5
|
"bin": {
|
|
9
6
|
"bimmo": "bin/bimmo"
|
|
10
7
|
},
|
|
11
8
|
"type": "module",
|
|
12
9
|
"keywords": [
|
|
13
|
-
"ai",
|
|
14
|
-
"cli",
|
|
15
|
-
"ink",
|
|
16
|
-
"react",
|
|
17
|
-
"openai",
|
|
18
|
-
"anthropic",
|
|
19
|
-
"gemini",
|
|
20
|
-
"agent",
|
|
21
|
-
"swarm",
|
|
22
|
-
"terminal"
|
|
10
|
+
"ai", "cli", "ink", "react", "openai", "anthropic", "gemini", "agent", "swarm", "terminal"
|
|
23
11
|
],
|
|
24
12
|
"author": "Judah",
|
|
25
13
|
"license": "MIT",
|
|
@@ -30,13 +18,21 @@
|
|
|
30
18
|
"files": [
|
|
31
19
|
"bin/",
|
|
32
20
|
"src/",
|
|
33
|
-
"dist/",
|
|
34
21
|
"README.md",
|
|
35
22
|
".bimmo-context.md"
|
|
36
23
|
],
|
|
37
24
|
"publishConfig": {
|
|
38
25
|
"access": "public"
|
|
39
26
|
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "rimraf dist && esbuild src/*.js src/**/*.js --format=esm --outdir=dist --platform=node --target=node20",
|
|
29
|
+
"dev": "node bin/bimmo",
|
|
30
|
+
"start": "npm run build && node bin/bimmo"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"esbuild": "^0.24.0",
|
|
34
|
+
"rimraf": "^6.0.1"
|
|
35
|
+
},
|
|
40
36
|
"dependencies": {
|
|
41
37
|
"@anthropic-ai/sdk": "^0.36.3",
|
|
42
38
|
"@google/generative-ai": "^0.21.0",
|
|
@@ -52,7 +48,6 @@
|
|
|
52
48
|
"ink-spinner": "^5.0.0",
|
|
53
49
|
"ink-text-input": "^6.0.0",
|
|
54
50
|
"inquirer": "^9.3.8",
|
|
55
|
-
"jiti": "^2.6.1",
|
|
56
51
|
"marked": "^14.0.0",
|
|
57
52
|
"marked-terminal": "^7.0.0",
|
|
58
53
|
"mime-types": "^2.1.35",
|
|
@@ -60,11 +55,6 @@
|
|
|
60
55
|
"openai": "^4.82.0",
|
|
61
56
|
"ora": "^8.1.1",
|
|
62
57
|
"react": "^18.2.0",
|
|
63
|
-
"tsx": "^4.19.2",
|
|
64
58
|
"zod": "^3.24.1"
|
|
65
|
-
},
|
|
66
|
-
"devDependencies": {
|
|
67
|
-
"esbuild": "^0.27.4",
|
|
68
|
-
"tsx": "^4.21.0"
|
|
69
59
|
}
|
|
70
60
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React, { useState, useEffect, useMemo
|
|
2
|
-
import { render, Box, Text, useInput, useApp } from 'ink';
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { render, Box, Text, useInput, useApp, Static } from 'ink';
|
|
3
3
|
import TextInput from 'ink-text-input';
|
|
4
4
|
import Spinner from 'ink-spinner';
|
|
5
|
+
import Divider from 'ink-divider';
|
|
5
6
|
import chalk from 'chalk';
|
|
6
7
|
import figlet from 'figlet';
|
|
7
8
|
import { marked } from 'marked';
|
|
@@ -16,17 +17,20 @@ import { getProjectContext } from './project-context.js';
|
|
|
16
17
|
import { editState } from './agent.js';
|
|
17
18
|
import { SwarmOrchestrator } from './orchestrator.js';
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
// Cores e Temas
|
|
21
|
+
const THEME = {
|
|
22
|
+
green: '#00ff9d',
|
|
23
|
+
lavender: '#c084fc',
|
|
24
|
+
gray: '#6272a4',
|
|
25
|
+
yellow: '#f1fa8c',
|
|
26
|
+
red: '#ff5555',
|
|
27
|
+
cyan: '#8be9fd',
|
|
28
|
+
border: '#44475a'
|
|
29
|
+
};
|
|
26
30
|
|
|
27
31
|
marked.use(new TerminalRenderer({
|
|
28
|
-
heading: chalk.hex(lavender).bold,
|
|
29
|
-
code: chalk.hex(green),
|
|
32
|
+
heading: chalk.hex(THEME.lavender).bold,
|
|
33
|
+
code: chalk.hex(THEME.green),
|
|
30
34
|
strong: chalk.bold,
|
|
31
35
|
em: chalk.italic,
|
|
32
36
|
html: () => '',
|
|
@@ -34,81 +38,94 @@ marked.use(new TerminalRenderer({
|
|
|
34
38
|
|
|
35
39
|
const __filename = fileURLToPath(import.meta.url);
|
|
36
40
|
const __dirname = path.dirname(__filename);
|
|
37
|
-
|
|
41
|
+
// A localização do package.json depende de onde este arquivo está (src ou dist)
|
|
42
|
+
const pkgPath = fs.existsSync(path.join(__dirname, '../package.json'))
|
|
43
|
+
? path.join(__dirname, '../package.json')
|
|
44
|
+
: path.join(__dirname, '../../package.json');
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
38
46
|
const version = pkg.version;
|
|
39
47
|
|
|
48
|
+
const h = React.createElement;
|
|
49
|
+
|
|
40
50
|
// --- COMPONENTES ---
|
|
41
51
|
|
|
42
52
|
const Header = ({ config }) => (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
h(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
54
|
+
h(Text, { color: THEME.lavender, bold: true }, figlet.textSync('bimmo', { font: 'Small' })),
|
|
55
|
+
h(Box, { borderStyle: 'round', borderColor: THEME.border, paddingX: 1, justifyContent: 'space-between' },
|
|
56
|
+
h(Text, { color: THEME.green }, `v${version}`),
|
|
57
|
+
h(Box, null,
|
|
58
|
+
h(Text, { color: THEME.gray }, `${config.activeProfile || 'Default'} `),
|
|
59
|
+
h(Text, { color: THEME.lavender }, '•'),
|
|
60
|
+
h(Text, { color: THEME.cyan }, ` ${config.model}`)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
)
|
|
54
64
|
);
|
|
55
65
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</Box>
|
|
76
|
-
);
|
|
66
|
+
const Message = ({ role, content, displayContent }) => {
|
|
67
|
+
const isUser = role === 'user';
|
|
68
|
+
const color = isUser ? THEME.green : THEME.lavender;
|
|
69
|
+
const label = isUser ? '› VOCÊ' : '› bimmo';
|
|
70
|
+
|
|
71
|
+
return h(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
72
|
+
h(Box, null,
|
|
73
|
+
h(Text, { color, bold: true }, label),
|
|
74
|
+
role === 'system' && h(Text, { color: THEME.yellow }, ' [SISTEMA]')
|
|
75
|
+
),
|
|
76
|
+
h(Box, { paddingLeft: 2 },
|
|
77
|
+
h(Text, null,
|
|
78
|
+
role === 'assistant'
|
|
79
|
+
? marked.parse(content).trim()
|
|
80
|
+
: (displayContent || content)
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
};
|
|
77
85
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
{f.isDir ? '📁' : '📄'} {f.rel}{f.isDir ? '/' : ''}
|
|
84
|
-
|
|
85
|
-
))
|
|
86
|
-
|
|
86
|
+
const AutocompleteSuggestions = ({ suggestions }) => (
|
|
87
|
+
h(Box, { flexDirection: 'column', borderStyle: 'round', borderColor: THEME.border, paddingX: 1, marginBottom: 1 },
|
|
88
|
+
h(Text, { color: THEME.gray, dimColor: true, italic: true }, 'Sugestões (TAB):'),
|
|
89
|
+
suggestions.map((f, i) => (
|
|
90
|
+
h(Text, { key: i, color: i === 0 ? THEME.green : THEME.gray },
|
|
91
|
+
`${f.isDir ? '📁' : '📄'} ${f.rel}${f.isDir ? '/' : ''}`
|
|
92
|
+
)
|
|
93
|
+
))
|
|
94
|
+
)
|
|
87
95
|
);
|
|
88
96
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
const FooterStatus = ({ mode, activePersona, exitCounter }) => (
|
|
98
|
+
h(Box, { marginTop: 1, flexDirection: 'column' },
|
|
99
|
+
h(Divider, { borderColor: THEME.border }),
|
|
100
|
+
h(Box, { justifyContent: 'space-between', paddingX: 1 },
|
|
101
|
+
h(Box, null,
|
|
102
|
+
h(Text, { color: THEME.gray }, `Modo: `),
|
|
103
|
+
h(Text, { color: mode === 'edit' ? THEME.red : mode === 'plan' ? THEME.cyan : THEME.lavender, bold: true }, mode.toUpperCase()),
|
|
104
|
+
activePersona && h(Text, { color: THEME.yellow }, ` (${activePersona})`)
|
|
105
|
+
),
|
|
106
|
+
h(Text, { color: THEME.gray, dimColor: true }, `📁 ${path.basename(process.cwd())}`),
|
|
107
|
+
exitCounter === 1
|
|
108
|
+
? h(Text, { color: THEME.yellow, bold: true }, ' Pressione Ctrl+C novamente para sair ')
|
|
109
|
+
: h(Text, { color: THEME.gray, italic: true }, ' /help para comandos ')
|
|
110
|
+
)
|
|
111
|
+
)
|
|
97
112
|
);
|
|
98
113
|
|
|
114
|
+
// --- APP PRINCIPAL ---
|
|
115
|
+
|
|
99
116
|
const BimmoApp = ({ initialConfig }) => {
|
|
100
117
|
const { exit } = useApp();
|
|
101
118
|
const [config, setConfig] = useState(initialConfig);
|
|
102
119
|
const [mode, setMode] = useState('chat');
|
|
103
120
|
const [activePersona, setActivePersona] = useState(null);
|
|
104
121
|
const [messages, setMessages] = useState([]);
|
|
122
|
+
const [staticMessages, setStaticMessages] = useState([]); // Para mensagens antigas
|
|
105
123
|
const [input, setInput] = useState('');
|
|
106
124
|
const [isThinking, setIsThinking] = useState(false);
|
|
107
125
|
const [thinkingMessage, setThinkingMessage] = useState('bimmo pensando...');
|
|
108
126
|
const [exitCounter, setExitCounter] = useState(0);
|
|
109
127
|
const [provider, setProvider] = useState(() => createProvider(initialConfig));
|
|
110
128
|
|
|
111
|
-
// Inicializa contexto
|
|
112
129
|
useEffect(() => {
|
|
113
130
|
const ctx = getProjectContext();
|
|
114
131
|
setMessages([
|
|
@@ -149,6 +166,7 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
149
166
|
// Comandos de Sistema
|
|
150
167
|
if (cmd === '/exit') exit();
|
|
151
168
|
if (cmd === '/clear') {
|
|
169
|
+
setStaticMessages([]);
|
|
152
170
|
setMessages([{ role: 'system', content: getProjectContext() }, { role: 'assistant', content: 'Chat limpo.' }]);
|
|
153
171
|
return;
|
|
154
172
|
}
|
|
@@ -193,29 +211,12 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
193
211
|
return;
|
|
194
212
|
}
|
|
195
213
|
|
|
196
|
-
if (cmd === '/swarm') {
|
|
197
|
-
const orchestrator = new SwarmOrchestrator(config);
|
|
198
|
-
setIsThinking(true);
|
|
199
|
-
setThinkingMessage('Enxame em ação...');
|
|
200
|
-
try {
|
|
201
|
-
let response;
|
|
202
|
-
if (parts[1] === 'seq') response = await orchestrator.runSequential(parts[2].split(','), parts.slice(3).join(' '));
|
|
203
|
-
if (parts[1] === 'run') response = await orchestrator.runHierarchical(parts[2], parts[3].split(','), parts.slice(4).join(' '));
|
|
204
|
-
setMessages(prev => [...prev, { role: 'user', content: rawInput }, { role: 'assistant', content: response }]);
|
|
205
|
-
} catch (err) {
|
|
206
|
-
setMessages(prev => [...prev, { role: 'system', content: `Erro no enxame: ${err.message}` }]);
|
|
207
|
-
} finally {
|
|
208
|
-
setIsThinking(false);
|
|
209
|
-
}
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
214
|
if (cmd === '/help') {
|
|
214
|
-
setMessages(prev => [...prev, { role: 'assistant', content: `**Comandos
|
|
215
|
+
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` }]);
|
|
215
216
|
return;
|
|
216
217
|
}
|
|
217
218
|
|
|
218
|
-
// Processamento de arquivos
|
|
219
|
+
// Processamento de arquivos
|
|
219
220
|
setIsThinking(true);
|
|
220
221
|
let processedInput = rawInput;
|
|
221
222
|
const fileMatches = rawInput.match(/@[\w\.\-\/]+/g);
|
|
@@ -232,15 +233,22 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
const userMsg = { role: 'user', content: processedInput, displayContent: rawInput };
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
|
|
237
|
+
// Move mensagens antigas para static para performance
|
|
238
|
+
if (messages.length > 5) {
|
|
239
|
+
setStaticMessages(prev => [...prev, ...messages.slice(0, -5)]);
|
|
240
|
+
setMessages(prev => [...prev.slice(-5), userMsg]);
|
|
241
|
+
} else {
|
|
242
|
+
setMessages(prev => [...prev, userMsg]);
|
|
243
|
+
}
|
|
237
244
|
|
|
238
245
|
try {
|
|
239
|
-
let finalMessages =
|
|
246
|
+
let finalMessages = [...staticMessages, ...messages, userMsg];
|
|
240
247
|
if (activePersona && config.agents[activePersona]) {
|
|
241
248
|
const agent = config.agents[activePersona];
|
|
242
|
-
finalMessages = [{ role: 'system', content: `Sua tarefa: ${agent.role}\n\n${getProjectContext()}` }, ...
|
|
249
|
+
finalMessages = [{ role: 'system', content: `Sua tarefa: ${agent.role}\n\n${getProjectContext()}` }, ...finalMessages.filter(m => m.role !== 'system')];
|
|
243
250
|
}
|
|
251
|
+
|
|
244
252
|
const response = await provider.sendMessage(finalMessages);
|
|
245
253
|
setMessages(prev => [...prev, { role: 'assistant', content: response }]);
|
|
246
254
|
} catch (err) {
|
|
@@ -254,8 +262,12 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
254
262
|
if (key.ctrl && input === 'c') {
|
|
255
263
|
if (isThinking) setIsThinking(false);
|
|
256
264
|
else {
|
|
257
|
-
if (exitCounter === 0) {
|
|
258
|
-
|
|
265
|
+
if (exitCounter === 0) {
|
|
266
|
+
setExitCounter(1);
|
|
267
|
+
setTimeout(() => setExitCounter(0), 2000);
|
|
268
|
+
} else {
|
|
269
|
+
exit();
|
|
270
|
+
}
|
|
259
271
|
}
|
|
260
272
|
}
|
|
261
273
|
if (key.tab && filePreview.length > 0) {
|
|
@@ -266,39 +278,49 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
266
278
|
});
|
|
267
279
|
|
|
268
280
|
return (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
<MessageList messages={messages} />
|
|
281
|
+
h(Box, { flexDirection: 'column', paddingX: 2, paddingY: 1, minHeight: 15 },
|
|
282
|
+
h(Header, { config }),
|
|
272
283
|
|
|
273
|
-
{
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
</Text>
|
|
278
|
-
</Box>
|
|
279
|
-
)}
|
|
284
|
+
h(Box, { flexDirection: 'column', flexGrow: 1, marginBottom: 1 },
|
|
285
|
+
h(Static, { items: staticMessages }, (m, i) => h(Message, { key: `static-${i}`, ...m })),
|
|
286
|
+
messages.filter(m => m.role !== 'system').map((m, i) => h(Message, { key: i, ...m }))
|
|
287
|
+
),
|
|
280
288
|
|
|
281
|
-
|
|
289
|
+
isThinking && h(Box, { marginBottom: 1 },
|
|
290
|
+
h(Text, { color: THEME.lavender },
|
|
291
|
+
h(Spinner, { type: 'dots' }),
|
|
292
|
+
h(Text, { italic: true }, ` ${thinkingMessage}`)
|
|
293
|
+
)
|
|
294
|
+
),
|
|
282
295
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
296
|
+
filePreview.length > 0 && h(AutocompleteSuggestions, { suggestions: filePreview }),
|
|
297
|
+
|
|
298
|
+
h(Box, { borderStyle: 'round', borderColor: isThinking ? THEME.gray : THEME.lavender, paddingX: 1 },
|
|
299
|
+
h(Text, { bold: true, color: mode === 'edit' ? THEME.red : mode === 'plan' ? THEME.cyan : THEME.lavender },
|
|
300
|
+
`${activePersona ? `[${activePersona.toUpperCase()}] ` : ''}› `
|
|
301
|
+
),
|
|
302
|
+
h(TextInput, {
|
|
303
|
+
value: input,
|
|
304
|
+
onChange: setInput,
|
|
305
|
+
onSubmit: handleSubmit,
|
|
306
|
+
placeholder: 'Diga algo ou use / para comandos...'
|
|
307
|
+
})
|
|
308
|
+
),
|
|
309
|
+
|
|
310
|
+
h(FooterStatus, { mode, activePersona, exitCounter })
|
|
311
|
+
)
|
|
293
312
|
);
|
|
294
313
|
};
|
|
295
314
|
|
|
296
315
|
export async function startInteractive() {
|
|
297
316
|
const config = getConfig();
|
|
298
317
|
if (!config.provider || !config.apiKey) {
|
|
299
|
-
console.log(chalk.yellow('Provedor não configurado.
|
|
318
|
+
console.log(chalk.yellow('\n⚠️ Provedor não configurado.'));
|
|
319
|
+
console.log(chalk.gray('Execute "bimmo config" para configurar sua chave de API.\n'));
|
|
300
320
|
process.exit(0);
|
|
301
321
|
}
|
|
322
|
+
|
|
323
|
+
// Limpa tela e inicia
|
|
302
324
|
process.stdout.write('\x1Bc');
|
|
303
|
-
render(
|
|
325
|
+
render(h(BimmoApp, { initialConfig: config }));
|
|
304
326
|
}
|
package/dist/interface.js
DELETED
|
@@ -1,852 +0,0 @@
|
|
|
1
|
-
// src/interface.jsx
|
|
2
|
-
import React, { useState, useEffect, useMemo, useRef } from "react";
|
|
3
|
-
import { render, Box, Text, useInput, useApp } from "ink";
|
|
4
|
-
import TextInput from "ink-text-input";
|
|
5
|
-
import Spinner from "ink-spinner";
|
|
6
|
-
import chalk4 from "chalk";
|
|
7
|
-
import figlet from "figlet";
|
|
8
|
-
import { marked } from "marked";
|
|
9
|
-
import TerminalRenderer from "marked-terminal";
|
|
10
|
-
import fs3 from "fs";
|
|
11
|
-
import path3 from "path";
|
|
12
|
-
import { fileURLToPath } from "url";
|
|
13
|
-
|
|
14
|
-
// src/config.js
|
|
15
|
-
import Conf from "conf";
|
|
16
|
-
import inquirer from "inquirer";
|
|
17
|
-
import chalk from "chalk";
|
|
18
|
-
var config = new Conf({ projectName: "bimmo-cli" });
|
|
19
|
-
function getConfig() {
|
|
20
|
-
return config.store;
|
|
21
|
-
}
|
|
22
|
-
function updateActiveModel(newModel) {
|
|
23
|
-
config.set("model", newModel);
|
|
24
|
-
const active = config.get("activeProfile");
|
|
25
|
-
if (active) {
|
|
26
|
-
const profiles = config.get("profiles");
|
|
27
|
-
profiles[active].model = newModel;
|
|
28
|
-
config.set("profiles", profiles);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function switchProfile(name) {
|
|
32
|
-
const profiles = config.get("profiles") || {};
|
|
33
|
-
if (profiles[name]) {
|
|
34
|
-
const p = profiles[name];
|
|
35
|
-
config.set("provider", p.provider);
|
|
36
|
-
config.set("apiKey", p.apiKey);
|
|
37
|
-
config.set("model", p.model);
|
|
38
|
-
config.set("baseURL", p.baseURL);
|
|
39
|
-
config.set("activeProfile", name);
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// src/providers/openai.js
|
|
46
|
-
import OpenAI from "openai";
|
|
47
|
-
|
|
48
|
-
// src/providers/base.js
|
|
49
|
-
var BaseProvider = class {
|
|
50
|
-
constructor(config3) {
|
|
51
|
-
this.config = config3;
|
|
52
|
-
}
|
|
53
|
-
async sendMessage(messages, options = {}) {
|
|
54
|
-
throw new Error("M\xE9todo sendMessage deve ser implementado");
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// src/agent.js
|
|
59
|
-
import { tavily } from "@tavily/core";
|
|
60
|
-
import fs from "fs";
|
|
61
|
-
import path from "path";
|
|
62
|
-
import { execSync } from "child_process";
|
|
63
|
-
import * as diff from "diff";
|
|
64
|
-
import chalk2 from "chalk";
|
|
65
|
-
import inquirer2 from "inquirer";
|
|
66
|
-
var config2 = getConfig();
|
|
67
|
-
var tvly = config2.tavilyKey ? tavily({ apiKey: config2.tavilyKey }) : null;
|
|
68
|
-
var editState = {
|
|
69
|
-
autoAccept: false
|
|
70
|
-
};
|
|
71
|
-
var tools = [
|
|
72
|
-
{
|
|
73
|
-
name: "search_internet",
|
|
74
|
-
description: "Pesquisa informa\xE7\xF5es atualizadas na internet sobre qualquer assunto.",
|
|
75
|
-
parameters: {
|
|
76
|
-
type: "object",
|
|
77
|
-
properties: {
|
|
78
|
-
query: { type: "string", description: "O termo de busca" }
|
|
79
|
-
},
|
|
80
|
-
required: ["query"]
|
|
81
|
-
},
|
|
82
|
-
execute: async ({ query }) => {
|
|
83
|
-
if (!tvly) return "Erro: Chave de API da Tavily n\xE3o configurada. Use /config para configurar.";
|
|
84
|
-
console.log(chalk2.blue(`
|
|
85
|
-
\u{1F310} Pesquisando na web: ${chalk2.bold(query)}...`));
|
|
86
|
-
const searchResponse = await tvly.search(query, {
|
|
87
|
-
searchDepth: "advanced",
|
|
88
|
-
maxResults: 5
|
|
89
|
-
});
|
|
90
|
-
return JSON.stringify(searchResponse.results.map((r) => ({
|
|
91
|
-
title: r.title,
|
|
92
|
-
url: r.url,
|
|
93
|
-
content: r.content
|
|
94
|
-
})));
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: "read_file",
|
|
99
|
-
description: "L\xEA o conte\xFAdo de um arquivo no sistema.",
|
|
100
|
-
parameters: {
|
|
101
|
-
type: "object",
|
|
102
|
-
properties: {
|
|
103
|
-
path: { type: "string", description: "Caminho do arquivo" }
|
|
104
|
-
},
|
|
105
|
-
required: ["path"]
|
|
106
|
-
},
|
|
107
|
-
execute: async ({ path: filePath }) => {
|
|
108
|
-
try {
|
|
109
|
-
console.log(chalk2.blue(`
|
|
110
|
-
\u{1F4D6} Lendo arquivo: ${chalk2.bold(filePath)}...`));
|
|
111
|
-
return fs.readFileSync(filePath, "utf-8");
|
|
112
|
-
} catch (err) {
|
|
113
|
-
return `Erro ao ler arquivo: ${err.message}`;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: "write_file",
|
|
119
|
-
description: "Cria ou sobrescreve um arquivo com um conte\xFAdo espec\xEDfico.",
|
|
120
|
-
parameters: {
|
|
121
|
-
type: "object",
|
|
122
|
-
properties: {
|
|
123
|
-
path: { type: "string", description: "Caminho de destino" },
|
|
124
|
-
content: { type: "string", description: "Conte\xFAdo do arquivo" }
|
|
125
|
-
},
|
|
126
|
-
required: ["path", "content"]
|
|
127
|
-
},
|
|
128
|
-
execute: async ({ path: filePath, content }) => {
|
|
129
|
-
try {
|
|
130
|
-
const absolutePath = path.resolve(filePath);
|
|
131
|
-
const oldContent = fs.existsSync(absolutePath) ? fs.readFileSync(absolutePath, "utf-8") : "";
|
|
132
|
-
const differences = diff.diffLines(oldContent, content);
|
|
133
|
-
console.log(`
|
|
134
|
-
${chalk2.cyan("\u{1F4DD} Altera\xE7\xF5es propostas em:")} ${chalk2.bold(filePath)}`);
|
|
135
|
-
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
136
|
-
let hasChanges = false;
|
|
137
|
-
differences.forEach((part) => {
|
|
138
|
-
if (part.added || part.removed) hasChanges = true;
|
|
139
|
-
const color = part.added ? chalk2.green : part.removed ? chalk2.red : chalk2.gray;
|
|
140
|
-
const prefix = part.added ? "+" : part.removed ? "-" : " ";
|
|
141
|
-
if (part.added || part.removed) {
|
|
142
|
-
const lines = part.value.split("\n");
|
|
143
|
-
lines.forEach((line) => {
|
|
144
|
-
if (line || part.value.endsWith("\n")) {
|
|
145
|
-
process.stdout.write(color(`${prefix} ${line}
|
|
146
|
-
`));
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
} else {
|
|
150
|
-
const lines = part.value.split("\n").filter((l) => l.trim() !== "");
|
|
151
|
-
if (lines.length > 4) {
|
|
152
|
-
process.stdout.write(color(` ${lines[0]}
|
|
153
|
-
...
|
|
154
|
-
${lines[lines.length - 1]}
|
|
155
|
-
`));
|
|
156
|
-
} else if (lines.length > 0) {
|
|
157
|
-
lines.forEach((line) => process.stdout.write(color(` ${line}
|
|
158
|
-
`)));
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
163
|
-
if (!hasChanges) {
|
|
164
|
-
return "Nenhuma mudan\xE7a detectada no arquivo.";
|
|
165
|
-
}
|
|
166
|
-
if (!editState.autoAccept) {
|
|
167
|
-
const { approve } = await inquirer2.prompt([{
|
|
168
|
-
type: "list",
|
|
169
|
-
name: "approve",
|
|
170
|
-
message: "Deseja aplicar estas altera\xE7\xF5es?",
|
|
171
|
-
choices: [
|
|
172
|
-
{ name: "\u2705 Sim", value: "yes" },
|
|
173
|
-
{ name: "\u274C N\xE3o", value: "no" },
|
|
174
|
-
{ name: "\u26A1 Sim para tudo (Auto-Accept)", value: "all" }
|
|
175
|
-
]
|
|
176
|
-
}]);
|
|
177
|
-
if (approve === "no") return "Altera\xE7\xE3o rejeitada pelo usu\xE1rio.";
|
|
178
|
-
if (approve === "all") editState.autoAccept = true;
|
|
179
|
-
}
|
|
180
|
-
const dir = path.dirname(absolutePath);
|
|
181
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
182
|
-
fs.writeFileSync(absolutePath, content);
|
|
183
|
-
return `Arquivo ${filePath} atualizado com sucesso.`;
|
|
184
|
-
} catch (err) {
|
|
185
|
-
return `Erro ao escrever arquivo: ${err.message}`;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
name: "run_command",
|
|
191
|
-
description: "Executa um comando shell no sistema.",
|
|
192
|
-
parameters: {
|
|
193
|
-
type: "object",
|
|
194
|
-
properties: {
|
|
195
|
-
command: { type: "string", description: "Comando shell a ser executado" }
|
|
196
|
-
},
|
|
197
|
-
required: ["command"]
|
|
198
|
-
},
|
|
199
|
-
execute: async ({ command }) => {
|
|
200
|
-
try {
|
|
201
|
-
console.log(chalk2.yellow(`
|
|
202
|
-
\u26A1 Comando proposto: ${chalk2.bold(command)}`));
|
|
203
|
-
if (!editState.autoAccept) {
|
|
204
|
-
const { approve } = await inquirer2.prompt([{
|
|
205
|
-
type: "list",
|
|
206
|
-
name: "approve",
|
|
207
|
-
message: "Executar este comando?",
|
|
208
|
-
choices: [
|
|
209
|
-
{ name: "\u2705 Sim", value: "yes" },
|
|
210
|
-
{ name: "\u274C N\xE3o", value: "no" },
|
|
211
|
-
{ name: "\u26A1 Sim para tudo (Auto-Accept)", value: "all" }
|
|
212
|
-
]
|
|
213
|
-
}]);
|
|
214
|
-
if (approve === "no") return "Comando rejeitado pelo usu\xE1rio.";
|
|
215
|
-
if (approve === "all") editState.autoAccept = true;
|
|
216
|
-
}
|
|
217
|
-
const output = execSync(command, { encoding: "utf-8", timeout: 6e4 });
|
|
218
|
-
return output || "Comando executado com sucesso (sem retorno).";
|
|
219
|
-
} catch (err) {
|
|
220
|
-
return `Erro ao executar comando: ${err.stderr || err.message}`;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
];
|
|
225
|
-
|
|
226
|
-
// src/providers/openai.js
|
|
227
|
-
var OpenAIProvider = class extends BaseProvider {
|
|
228
|
-
constructor(config3) {
|
|
229
|
-
super(config3);
|
|
230
|
-
const extraHeaders = {};
|
|
231
|
-
if (this.config.baseURL?.includes("openrouter.ai")) {
|
|
232
|
-
extraHeaders["HTTP-Referer"] = "https://github.com/JudahAragao/bimmo-cli";
|
|
233
|
-
extraHeaders["X-Title"] = "bimmo-cli";
|
|
234
|
-
}
|
|
235
|
-
this.client = new OpenAI({
|
|
236
|
-
apiKey: this.config.apiKey,
|
|
237
|
-
baseURL: this.config.baseURL,
|
|
238
|
-
defaultHeaders: extraHeaders
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
formatMessages(messages) {
|
|
242
|
-
return messages.map((msg) => {
|
|
243
|
-
if (typeof msg.content === "string" || msg.content === null) return msg;
|
|
244
|
-
if (Array.isArray(msg.content)) {
|
|
245
|
-
const content = msg.content.map((part) => {
|
|
246
|
-
if (part.type === "text") return { type: "text", text: part.text };
|
|
247
|
-
if (part.type === "image") return {
|
|
248
|
-
type: "image_url",
|
|
249
|
-
image_url: { url: `data:${part.mimeType};base64,${part.data}` }
|
|
250
|
-
};
|
|
251
|
-
return part;
|
|
252
|
-
});
|
|
253
|
-
return { ...msg, content };
|
|
254
|
-
}
|
|
255
|
-
return msg;
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
async sendMessage(messages, options = {}) {
|
|
259
|
-
const formattedMessages = this.formatMessages(messages);
|
|
260
|
-
const openAiTools = tools.map((t) => ({
|
|
261
|
-
type: "function",
|
|
262
|
-
function: {
|
|
263
|
-
name: t.name,
|
|
264
|
-
description: t.description,
|
|
265
|
-
parameters: t.parameters
|
|
266
|
-
}
|
|
267
|
-
}));
|
|
268
|
-
const requestOptions = {
|
|
269
|
-
model: this.config.model,
|
|
270
|
-
messages: formattedMessages,
|
|
271
|
-
temperature: 0.7
|
|
272
|
-
};
|
|
273
|
-
if (openAiTools.length > 0) {
|
|
274
|
-
requestOptions.tools = openAiTools;
|
|
275
|
-
requestOptions.tool_choice = "auto";
|
|
276
|
-
}
|
|
277
|
-
const response = await this.client.chat.completions.create(requestOptions, { signal: options.signal });
|
|
278
|
-
const message = response.choices[0].message;
|
|
279
|
-
if (message.tool_calls) {
|
|
280
|
-
const toolResults = [];
|
|
281
|
-
for (const toolCall of message.tool_calls) {
|
|
282
|
-
if (options.signal?.aborted) throw new Error("Abortado pelo usu\xE1rio");
|
|
283
|
-
const tool = tools.find((t) => t.name === toolCall.function.name);
|
|
284
|
-
if (tool) {
|
|
285
|
-
const args = JSON.parse(toolCall.function.arguments);
|
|
286
|
-
const result = await tool.execute(args);
|
|
287
|
-
toolResults.push({
|
|
288
|
-
role: "tool",
|
|
289
|
-
tool_call_id: toolCall.id,
|
|
290
|
-
content: String(result)
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
const nextMessages = [...formattedMessages, message, ...toolResults];
|
|
295
|
-
return this.sendMessage(nextMessages, options);
|
|
296
|
-
}
|
|
297
|
-
return message.content;
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// src/providers/anthropic.js
|
|
302
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
303
|
-
var AnthropicProvider = class extends BaseProvider {
|
|
304
|
-
constructor(config3) {
|
|
305
|
-
super(config3);
|
|
306
|
-
this.client = new Anthropic({
|
|
307
|
-
apiKey: this.config.apiKey
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
formatContent(content) {
|
|
311
|
-
if (typeof content === "string") return content;
|
|
312
|
-
return content.map((part) => {
|
|
313
|
-
if (part.type === "text") return { type: "text", text: part.text };
|
|
314
|
-
if (part.type === "image") return {
|
|
315
|
-
type: "image",
|
|
316
|
-
source: { type: "base64", media_type: part.mimeType, data: part.data }
|
|
317
|
-
};
|
|
318
|
-
return part;
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
async sendMessage(messages, options = {}) {
|
|
322
|
-
const systemMessage = messages.find((m) => m.role === "system");
|
|
323
|
-
const userMessages = messages.filter((m) => m.role !== "system").map((m) => ({
|
|
324
|
-
role: m.role,
|
|
325
|
-
content: this.formatContent(m.content)
|
|
326
|
-
}));
|
|
327
|
-
const anthropicTools = tools.map((t) => ({
|
|
328
|
-
name: t.name,
|
|
329
|
-
description: t.description,
|
|
330
|
-
input_schema: t.parameters
|
|
331
|
-
}));
|
|
332
|
-
const response = await this.client.messages.create({
|
|
333
|
-
model: this.config.model,
|
|
334
|
-
max_tokens: 4096,
|
|
335
|
-
system: systemMessage ? systemMessage.content : void 0,
|
|
336
|
-
messages: userMessages,
|
|
337
|
-
tools: anthropicTools,
|
|
338
|
-
temperature: 0.7
|
|
339
|
-
}, { signal: options.signal });
|
|
340
|
-
if (response.stop_reason === "tool_use") {
|
|
341
|
-
const toolUse = response.content.find((p) => p.type === "tool_use");
|
|
342
|
-
const tool = tools.find((t) => t.name === toolUse.name);
|
|
343
|
-
if (tool) {
|
|
344
|
-
if (options.signal?.aborted) throw new Error("Abortado pelo usu\xE1rio");
|
|
345
|
-
const result = await tool.execute(toolUse.input);
|
|
346
|
-
const nextMessages = [
|
|
347
|
-
...messages,
|
|
348
|
-
{ role: "assistant", content: response.content },
|
|
349
|
-
{
|
|
350
|
-
role: "user",
|
|
351
|
-
content: [{
|
|
352
|
-
type: "tool_result",
|
|
353
|
-
tool_use_id: toolUse.id,
|
|
354
|
-
content: String(result)
|
|
355
|
-
}]
|
|
356
|
-
}
|
|
357
|
-
];
|
|
358
|
-
return this.sendMessage(nextMessages, options);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return response.content[0].text;
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
// src/providers/grok.js
|
|
366
|
-
import OpenAI2 from "openai";
|
|
367
|
-
var GrokProvider = class extends BaseProvider {
|
|
368
|
-
constructor(config3) {
|
|
369
|
-
super(config3);
|
|
370
|
-
this.client = new OpenAI2({
|
|
371
|
-
apiKey: this.config.apiKey,
|
|
372
|
-
baseURL: this.config.baseURL || "https://api.x.ai/v1"
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
formatMessages(messages) {
|
|
376
|
-
return messages.map((msg) => {
|
|
377
|
-
if (typeof msg.content === "string" || msg.content === null) {
|
|
378
|
-
return msg;
|
|
379
|
-
}
|
|
380
|
-
if (Array.isArray(msg.content)) {
|
|
381
|
-
const content = msg.content.map((part) => {
|
|
382
|
-
if (part.type === "text") return { type: "text", text: part.text };
|
|
383
|
-
if (part.type === "image") return {
|
|
384
|
-
type: "image_url",
|
|
385
|
-
image_url: { url: `data:${part.mimeType};base64,${part.data}` }
|
|
386
|
-
};
|
|
387
|
-
return part;
|
|
388
|
-
});
|
|
389
|
-
return { ...msg, content };
|
|
390
|
-
}
|
|
391
|
-
return msg;
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
async sendMessage(messages, options = {}) {
|
|
395
|
-
const formattedMessages = this.formatMessages(messages);
|
|
396
|
-
const response = await this.client.chat.completions.create({
|
|
397
|
-
model: this.config.model,
|
|
398
|
-
messages: formattedMessages,
|
|
399
|
-
temperature: 0.7
|
|
400
|
-
}, { signal: options.signal });
|
|
401
|
-
return response.choices[0].message.content;
|
|
402
|
-
}
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
// src/providers/gemini.js
|
|
406
|
-
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
407
|
-
var GeminiProvider = class extends BaseProvider {
|
|
408
|
-
constructor(config3) {
|
|
409
|
-
super(config3);
|
|
410
|
-
this.genAI = new GoogleGenerativeAI(this.config.apiKey);
|
|
411
|
-
}
|
|
412
|
-
formatContent(content) {
|
|
413
|
-
if (typeof content === "string") return [{ text: content }];
|
|
414
|
-
return content.map((part) => {
|
|
415
|
-
if (part.type === "text") return { text: part.text };
|
|
416
|
-
if (part.type === "image") return {
|
|
417
|
-
inlineData: { mimeType: part.mimeType, data: part.data }
|
|
418
|
-
};
|
|
419
|
-
return part;
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
async sendMessage(messages, options = {}) {
|
|
423
|
-
const systemPrompt = messages.find((m) => m.role === "system")?.content;
|
|
424
|
-
const history = messages.filter((m) => m.role !== "system").slice(0, -1).map((msg) => ({
|
|
425
|
-
role: msg.role === "user" ? "user" : "model",
|
|
426
|
-
parts: this.formatContent(msg.content)
|
|
427
|
-
}));
|
|
428
|
-
const lastMessageContent = this.formatContent(messages[messages.length - 1].content);
|
|
429
|
-
const geminiTools = tools.map((t) => ({
|
|
430
|
-
functionDeclarations: [{
|
|
431
|
-
name: t.name,
|
|
432
|
-
description: t.description,
|
|
433
|
-
parameters: t.parameters
|
|
434
|
-
}]
|
|
435
|
-
}));
|
|
436
|
-
const model = this.genAI.getGenerativeModel({
|
|
437
|
-
model: this.config.model,
|
|
438
|
-
systemInstruction: systemPrompt,
|
|
439
|
-
tools: geminiTools
|
|
440
|
-
});
|
|
441
|
-
const chat = model.startChat({
|
|
442
|
-
history,
|
|
443
|
-
generationConfig: { temperature: 0.7, maxOutputTokens: 8192 }
|
|
444
|
-
});
|
|
445
|
-
const result = await chat.sendMessage(lastMessageContent);
|
|
446
|
-
const response = await result.response;
|
|
447
|
-
const call = response.candidates[0].content.parts.find((p) => p.functionCall);
|
|
448
|
-
if (call) {
|
|
449
|
-
if (options.signal?.aborted) throw new Error("Abortado pelo usu\xE1rio");
|
|
450
|
-
const tool = tools.find((t) => t.name === call.functionCall.name);
|
|
451
|
-
if (tool) {
|
|
452
|
-
const toolResult = await tool.execute(call.functionCall.args);
|
|
453
|
-
const resultResponse = await chat.sendMessage([{
|
|
454
|
-
functionResponse: {
|
|
455
|
-
name: call.functionCall.name,
|
|
456
|
-
response: { content: toolResult }
|
|
457
|
-
}
|
|
458
|
-
}]);
|
|
459
|
-
return resultResponse.response.text();
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return response.text();
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// src/providers/ollama.js
|
|
467
|
-
import ollama from "ollama";
|
|
468
|
-
var OllamaProvider = class extends BaseProvider {
|
|
469
|
-
constructor(config3) {
|
|
470
|
-
super(config3);
|
|
471
|
-
this.client = ollama;
|
|
472
|
-
}
|
|
473
|
-
formatMessages(messages) {
|
|
474
|
-
return messages.map((msg) => {
|
|
475
|
-
if (typeof msg.content === "string") return msg;
|
|
476
|
-
const images = msg.content.filter((part) => part.type === "image").map((part) => part.data);
|
|
477
|
-
const text = msg.content.filter((part) => part.type === "text").map((part) => part.text).join(" ");
|
|
478
|
-
return {
|
|
479
|
-
role: msg.role,
|
|
480
|
-
content: text,
|
|
481
|
-
images: images.length > 0 ? images : void 0
|
|
482
|
-
};
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
async sendMessage(messages, options = {}) {
|
|
486
|
-
const formattedMessages = this.formatMessages(messages);
|
|
487
|
-
const response = await this.client.chat({
|
|
488
|
-
model: this.config.model,
|
|
489
|
-
messages: formattedMessages,
|
|
490
|
-
stream: false,
|
|
491
|
-
options: {
|
|
492
|
-
temperature: 0.7
|
|
493
|
-
}
|
|
494
|
-
}, { signal: options.signal });
|
|
495
|
-
const text = response.message?.content;
|
|
496
|
-
if (!text) {
|
|
497
|
-
throw new Error("Resposta inv\xE1lida do Ollama");
|
|
498
|
-
}
|
|
499
|
-
return text;
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
// src/providers/factory.js
|
|
504
|
-
function createProvider(config3) {
|
|
505
|
-
switch (config3.provider) {
|
|
506
|
-
case "openai":
|
|
507
|
-
return new OpenAIProvider(config3);
|
|
508
|
-
case "anthropic":
|
|
509
|
-
return new AnthropicProvider(config3);
|
|
510
|
-
case "grok":
|
|
511
|
-
return new GrokProvider(config3);
|
|
512
|
-
case "gemini":
|
|
513
|
-
return new GeminiProvider(config3);
|
|
514
|
-
case "ollama":
|
|
515
|
-
return new OllamaProvider(config3);
|
|
516
|
-
case "openrouter":
|
|
517
|
-
return new OpenAIProvider(config3);
|
|
518
|
-
case "deepseek":
|
|
519
|
-
return new OpenAIProvider(config3);
|
|
520
|
-
case "zai":
|
|
521
|
-
return new OpenAIProvider(config3);
|
|
522
|
-
default:
|
|
523
|
-
throw new Error(`Provider ${config3.provider} n\xE3o suportado ainda.`);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// src/project-context.js
|
|
528
|
-
import fs2 from "fs";
|
|
529
|
-
import path2 from "path";
|
|
530
|
-
import { execSync as execSync2 } from "child_process";
|
|
531
|
-
function getProjectContext() {
|
|
532
|
-
const cwd = process.cwd();
|
|
533
|
-
let context = "=== CONTEXTO DO PROJETO ===\n";
|
|
534
|
-
const bimmoRcPath = path2.join(cwd, ".bimmorc.json");
|
|
535
|
-
if (fs2.existsSync(bimmoRcPath)) {
|
|
536
|
-
try {
|
|
537
|
-
const rc = JSON.parse(fs2.readFileSync(bimmoRcPath, "utf-8"));
|
|
538
|
-
context += `Regras de Projeto (.bimmorc):
|
|
539
|
-
${JSON.stringify(rc, null, 2)}
|
|
540
|
-
|
|
541
|
-
`;
|
|
542
|
-
} catch (e) {
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
const instructionFiles = ["CLAUDE.md", "INSTRUCTIONS.md", ".bimmo-context.md", "CONTRIBUTING.md"];
|
|
546
|
-
for (const file of instructionFiles) {
|
|
547
|
-
const p = path2.join(cwd, file);
|
|
548
|
-
if (fs2.existsSync(p)) {
|
|
549
|
-
context += `Instru\xE7\xF5es de ${file}:
|
|
550
|
-
${fs2.readFileSync(p, "utf-8")}
|
|
551
|
-
|
|
552
|
-
`;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
try {
|
|
556
|
-
const tree = execSync2('find . -maxdepth 2 -not -path "*/.*" -not -path "./node_modules*"', { encoding: "utf-8" });
|
|
557
|
-
context += `Estrutura de Arquivos (Resumo):
|
|
558
|
-
${tree}
|
|
559
|
-
`;
|
|
560
|
-
} catch (e) {
|
|
561
|
-
context += "Estrutura de arquivos indispon\xEDvel.\n";
|
|
562
|
-
}
|
|
563
|
-
return context;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// src/orchestrator.js
|
|
567
|
-
import chalk3 from "chalk";
|
|
568
|
-
import ora from "ora";
|
|
569
|
-
var SwarmOrchestrator = class {
|
|
570
|
-
constructor(config3) {
|
|
571
|
-
this.config = config3;
|
|
572
|
-
this.agents = config3.agents || {};
|
|
573
|
-
this.profiles = config3.profiles || {};
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Executa uma tarefa sequencialmente através de uma lista de agentes.
|
|
577
|
-
* O output de um agente vira o contexto do próximo.
|
|
578
|
-
*/
|
|
579
|
-
async runSequential(agentNames, goal, options = {}) {
|
|
580
|
-
let currentContext = goal;
|
|
581
|
-
const results = [];
|
|
582
|
-
console.log(chalk3.cyan(`
|
|
583
|
-
\u{1F680} Iniciando Enxame Sequencial: ${agentNames.join(" \u2192 ")}
|
|
584
|
-
`));
|
|
585
|
-
for (const name of agentNames) {
|
|
586
|
-
const agent = this.agents[name];
|
|
587
|
-
if (!agent) throw new Error(`Agente ${name} n\xE3o encontrado.`);
|
|
588
|
-
const profile = this.profiles[agent.profile];
|
|
589
|
-
if (!profile) throw new Error(`Perfil ${agent.profile} do agente ${name} n\xE3o encontrado.`);
|
|
590
|
-
const agentConfig = {
|
|
591
|
-
...profile,
|
|
592
|
-
model: agent.modelOverride || profile.model
|
|
593
|
-
};
|
|
594
|
-
const provider = createProvider(agentConfig);
|
|
595
|
-
const spinner = ora({
|
|
596
|
-
text: chalk3.magenta(`Agente [${name}] trabalhando...`),
|
|
597
|
-
color: agent.mode === "edit" ? "red" : "magenta"
|
|
598
|
-
}).start();
|
|
599
|
-
const messages = [
|
|
600
|
-
{ role: "system", content: `Voc\xEA \xE9 o agente ${name}. Sua tarefa espec\xEDfica \xE9: ${agent.role}
|
|
601
|
-
|
|
602
|
-
MODO ATUAL: ${agent.mode.toUpperCase()}
|
|
603
|
-
${getProjectContext()}` },
|
|
604
|
-
{ role: "user", content: `CONTEXTO ATUAL:
|
|
605
|
-
${currentContext}
|
|
606
|
-
|
|
607
|
-
OBJETIVO DO ENXAME:
|
|
608
|
-
${goal}
|
|
609
|
-
|
|
610
|
-
Por favor, execute sua parte e retorne o resultado final processado.` }
|
|
611
|
-
];
|
|
612
|
-
try {
|
|
613
|
-
const response = await provider.sendMessage(messages, { signal: options.signal });
|
|
614
|
-
spinner.succeed(chalk3.green(`Agente [${name}] conclu\xEDdo.`));
|
|
615
|
-
currentContext = response;
|
|
616
|
-
results.push({ agent: name, output: response });
|
|
617
|
-
} catch (err) {
|
|
618
|
-
spinner.fail(chalk3.red(`Agente [${name}] falhou: ${err.message}`));
|
|
619
|
-
throw err;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
return currentContext;
|
|
623
|
-
}
|
|
624
|
-
/**
|
|
625
|
-
* Executa uma tarefa hierárquica.
|
|
626
|
-
* Um Manager recebe o objetivo, define o que cada Worker deve fazer, e consolida.
|
|
627
|
-
*/
|
|
628
|
-
async runHierarchical(managerName, workerNames, goal, options = {}) {
|
|
629
|
-
console.log(chalk3.cyan(`
|
|
630
|
-
\u{1F451} Iniciando Enxame Hier\xE1rquico (L\xEDder: ${managerName})
|
|
631
|
-
`));
|
|
632
|
-
const managerOutput = await this.runSequential([managerName], `Analise o objetivo abaixo e descreva o que cada um dos seguintes agentes deve fazer: ${workerNames.join(", ")}.
|
|
633
|
-
|
|
634
|
-
OBJETIVO: ${goal}`);
|
|
635
|
-
console.log(chalk3.blue(`
|
|
636
|
-
\u{1F477} Workers entrando em a\xE7\xE3o...
|
|
637
|
-
`));
|
|
638
|
-
const workerPromises = workerNames.map(
|
|
639
|
-
(name) => this.runSequential([name], `Baseado no plano do Manager:
|
|
640
|
-
${managerOutput}
|
|
641
|
-
|
|
642
|
-
Execute sua tarefa para o objetivo: ${goal}`, options)
|
|
643
|
-
);
|
|
644
|
-
const workerResults = await Promise.all(workerPromises);
|
|
645
|
-
const finalResult = await this.runSequential([managerName], `Aqui est\xE3o os resultados dos workers:
|
|
646
|
-
${workerResults.join("\n---\n")}
|
|
647
|
-
|
|
648
|
-
Por favor, consolide tudo em um resultado final perfeito para o objetivo: ${goal}`, options);
|
|
649
|
-
return finalResult;
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
// src/interface.jsx
|
|
654
|
-
var green = "#00ff9d";
|
|
655
|
-
var lavender = "#c084fc";
|
|
656
|
-
var gray = "#6272a4";
|
|
657
|
-
var yellow = "#f1fa8c";
|
|
658
|
-
var red = "#ff5555";
|
|
659
|
-
var cyan = "#8be9fd";
|
|
660
|
-
marked.use(new TerminalRenderer({
|
|
661
|
-
heading: chalk4.hex(lavender).bold,
|
|
662
|
-
code: chalk4.hex(green),
|
|
663
|
-
strong: chalk4.bold,
|
|
664
|
-
em: chalk4.italic,
|
|
665
|
-
html: () => ""
|
|
666
|
-
}));
|
|
667
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
668
|
-
var __dirname = path3.dirname(__filename);
|
|
669
|
-
var pkg = JSON.parse(fs3.readFileSync(path3.join(__dirname, "../package.json"), "utf-8"));
|
|
670
|
-
var version = pkg.version;
|
|
671
|
-
var Header = ({ config: config3 }) => /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: lavender }, figlet.textSync("bimmo")), /* @__PURE__ */ React.createElement(Box, { borderStyle: "single", borderColor: lavender, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, { color: green, bold: true }, "v", version), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: gray }, config3.activeProfile || "Default", " "), /* @__PURE__ */ React.createElement(Text, { color: lavender }, "\u2022"), /* @__PURE__ */ React.createElement(Text, { color: gray }, " ", config3.model))));
|
|
672
|
-
var MessageList = ({ messages }) => /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", flexGrow: 1 }, messages.filter((m) => m.role !== "system").slice(-10).map((m, i) => /* @__PURE__ */ React.createElement(Box, { key: i, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true, color: m.role === "user" ? green : lavender }, m.role === "user" ? "\u203A Voc\xEA" : "\u203A bimmo"), m.role === "system" && /* @__PURE__ */ React.createElement(Text, { color: yellow }, " [SISTEMA]")), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, null, m.role === "assistant" ? marked.parse(m.content).trim() : m.displayContent || m.content)))));
|
|
673
|
-
var Autocomplete = ({ suggestions }) => /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: gray, paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: gray, dimColor: true, italic: true }, "Sugest\xF5es (TAB para completar):"), suggestions.map((f, i) => /* @__PURE__ */ React.createElement(Text, { key: i, color: i === 0 ? green : gray }, f.isDir ? "\u{1F4C1}" : "\u{1F4C4}", " ", f.rel, f.isDir ? "/" : "")));
|
|
674
|
-
var Footer = ({ exitCounter }) => /* @__PURE__ */ React.createElement(Box, { marginTop: 1, justifyContent: "space-between", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: gray, dimColor: true }, "\u{1F4C1} ", path3.relative(process.env.HOME || "", process.cwd())), exitCounter === 1 && /* @__PURE__ */ React.createElement(Text, { color: yellow, bold: true }, " Pressione Ctrl+C novamente para sair"), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: gray, dimColor: true, italic: true }, "\u2191\u2193 para hist\xF3rico \u2022 /help para comandos")));
|
|
675
|
-
var BimmoApp = ({ initialConfig }) => {
|
|
676
|
-
const { exit } = useApp();
|
|
677
|
-
const [config3, setConfig] = useState(initialConfig);
|
|
678
|
-
const [mode, setMode] = useState("chat");
|
|
679
|
-
const [activePersona, setActivePersona] = useState(null);
|
|
680
|
-
const [messages, setMessages] = useState([]);
|
|
681
|
-
const [input, setInput] = useState("");
|
|
682
|
-
const [isThinking, setIsThinking] = useState(false);
|
|
683
|
-
const [thinkingMessage, setThinkingMessage] = useState("bimmo pensando...");
|
|
684
|
-
const [exitCounter, setExitCounter] = useState(0);
|
|
685
|
-
const [provider, setProvider] = useState(() => createProvider(initialConfig));
|
|
686
|
-
useEffect(() => {
|
|
687
|
-
const ctx = getProjectContext();
|
|
688
|
-
setMessages([
|
|
689
|
-
{ role: "system", content: ctx },
|
|
690
|
-
{ role: "assistant", content: `Ol\xE1! Sou o **bimmo v${version}**. Como posso ajudar hoje?
|
|
691
|
-
|
|
692
|
-
Digite \`/help\` para ver os comandos.` }
|
|
693
|
-
]);
|
|
694
|
-
}, []);
|
|
695
|
-
const filePreview = useMemo(() => {
|
|
696
|
-
if (!input.includes("@")) return [];
|
|
697
|
-
const words = input.split(" ");
|
|
698
|
-
const lastWord = words[words.length - 1];
|
|
699
|
-
if (!lastWord.startsWith("@")) return [];
|
|
700
|
-
try {
|
|
701
|
-
const p = lastWord.slice(1);
|
|
702
|
-
const dir = p.includes("/") ? p.substring(0, p.lastIndexOf("/")) : ".";
|
|
703
|
-
const filter = p.includes("/") ? p.substring(p.lastIndexOf("/") + 1) : p;
|
|
704
|
-
const absDir = path3.resolve(process.cwd(), dir);
|
|
705
|
-
if (!fs3.existsSync(absDir)) return [];
|
|
706
|
-
return fs3.readdirSync(absDir).filter((f) => f.startsWith(filter) && !f.startsWith(".") && f !== "node_modules").slice(0, 5).map((f) => ({
|
|
707
|
-
name: f,
|
|
708
|
-
isDir: fs3.statSync(path3.join(absDir, f)).isDirectory(),
|
|
709
|
-
rel: path3.join(dir === "." ? "" : dir, f)
|
|
710
|
-
}));
|
|
711
|
-
} catch (e) {
|
|
712
|
-
return [];
|
|
713
|
-
}
|
|
714
|
-
}, [input]);
|
|
715
|
-
const handleSubmit = async (val) => {
|
|
716
|
-
const rawInput = val.trim();
|
|
717
|
-
if (!rawInput) return;
|
|
718
|
-
setInput("");
|
|
719
|
-
const parts = rawInput.split(" ");
|
|
720
|
-
const cmd = parts[0].toLowerCase();
|
|
721
|
-
if (cmd === "/exit") exit();
|
|
722
|
-
if (cmd === "/clear") {
|
|
723
|
-
setMessages([{ role: "system", content: getProjectContext() }, { role: "assistant", content: "Chat limpo." }]);
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
if (["/chat", "/plan", "/edit"].includes(cmd)) {
|
|
727
|
-
setMode(cmd.slice(1));
|
|
728
|
-
if (cmd === "/edit") editState.autoAccept = parts[1] === "auto";
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
if (cmd === "/model") {
|
|
732
|
-
const newModel = parts[1];
|
|
733
|
-
if (newModel) {
|
|
734
|
-
updateActiveModel(newModel);
|
|
735
|
-
const newCfg = getConfig();
|
|
736
|
-
setConfig(newCfg);
|
|
737
|
-
setProvider(createProvider(newCfg));
|
|
738
|
-
setMessages((prev) => [...prev, { role: "system", content: `Modelo alterado para: ${newModel}` }]);
|
|
739
|
-
}
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
if (cmd === "/switch") {
|
|
743
|
-
const profile = parts[1];
|
|
744
|
-
if (switchProfile(profile)) {
|
|
745
|
-
const newCfg = getConfig();
|
|
746
|
-
setConfig(newCfg);
|
|
747
|
-
setProvider(createProvider(newCfg));
|
|
748
|
-
setMessages((prev) => [...prev, { role: "system", content: `Perfil alterado para: ${profile}` }]);
|
|
749
|
-
}
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
if (cmd === "/use") {
|
|
753
|
-
const agentName = parts[1];
|
|
754
|
-
const agents = config3.agents || {};
|
|
755
|
-
if (agentName === "normal") {
|
|
756
|
-
setActivePersona(null);
|
|
757
|
-
} else if (agents[agentName]) {
|
|
758
|
-
setActivePersona(agentName);
|
|
759
|
-
setMode(agents[agentName].mode || "chat");
|
|
760
|
-
}
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
if (cmd === "/swarm") {
|
|
764
|
-
const orchestrator = new SwarmOrchestrator(config3);
|
|
765
|
-
setIsThinking(true);
|
|
766
|
-
setThinkingMessage("Enxame em a\xE7\xE3o...");
|
|
767
|
-
try {
|
|
768
|
-
let response;
|
|
769
|
-
if (parts[1] === "seq") response = await orchestrator.runSequential(parts[2].split(","), parts.slice(3).join(" "));
|
|
770
|
-
if (parts[1] === "run") response = await orchestrator.runHierarchical(parts[2], parts[3].split(","), parts.slice(4).join(" "));
|
|
771
|
-
setMessages((prev) => [...prev, { role: "user", content: rawInput }, { role: "assistant", content: response }]);
|
|
772
|
-
} catch (err) {
|
|
773
|
-
setMessages((prev) => [...prev, { role: "system", content: `Erro no enxame: ${err.message}` }]);
|
|
774
|
-
} finally {
|
|
775
|
-
setIsThinking(false);
|
|
776
|
-
}
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
if (cmd === "/help") {
|
|
780
|
-
setMessages((prev) => [...prev, { role: "assistant", content: `**Comandos:** /chat, /plan, /edit, /switch, /model, /use, /swarm, /clear, /exit, @arquivo` }]);
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
setIsThinking(true);
|
|
784
|
-
let processedInput = rawInput;
|
|
785
|
-
const fileMatches = rawInput.match(/@[\w\.\-\/]+/g);
|
|
786
|
-
if (fileMatches) {
|
|
787
|
-
for (const match of fileMatches) {
|
|
788
|
-
const filePath = match.slice(1);
|
|
789
|
-
try {
|
|
790
|
-
if (fs3.existsSync(filePath)) {
|
|
791
|
-
const content = fs3.readFileSync(filePath, "utf-8");
|
|
792
|
-
processedInput = processedInput.replace(match, `
|
|
793
|
-
|
|
794
|
-
[Arquivo: ${filePath}]
|
|
795
|
-
\`\`\`
|
|
796
|
-
${content}
|
|
797
|
-
\`\`\`
|
|
798
|
-
`);
|
|
799
|
-
}
|
|
800
|
-
} catch (e) {
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
const userMsg = { role: "user", content: processedInput, displayContent: rawInput };
|
|
805
|
-
const newMessages = [...messages, userMsg];
|
|
806
|
-
setMessages(newMessages);
|
|
807
|
-
try {
|
|
808
|
-
let finalMessages = newMessages;
|
|
809
|
-
if (activePersona && config3.agents[activePersona]) {
|
|
810
|
-
const agent = config3.agents[activePersona];
|
|
811
|
-
finalMessages = [{ role: "system", content: `Sua tarefa: ${agent.role}
|
|
812
|
-
|
|
813
|
-
${getProjectContext()}` }, ...newMessages.filter((m) => m.role !== "system")];
|
|
814
|
-
}
|
|
815
|
-
const response = await provider.sendMessage(finalMessages);
|
|
816
|
-
setMessages((prev) => [...prev, { role: "assistant", content: response }]);
|
|
817
|
-
} catch (err) {
|
|
818
|
-
setMessages((prev) => [...prev, { role: "system", content: `Erro: ${err.message}` }]);
|
|
819
|
-
} finally {
|
|
820
|
-
setIsThinking(false);
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
useInput((input2, key) => {
|
|
824
|
-
if (key.ctrl && input2 === "c") {
|
|
825
|
-
if (isThinking) setIsThinking(false);
|
|
826
|
-
else {
|
|
827
|
-
if (exitCounter === 0) {
|
|
828
|
-
setExitCounter(1);
|
|
829
|
-
setTimeout(() => setExitCounter(0), 2e3);
|
|
830
|
-
} else exit();
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
if (key.tab && filePreview.length > 0) {
|
|
834
|
-
const words = input2.split(" ");
|
|
835
|
-
words[words.length - 1] = `@${filePreview[0].rel}${filePreview[0].isDir ? "/" : ""}`;
|
|
836
|
-
setInput(words.join(" "));
|
|
837
|
-
}
|
|
838
|
-
});
|
|
839
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 1, minHeight: 10 }, /* @__PURE__ */ React.createElement(Header, { config: config3 }), /* @__PURE__ */ React.createElement(MessageList, { messages }), isThinking && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: lavender }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" }), " ", /* @__PURE__ */ React.createElement(Text, { italic: true }, thinkingMessage))), filePreview.length > 0 && /* @__PURE__ */ React.createElement(Autocomplete, { suggestions: filePreview }), /* @__PURE__ */ React.createElement(Box, { borderStyle: "round", borderColor: isThinking ? gray : lavender, paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: mode === "edit" ? red : mode === "plan" ? cyan : lavender }, activePersona ? `[${activePersona.toUpperCase()}] ` : "", "[", mode.toUpperCase(), "] \u203A", " "), /* @__PURE__ */ React.createElement(TextInput, { value: input, onChange: setInput, onSubmit: handleSubmit, placeholder: "Como posso ajudar hoje?" })), /* @__PURE__ */ React.createElement(Footer, { exitCounter }));
|
|
840
|
-
};
|
|
841
|
-
async function startInteractive() {
|
|
842
|
-
const config3 = getConfig();
|
|
843
|
-
if (!config3.provider || !config3.apiKey) {
|
|
844
|
-
console.log(chalk4.yellow('Provedor n\xE3o configurado. Execute "bimmo config" primeiro.'));
|
|
845
|
-
process.exit(0);
|
|
846
|
-
}
|
|
847
|
-
process.stdout.write("\x1Bc");
|
|
848
|
-
render(/* @__PURE__ */ React.createElement(BimmoApp, { initialConfig: config3 }));
|
|
849
|
-
}
|
|
850
|
-
export {
|
|
851
|
-
startInteractive
|
|
852
|
-
};
|