bimmo-cli 3.0.0 → 3.1.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 +2 -2
- package/package.json +16 -2
- package/src/{interface.js → interface.jsx} +97 -153
package/bin/bimmo
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env -S node --import=tsx
|
|
2
2
|
|
|
3
3
|
// Silencia o aviso de depreciação do punycode que polui o terminal em versões novas do Node
|
|
4
4
|
process.removeAllListeners('warning');
|
|
5
5
|
|
|
6
6
|
import { program } from 'commander';
|
|
7
|
-
import { startInteractive } from '../src/interface.
|
|
7
|
+
import { startInteractive } from '../src/interface.jsx';
|
|
8
8
|
import { configure } from '../src/config.js';
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import path from 'path';
|
package/package.json
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bimmo-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "🌿 Plataforma de IA universal profissional com interface React (Ink). Agentes, Swarms, Autocomplete real-time e Contexto Inteligente.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bimmo": "bin/bimmo"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"keywords": [
|
|
10
|
-
"ai",
|
|
10
|
+
"ai",
|
|
11
|
+
"cli",
|
|
12
|
+
"ink",
|
|
13
|
+
"react",
|
|
14
|
+
"openai",
|
|
15
|
+
"anthropic",
|
|
16
|
+
"gemini",
|
|
17
|
+
"agent",
|
|
18
|
+
"swarm",
|
|
19
|
+
"terminal"
|
|
11
20
|
],
|
|
12
21
|
"author": "Judah",
|
|
13
22
|
"license": "MIT",
|
|
@@ -25,6 +34,7 @@
|
|
|
25
34
|
"access": "public"
|
|
26
35
|
},
|
|
27
36
|
"dependencies": {
|
|
37
|
+
"tsx": "^4.19.2",
|
|
28
38
|
"@anthropic-ai/sdk": "^0.36.3",
|
|
29
39
|
"@google/generative-ai": "^0.21.0",
|
|
30
40
|
"@tavily/core": "^0.0.2",
|
|
@@ -39,6 +49,7 @@
|
|
|
39
49
|
"ink-spinner": "^5.0.0",
|
|
40
50
|
"ink-text-input": "^6.0.0",
|
|
41
51
|
"inquirer": "^9.3.8",
|
|
52
|
+
"jiti": "^2.6.1",
|
|
42
53
|
"marked": "^14.0.0",
|
|
43
54
|
"marked-terminal": "^7.0.0",
|
|
44
55
|
"mime-types": "^2.1.35",
|
|
@@ -47,5 +58,8 @@
|
|
|
47
58
|
"ora": "^8.1.1",
|
|
48
59
|
"react": "^18.2.0",
|
|
49
60
|
"zod": "^3.24.1"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"tsx": "^4.21.0"
|
|
50
64
|
}
|
|
51
65
|
}
|
|
@@ -16,10 +16,17 @@ import { getProjectContext } from './project-context.js';
|
|
|
16
16
|
import { editState } from './agent.js';
|
|
17
17
|
import { SwarmOrchestrator } from './orchestrator.js';
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// --- CONFIGURAÇÃO VISUAL ---
|
|
20
|
+
const green = '#00ff9d';
|
|
21
|
+
const lavender = '#c084fc';
|
|
22
|
+
const gray = '#6272a4';
|
|
23
|
+
const yellow = '#f1fa8c';
|
|
24
|
+
const red = '#ff5555';
|
|
25
|
+
const cyan = '#8be9fd';
|
|
26
|
+
|
|
20
27
|
marked.use(new TerminalRenderer({
|
|
21
|
-
heading: chalk.hex(
|
|
22
|
-
code: chalk.hex(
|
|
28
|
+
heading: chalk.hex(lavender).bold,
|
|
29
|
+
code: chalk.hex(green),
|
|
23
30
|
strong: chalk.bold,
|
|
24
31
|
em: chalk.italic,
|
|
25
32
|
html: () => '',
|
|
@@ -30,12 +37,64 @@ const __dirname = path.dirname(__filename);
|
|
|
30
37
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
31
38
|
const version = pkg.version;
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
// --- COMPONENTES ---
|
|
41
|
+
|
|
42
|
+
const Header = ({ config }) => (
|
|
43
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
44
|
+
<Text color={lavender}>{figlet.textSync('bimmo')}</Text>
|
|
45
|
+
<Box borderStyle="single" borderColor={lavender} paddingX={1} justifyContent="space-between">
|
|
46
|
+
<Text color={green} bold>v{version}</Text>
|
|
47
|
+
<Box>
|
|
48
|
+
<Text color={gray}>{config.activeProfile || 'Default'} </Text>
|
|
49
|
+
<Text color={lavender}>•</Text>
|
|
50
|
+
<Text color={gray}> {config.model}</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
</Box>
|
|
53
|
+
</Box>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const MessageList = ({ messages }) => (
|
|
57
|
+
<Box flexDirection="column" flexGrow={1}>
|
|
58
|
+
{messages.filter(m => m.role !== 'system').slice(-10).map((m, i) => (
|
|
59
|
+
<Box key={i} flexDirection="column" marginBottom={1}>
|
|
60
|
+
<Box>
|
|
61
|
+
<Text bold color={m.role === 'user' ? green : lavender}>
|
|
62
|
+
{m.role === 'user' ? '› Você' : '› bimmo'}
|
|
63
|
+
</Text>
|
|
64
|
+
{m.role === 'system' && <Text color={yellow}> [SISTEMA]</Text>}
|
|
65
|
+
</Box>
|
|
66
|
+
<Box paddingLeft={2}>
|
|
67
|
+
<Text>
|
|
68
|
+
{m.role === 'assistant'
|
|
69
|
+
? marked.parse(m.content).trim()
|
|
70
|
+
: (m.displayContent || m.content)}
|
|
71
|
+
</Text>
|
|
72
|
+
</Box>
|
|
73
|
+
</Box>
|
|
74
|
+
))}
|
|
75
|
+
</Box>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const Autocomplete = ({ suggestions }) => (
|
|
79
|
+
<Box flexDirection="column" borderStyle="round" borderColor={gray} paddingX={1} marginBottom={1}>
|
|
80
|
+
<Text color={gray} dimColor italic>Sugestões (TAB para completar):</Text>
|
|
81
|
+
{suggestions.map((f, i) => (
|
|
82
|
+
<Text key={i} color={i === 0 ? green : gray}>
|
|
83
|
+
{f.isDir ? '📁' : '📄'} {f.rel}{f.isDir ? '/' : ''}
|
|
84
|
+
</Text>
|
|
85
|
+
))}
|
|
86
|
+
</Box>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const Footer = ({ exitCounter }) => (
|
|
90
|
+
<Box marginTop={1} justifyContent="space-between" paddingX={1}>
|
|
91
|
+
<Text color={gray} dimColor>📁 {path.relative(process.env.HOME || '', process.cwd())}</Text>
|
|
92
|
+
{exitCounter === 1 && <Text color={yellow} bold> Pressione Ctrl+C novamente para sair</Text>}
|
|
93
|
+
<Box>
|
|
94
|
+
<Text color={gray} dimColor italic>↑↓ para histórico • /help para comandos</Text>
|
|
95
|
+
</Box>
|
|
96
|
+
</Box>
|
|
97
|
+
);
|
|
39
98
|
|
|
40
99
|
const BimmoApp = ({ initialConfig }) => {
|
|
41
100
|
const { exit } = useApp();
|
|
@@ -49,32 +108,26 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
49
108
|
const [exitCounter, setExitCounter] = useState(0);
|
|
50
109
|
const [provider, setProvider] = useState(() => createProvider(initialConfig));
|
|
51
110
|
|
|
52
|
-
// Inicializa
|
|
111
|
+
// Inicializa contexto
|
|
53
112
|
useEffect(() => {
|
|
54
113
|
const ctx = getProjectContext();
|
|
55
|
-
setMessages([
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
role: 'assistant',
|
|
60
|
-
content: `Olá! Sou o **bimmo v${version}**. Como posso ajudar hoje?\n\nDigite \`/help\` para ver os comandos disponíveis.`
|
|
61
|
-
}]);
|
|
114
|
+
setMessages([
|
|
115
|
+
{ role: 'system', content: ctx },
|
|
116
|
+
{ role: 'assistant', content: `Olá! Sou o **bimmo v${version}**. Como posso ajudar hoje?\n\nDigite \`/help\` para ver os comandos.` }
|
|
117
|
+
]);
|
|
62
118
|
}, []);
|
|
63
119
|
|
|
64
|
-
// Lógica de Autocomplete em tempo real para @arquivos
|
|
65
120
|
const filePreview = useMemo(() => {
|
|
66
121
|
if (!input.includes('@')) return [];
|
|
67
122
|
const words = input.split(' ');
|
|
68
123
|
const lastWord = words[words.length - 1];
|
|
69
124
|
if (!lastWord.startsWith('@')) return [];
|
|
70
|
-
|
|
71
125
|
try {
|
|
72
126
|
const p = lastWord.slice(1);
|
|
73
127
|
const dir = p.includes('/') ? p.substring(0, p.lastIndexOf('/')) : '.';
|
|
74
128
|
const filter = p.includes('/') ? p.substring(p.lastIndexOf('/') + 1) : p;
|
|
75
129
|
const absDir = path.resolve(process.cwd(), dir);
|
|
76
130
|
if (!fs.existsSync(absDir)) return [];
|
|
77
|
-
|
|
78
131
|
return fs.readdirSync(absDir)
|
|
79
132
|
.filter(f => f.startsWith(filter) && !f.startsWith('.') && f !== 'node_modules')
|
|
80
133
|
.slice(0, 5)
|
|
@@ -90,26 +143,19 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
90
143
|
const rawInput = val.trim();
|
|
91
144
|
if (!rawInput) return;
|
|
92
145
|
setInput('');
|
|
93
|
-
|
|
94
|
-
const lowerInput = rawInput.toLowerCase();
|
|
95
146
|
const parts = rawInput.split(' ');
|
|
96
147
|
const cmd = parts[0].toLowerCase();
|
|
97
148
|
|
|
98
|
-
//
|
|
99
|
-
if (cmd === '/exit'
|
|
100
|
-
|
|
149
|
+
// Comandos de Sistema
|
|
150
|
+
if (cmd === '/exit') exit();
|
|
101
151
|
if (cmd === '/clear') {
|
|
102
|
-
|
|
103
|
-
setMessages([{ role: 'system', content: ctx }, { role: 'assistant', content: 'Chat limpo.' }]);
|
|
152
|
+
setMessages([{ role: 'system', content: getProjectContext() }, { role: 'assistant', content: 'Chat limpo.' }]);
|
|
104
153
|
return;
|
|
105
154
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
setMode('edit');
|
|
111
|
-
editState.autoAccept = parts[1] === 'auto';
|
|
112
|
-
return;
|
|
155
|
+
if (['/chat', '/plan', '/edit'].includes(cmd)) {
|
|
156
|
+
setMode(cmd.slice(1));
|
|
157
|
+
if (cmd === '/edit') editState.autoAccept = parts[1] === 'auto';
|
|
158
|
+
return;
|
|
113
159
|
}
|
|
114
160
|
|
|
115
161
|
if (cmd === '/model') {
|
|
@@ -131,8 +177,6 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
131
177
|
setConfig(newCfg);
|
|
132
178
|
setProvider(createProvider(newCfg));
|
|
133
179
|
setMessages(prev => [...prev, { role: 'system', content: `Perfil alterado para: ${profile}` }]);
|
|
134
|
-
} else {
|
|
135
|
-
setMessages(prev => [...prev, { role: 'system', content: `Perfil "${profile}" não encontrado.` }]);
|
|
136
180
|
}
|
|
137
181
|
return;
|
|
138
182
|
}
|
|
@@ -142,79 +186,44 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
142
186
|
const agents = config.agents || {};
|
|
143
187
|
if (agentName === 'normal') {
|
|
144
188
|
setActivePersona(null);
|
|
145
|
-
setMessages(prev => [...prev, { role: 'system', content: 'Modo normal ativado.' }]);
|
|
146
189
|
} else if (agents[agentName]) {
|
|
147
|
-
const agent = agents[agentName];
|
|
148
190
|
setActivePersona(agentName);
|
|
149
|
-
setMode(
|
|
150
|
-
if (agent.profile && agent.profile !== config.activeProfile) {
|
|
151
|
-
switchProfile(agent.profile);
|
|
152
|
-
const newCfg = getConfig();
|
|
153
|
-
setConfig(newCfg);
|
|
154
|
-
setProvider(createProvider(newCfg));
|
|
155
|
-
}
|
|
156
|
-
setMessages(prev => [...prev, { role: 'system', content: `Agente "${agentName}" ativo.` }]);
|
|
157
|
-
} else {
|
|
158
|
-
setMessages(prev => [...prev, { role: 'system', content: `Agente "${agentName}" não encontrado.` }]);
|
|
191
|
+
setMode(agents[agentName].mode || 'chat');
|
|
159
192
|
}
|
|
160
193
|
return;
|
|
161
194
|
}
|
|
162
195
|
|
|
163
196
|
if (cmd === '/swarm') {
|
|
164
|
-
const swarmType = parts[1];
|
|
165
197
|
const orchestrator = new SwarmOrchestrator(config);
|
|
166
198
|
setIsThinking(true);
|
|
167
199
|
setThinkingMessage('Enxame em ação...');
|
|
168
|
-
|
|
169
200
|
try {
|
|
170
201
|
let response;
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
const goal = parts.slice(3).join(' ');
|
|
174
|
-
response = await orchestrator.runSequential(agents, goal);
|
|
175
|
-
} else if (swarmType === 'run') {
|
|
176
|
-
const manager = parts[2];
|
|
177
|
-
const workers = parts[3].split(',');
|
|
178
|
-
const goal = parts.slice(4).join(' ');
|
|
179
|
-
response = await orchestrator.runHierarchical(manager, workers, goal);
|
|
180
|
-
}
|
|
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(' '));
|
|
181
204
|
setMessages(prev => [...prev, { role: 'user', content: rawInput }, { role: 'assistant', content: response }]);
|
|
182
205
|
} catch (err) {
|
|
183
206
|
setMessages(prev => [...prev, { role: 'system', content: `Erro no enxame: ${err.message}` }]);
|
|
184
207
|
} finally {
|
|
185
208
|
setIsThinking(false);
|
|
186
|
-
setThinkingMessage('bimmo pensando...');
|
|
187
209
|
}
|
|
188
210
|
return;
|
|
189
211
|
}
|
|
190
212
|
|
|
191
213
|
if (cmd === '/help') {
|
|
192
|
-
|
|
193
|
-
**Comandos Disponíveis:**
|
|
194
|
-
\`/chat\` | \`/plan\` | \`/edit [auto|manual]\` - Muda o modo
|
|
195
|
-
\`/switch [perfil]\` - Alterna perfis
|
|
196
|
-
\`/model [modelo]\` - Altera o modelo
|
|
197
|
-
\`/use [agente|normal]\` - Ativa um agente
|
|
198
|
-
\`/swarm seq [agente1,agente2] [objetivo]\` - Enxame sequencial
|
|
199
|
-
\`/swarm run [líder] [worker1,worker2] [objetivo]\` - Enxame hierárquico
|
|
200
|
-
\`/clear\` - Limpa o chat
|
|
201
|
-
\`/exit\` - Encerra o bimmo
|
|
202
|
-
\`@arquivo\` - Inclui conteúdo de arquivo
|
|
203
|
-
`;
|
|
204
|
-
setMessages(prev => [...prev, { role: 'assistant', content: helpText }]);
|
|
214
|
+
setMessages(prev => [...prev, { role: 'assistant', content: `**Comandos:** /chat, /plan, /edit, /switch, /model, /use, /swarm, /clear, /exit, @arquivo` }]);
|
|
205
215
|
return;
|
|
206
216
|
}
|
|
207
217
|
|
|
208
|
-
//
|
|
218
|
+
// Processamento de arquivos @
|
|
209
219
|
setIsThinking(true);
|
|
210
|
-
|
|
211
220
|
let processedInput = rawInput;
|
|
212
221
|
const fileMatches = rawInput.match(/@[\w\.\-\/]+/g);
|
|
213
222
|
if (fileMatches) {
|
|
214
223
|
for (const match of fileMatches) {
|
|
215
224
|
const filePath = match.slice(1);
|
|
216
225
|
try {
|
|
217
|
-
if (fs.existsSync(filePath)
|
|
226
|
+
if (fs.existsSync(filePath)) {
|
|
218
227
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
219
228
|
processedInput = processedInput.replace(match, `\n\n[Arquivo: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n`);
|
|
220
229
|
}
|
|
@@ -230,12 +239,8 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
230
239
|
let finalMessages = newMessages;
|
|
231
240
|
if (activePersona && config.agents[activePersona]) {
|
|
232
241
|
const agent = config.agents[activePersona];
|
|
233
|
-
finalMessages = [
|
|
234
|
-
{ role: 'system', content: `Sua tarefa: ${agent.role}\n\n${getProjectContext()}` },
|
|
235
|
-
...newMessages.filter(m => m.role !== 'system')
|
|
236
|
-
];
|
|
242
|
+
finalMessages = [{ role: 'system', content: `Sua tarefa: ${agent.role}\n\n${getProjectContext()}` }, ...newMessages.filter(m => m.role !== 'system')];
|
|
237
243
|
}
|
|
238
|
-
|
|
239
244
|
const response = await provider.sendMessage(finalMessages);
|
|
240
245
|
setMessages(prev => [...prev, { role: 'assistant', content: response }]);
|
|
241
246
|
} catch (err) {
|
|
@@ -247,18 +252,12 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
247
252
|
|
|
248
253
|
useInput((input, key) => {
|
|
249
254
|
if (key.ctrl && input === 'c') {
|
|
250
|
-
if (isThinking)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
setExitCounter(1);
|
|
255
|
-
setTimeout(() => setExitCounter(0), 2000);
|
|
256
|
-
} else {
|
|
257
|
-
exit();
|
|
258
|
-
}
|
|
255
|
+
if (isThinking) setIsThinking(false);
|
|
256
|
+
else {
|
|
257
|
+
if (exitCounter === 0) { setExitCounter(1); setTimeout(() => setExitCounter(0), 2000); }
|
|
258
|
+
else exit();
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
|
-
|
|
262
261
|
if (key.tab && filePreview.length > 0) {
|
|
263
262
|
const words = input.split(' ');
|
|
264
263
|
words[words.length - 1] = `@${filePreview[0].rel}${filePreview[0].isDir ? '/' : ''}`;
|
|
@@ -268,41 +267,9 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
268
267
|
|
|
269
268
|
return (
|
|
270
269
|
<Box flexDirection="column" paddingX={1} minHeight={10}>
|
|
271
|
-
{
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
<Box borderStyle="single" borderColor={lavender} paddingX={1} justifyContent="space-between">
|
|
275
|
-
<Text color={green} bold>v{version}</Text>
|
|
276
|
-
<Box>
|
|
277
|
-
<Text color={gray}>{config.activeProfile || 'Default'} </Text>
|
|
278
|
-
<Text color={lavender}>•</Text>
|
|
279
|
-
<Text color={gray}> {config.model}</Text>
|
|
280
|
-
</Box>
|
|
281
|
-
</Box>
|
|
282
|
-
</Box>
|
|
283
|
-
|
|
284
|
-
{/* MENSAGENS */}
|
|
285
|
-
<Box flexDirection="column" flexGrow={1}>
|
|
286
|
-
{messages.filter(m => m.role !== 'system').slice(-10).map((m, i) => (
|
|
287
|
-
<Box key={i} flexDirection="column" marginBottom={1}>
|
|
288
|
-
<Box>
|
|
289
|
-
<Text bold color={m.role === 'user' ? green : lavender}>
|
|
290
|
-
{m.role === 'user' ? '› Você' : '› bimmo'}
|
|
291
|
-
</Text>
|
|
292
|
-
{m.role === 'system' && <Text color={yellow}> [SISTEMA]</Text>}
|
|
293
|
-
</Box>
|
|
294
|
-
<Box paddingLeft={2}>
|
|
295
|
-
<Text>
|
|
296
|
-
{m.role === 'assistant'
|
|
297
|
-
? marked.parse(m.content).trim()
|
|
298
|
-
: (m.displayContent || m.content)}
|
|
299
|
-
</Text>
|
|
300
|
-
</Box>
|
|
301
|
-
</Box>
|
|
302
|
-
))}
|
|
303
|
-
</Box>
|
|
304
|
-
|
|
305
|
-
{/* STATUS / THINKING */}
|
|
270
|
+
<Header config={config} />
|
|
271
|
+
<MessageList messages={messages} />
|
|
272
|
+
|
|
306
273
|
{isThinking && (
|
|
307
274
|
<Box marginBottom={1}>
|
|
308
275
|
<Text color={lavender}>
|
|
@@ -311,40 +278,17 @@ const BimmoApp = ({ initialConfig }) => {
|
|
|
311
278
|
</Box>
|
|
312
279
|
)}
|
|
313
280
|
|
|
314
|
-
{
|
|
315
|
-
{filePreview.length > 0 && (
|
|
316
|
-
<Box flexDirection="column" borderStyle="round" borderColor={gray} paddingX={1} marginBottom={1}>
|
|
317
|
-
<Text color={gray} dimColor italic>Sugestões (TAB para completar):</Text>
|
|
318
|
-
{filePreview.map((f, i) => (
|
|
319
|
-
<Text key={i} color={i === 0 ? green : gray}>
|
|
320
|
-
{f.isDir ? '📁' : '📄'} {f.rel}{f.isDir ? '/' : ''}
|
|
321
|
-
</Text>
|
|
322
|
-
))}
|
|
323
|
-
</Box>
|
|
324
|
-
)}
|
|
281
|
+
{filePreview.length > 0 && <Autocomplete suggestions={filePreview} />}
|
|
325
282
|
|
|
326
|
-
{/* PROMPT */}
|
|
327
283
|
<Box borderStyle="round" borderColor={isThinking ? gray : lavender} paddingX={1}>
|
|
328
284
|
<Text bold color={mode === 'edit' ? red : mode === 'plan' ? cyan : lavender}>
|
|
329
285
|
{activePersona ? `[${activePersona.toUpperCase()}] ` : ''}
|
|
330
286
|
[{mode.toUpperCase()}] ›{' '}
|
|
331
287
|
</Text>
|
|
332
|
-
<TextInput
|
|
333
|
-
value={input}
|
|
334
|
-
onChange={setInput}
|
|
335
|
-
onSubmit={handleSubmit}
|
|
336
|
-
placeholder="Como posso ajudar hoje?"
|
|
337
|
-
/>
|
|
288
|
+
<TextInput value={input} onChange={setInput} onSubmit={handleSubmit} placeholder="Como posso ajudar hoje?" />
|
|
338
289
|
</Box>
|
|
339
290
|
|
|
340
|
-
{
|
|
341
|
-
<Box marginTop={1} justifyContent="space-between" paddingX={1}>
|
|
342
|
-
<Text color={gray} dimColor>📁 {path.relative(process.env.HOME || '', process.cwd())}</Text>
|
|
343
|
-
{exitCounter === 1 && <Text color={yellow} bold> Pressione Ctrl+C novamente para sair</Text>}
|
|
344
|
-
<Box>
|
|
345
|
-
<Text color={gray} dimColor italic>↑↓ para histórico (em breve) • /help para comandos</Text>
|
|
346
|
-
</Box>
|
|
347
|
-
</Box>
|
|
291
|
+
<Footer exitCounter={exitCounter} />
|
|
348
292
|
</Box>
|
|
349
293
|
);
|
|
350
294
|
};
|