primocode 1.1.0 → 4.1.0
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/README.md +146 -67
- package/bin/primocode.js +574 -388
- package/lib/act.js +500 -495
- package/lib/api.js +39 -39
- package/lib/browser.js +492 -492
- package/lib/config.js +76 -71
- package/lib/mascot.js +121 -0
- package/lib/memory.js +179 -0
- package/lib/mouse.js +144 -0
- package/lib/platform.js +245 -0
- package/lib/tools.js +503 -503
- package/lib/ui.js +104 -132
- package/package.json +32 -37
- package/lib/jarvis.js +0 -68
package/bin/primocode.js
CHANGED
|
@@ -1,388 +1,574 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* PrimoCode
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
case '/
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PrimoCode v4.1 — Cliente Multi-Agente (Cross-Platform)
|
|
4
|
+
* Primo Code: Engenharia completa com pipeline oculto de 5 agentes
|
|
5
|
+
* Chat: Conversa direta
|
|
6
|
+
*
|
|
7
|
+
* REGRAS:
|
|
8
|
+
* - IA NUNCA entrega código no CMD
|
|
9
|
+
* - Sempre salva em arquivo e abre no navegador
|
|
10
|
+
* - Pesquisa web oculta para ganhar vantagem de qualidade
|
|
11
|
+
* - Memória de projetos em C:\primocode\ (ou ~/primocode/)
|
|
12
|
+
* - Mouse virtual para controle completo da interface
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const readline = require('readline');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const { spawn, execSync } = require('child_process');
|
|
22
|
+
|
|
23
|
+
const { c, welcomeBox, Spinner } = require('../lib/ui');
|
|
24
|
+
const api = require('../lib/api');
|
|
25
|
+
const tools = require('../lib/tools');
|
|
26
|
+
const cfg = require('../lib/config');
|
|
27
|
+
const memory = require('../lib/memory');
|
|
28
|
+
const mascot = require('../lib/mascot');
|
|
29
|
+
const mouse = require('../lib/mouse');
|
|
30
|
+
const platform = require('../lib/platform');
|
|
31
|
+
|
|
32
|
+
const state = {
|
|
33
|
+
config: cfg.loadConfig(),
|
|
34
|
+
history: [],
|
|
35
|
+
mode: 'Primo Code',
|
|
36
|
+
attachedFiles: [],
|
|
37
|
+
lastResponse: '',
|
|
38
|
+
currentProject: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const HELP = `
|
|
42
|
+
${c.bold('Comandos')}
|
|
43
|
+
/mode <nome> Primo Code | Chat
|
|
44
|
+
/attach <arquivo> anexa arquivo
|
|
45
|
+
/clear limpa conversa
|
|
46
|
+
/cls limpa tela
|
|
47
|
+
/uninstall remove PrimoCode
|
|
48
|
+
/app <nome> abrir aplicativo
|
|
49
|
+
/terminal abrir terminal
|
|
50
|
+
/run <arquivo> executar arquivo (py, js, html)
|
|
51
|
+
/screenshot capturar tela
|
|
52
|
+
/mouse <x> <y> mover mouse para coordenadas
|
|
53
|
+
/click <x> <y> clicar em coordenadas
|
|
54
|
+
/search <termo> pesquisar na web
|
|
55
|
+
/project mostrar projeto atual
|
|
56
|
+
/projects listar projetos
|
|
57
|
+
/help ajuda
|
|
58
|
+
/exit sair
|
|
59
|
+
|
|
60
|
+
${c.bold('Modos')}
|
|
61
|
+
${c.brand('Primo Code')} Engenharia completa (pipeline oculto)
|
|
62
|
+
${c.brand('Chat')} Conversa direta
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
let rl = null;
|
|
66
|
+
|
|
67
|
+
function makePrompt() {
|
|
68
|
+
const tag = state.mode === 'Primo Code' ? c.magenta('PrimoCode') : c.brand(state.mode);
|
|
69
|
+
const att = state.attachedFiles.length ? c.muted(` +${state.attachedFiles.length}`) : '';
|
|
70
|
+
const proj = state.currentProject ? c.muted(` [${state.currentProject}]`) : '';
|
|
71
|
+
return `\n${c.muted(tag + att + proj)} ${c.brand('❯')} `;
|
|
72
|
+
}
|
|
73
|
+
function refreshPrompt() { if (rl) rl.setPrompt(makePrompt()); }
|
|
74
|
+
|
|
75
|
+
// ---- Pesquisa Web Oculta ----
|
|
76
|
+
async function webSearch(query) {
|
|
77
|
+
try {
|
|
78
|
+
const result = platform.webSearch(query);
|
|
79
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
80
|
+
return result;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return { ok: false, error: e.message };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---- Pipeline Multi-Agente Oculto ----
|
|
87
|
+
async function runMultiAgent(userText) {
|
|
88
|
+
const sp = new Spinner('').start();
|
|
89
|
+
|
|
90
|
+
// Mostrar mascote pensando
|
|
91
|
+
process.stdout.write(mascot.setState('thinking', 'Analisando...'));
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// 1. Pesquisa web oculta (pesquisa web oculta) para ganhar vantagem
|
|
95
|
+
sp.update('Pesquisando na web...');
|
|
96
|
+
process.stdout.write(mascot.setState('searching', 'Pesquisando web...'));
|
|
97
|
+
await webSearch(userText + ' best practices tutorial');
|
|
98
|
+
|
|
99
|
+
// 2. Pipeline multi-agente (oculto do usuário)
|
|
100
|
+
sp.update('Processando...');
|
|
101
|
+
process.stdout.write(mascot.setState('thinking', 'Processando...'));
|
|
102
|
+
|
|
103
|
+
const result = await api.request(state.config.server, '/api/act', {
|
|
104
|
+
prompt: userText,
|
|
105
|
+
history: [],
|
|
106
|
+
model: 'main',
|
|
107
|
+
maxIterations: 3,
|
|
108
|
+
projectPath: process.cwd(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
sp.stop();
|
|
112
|
+
process.stdout.write(mascot.setState('coding', 'Gerando código...'));
|
|
113
|
+
|
|
114
|
+
if (result.error) {
|
|
115
|
+
console.log(c.err(`\nErro: ${result.error}`));
|
|
116
|
+
process.stdout.write(mascot.setState('idle'));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const code = result.finalCode || result.response || '';
|
|
121
|
+
state.lastResponse = code;
|
|
122
|
+
|
|
123
|
+
// 3. NUNCA mostrar código no CMD — sempre salvar em arquivo
|
|
124
|
+
if (code && code.length > 0) {
|
|
125
|
+
// Determinar nome do projeto
|
|
126
|
+
let projectName = state.currentProject;
|
|
127
|
+
if (!projectName) {
|
|
128
|
+
// Extrair nome do projeto do prompt
|
|
129
|
+
const match = userText.match(/(?:crie|faça|desenvolva|build|make|create)\s+(?:um|uma|o|a)?\s*([\w\-]+)/i);
|
|
130
|
+
projectName = match ? match[1] : `projeto-${Date.now()}`;
|
|
131
|
+
state.currentProject = projectName;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Criar projeto se não existir
|
|
135
|
+
const project = memory.createProject(projectName);
|
|
136
|
+
|
|
137
|
+
// Salvar código em arquivo
|
|
138
|
+
const ext = code.includes('<!DOCTYPE html>') || code.includes('<html') ? '.html' :
|
|
139
|
+
code.includes('import ') || code.includes('export ') ? '.js' :
|
|
140
|
+
code.includes('def ') || code.includes('import ') ? '.py' : '.txt';
|
|
141
|
+
const filename = `index${ext}`;
|
|
142
|
+
const filePath = memory.saveProjectFile(projectName, filename, code);
|
|
143
|
+
|
|
144
|
+
// Salvar também como output.html para fácil acesso
|
|
145
|
+
const outputHtml = path.join(memory.PRIMOCODE_DIR, 'output.html');
|
|
146
|
+
fs.writeFileSync(outputHtml, code, 'utf8');
|
|
147
|
+
|
|
148
|
+
// Atualizar memória do projeto
|
|
149
|
+
const projMem = memory.loadProjectMemory(projectName);
|
|
150
|
+
const updatedMem = projMem + `\n## Código Gerado (${new Date().toLocaleString('pt-BR')})\n\`\`\`\n${code.substring(0, 500)}...\n\`\`\`\n`;
|
|
151
|
+
memory.saveProjectMemory(projectName, updatedMem);
|
|
152
|
+
|
|
153
|
+
console.log(c.ok(`\n ✓ Código salvo em: ${filePath}`));
|
|
154
|
+
|
|
155
|
+
// Abrir no navegador se for HTML
|
|
156
|
+
if (ext === '.html') {
|
|
157
|
+
try {
|
|
158
|
+
platform.openBrowser(filePath);
|
|
159
|
+
console.log(c.muted(' 🌐 Abrindo no navegador...'));
|
|
160
|
+
process.stdout.write(mascot.setState('happy', 'Pronto!'));
|
|
161
|
+
} catch (e) {
|
|
162
|
+
console.log(c.warn(` ⚠ Não foi possível abrir no navegador: ${e.message}`));
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// Para outros arquivos, tenta executar
|
|
166
|
+
try {
|
|
167
|
+
if (ext === '.py') {
|
|
168
|
+
execSync(`start cmd /k python "${filePath}"`, { stdio: 'ignore' });
|
|
169
|
+
console.log(c.muted(' ▶ Executando Python...'));
|
|
170
|
+
} else if (ext === '.js') {
|
|
171
|
+
execSync(`start cmd /k node "${filePath}"`, { stdio: 'ignore' });
|
|
172
|
+
console.log(c.muted(' ▶ Executando Node.js...'));
|
|
173
|
+
}
|
|
174
|
+
process.stdout.write(mascot.setState('happy', 'Executando!'));
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.log(c.muted(' 📄 Arquivo salvo (não executável automaticamente)'));
|
|
177
|
+
process.stdout.write(mascot.setState('happy', 'Pronto!'));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
cfg.appendHistory({ mode: 'Primo Code', prompt: userText, response: code });
|
|
183
|
+
|
|
184
|
+
} catch (err) {
|
|
185
|
+
sp.stop();
|
|
186
|
+
console.log(c.err(`\nFalha no pipeline: ${err.message}`));
|
|
187
|
+
console.log(c.muted(' Tentando modo normal...'));
|
|
188
|
+
process.stdout.write(mascot.setState('idle'));
|
|
189
|
+
await runChat(userText, userText);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function runChat(promptForModel, userTextForMemory) {
|
|
194
|
+
const sp = new Spinner('').start();
|
|
195
|
+
process.stdout.write(mascot.setState('thinking', 'Pensando...'));
|
|
196
|
+
|
|
197
|
+
let started = false;
|
|
198
|
+
let text = '';
|
|
199
|
+
try {
|
|
200
|
+
await api.requestStream(state.config.server, '/api/normal/stream', {
|
|
201
|
+
prompt: promptForModel,
|
|
202
|
+
history: state.history.slice(-20),
|
|
203
|
+
model: cfg.resolveModel(state.mode),
|
|
204
|
+
}, (evt) => {
|
|
205
|
+
if (evt.erro) {
|
|
206
|
+
if (!started) sp.stop();
|
|
207
|
+
console.log(c.err(`\nErro: ${evt.erro}`));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (evt.delta) {
|
|
211
|
+
if (!started) { sp.stop(); process.stdout.write('\n'); started = true; }
|
|
212
|
+
text += evt.delta;
|
|
213
|
+
process.stdout.write(c.white(evt.delta));
|
|
214
|
+
}
|
|
215
|
+
if (evt.done && started) process.stdout.write('\n');
|
|
216
|
+
});
|
|
217
|
+
} catch (err) {
|
|
218
|
+
sp.stop();
|
|
219
|
+
console.log(c.err(`\nFalha de conexao: ${err.message}`));
|
|
220
|
+
process.stdout.write(mascot.setState('idle'));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (!started) { sp.stop(); console.log(c.muted('\n(Sem resposta)')); process.stdout.write(mascot.setState('idle')); return; }
|
|
224
|
+
|
|
225
|
+
state.history.push({ role: 'user', content: userTextForMemory });
|
|
226
|
+
state.history.push({ role: 'assistant', content: text });
|
|
227
|
+
state.lastResponse = text;
|
|
228
|
+
cfg.appendHistory({ mode: state.mode, prompt: userTextForMemory, response: text });
|
|
229
|
+
process.stdout.write(mascot.setState('happy'));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---- Envio Principal ----
|
|
233
|
+
async function sendPrompt(userText) {
|
|
234
|
+
let promptWithAttach = userText;
|
|
235
|
+
if (state.attachedFiles.length) {
|
|
236
|
+
const parts = state.attachedFiles.map(f => `Arquivo: ${f.path}\n\`\`\`\n${f.content}\n\`\`\``);
|
|
237
|
+
promptWithAttach = parts.join('\n\n') + '\n\n' + userText;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Adicionar contexto de memória
|
|
241
|
+
const memContext = memory.loadMemory();
|
|
242
|
+
if (memContext) {
|
|
243
|
+
promptWithAttach = `[Contexto de memória: ${memContext.substring(0, 500)}...]\n\n${promptWithAttach}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (state.mode === 'Primo Code') {
|
|
247
|
+
await runMultiAgent(promptWithAttach);
|
|
248
|
+
} else {
|
|
249
|
+
await runChat(promptWithAttach, userText);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
state.attachedFiles = [];
|
|
253
|
+
refreshPrompt();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ---- Slash Commands ----
|
|
257
|
+
async function handleSlash(line) {
|
|
258
|
+
const [cmd, ...rest] = line.trim().split(/\s+/);
|
|
259
|
+
const arg = rest.join(' ').trim();
|
|
260
|
+
|
|
261
|
+
switch (cmd.toLowerCase()) {
|
|
262
|
+
case '/help': process.stdout.write(HELP); return;
|
|
263
|
+
case '/exit': case '/quit':
|
|
264
|
+
console.log(c.muted('\nEncerrado.\n')); process.exit(0);
|
|
265
|
+
case '/cls': console.clear(); process.stdout.write(welcomeBox(state.config)); return;
|
|
266
|
+
case '/clear':
|
|
267
|
+
state.history = [];
|
|
268
|
+
console.log(c.muted(' Conversa limpa'));
|
|
269
|
+
return;
|
|
270
|
+
case '/version':
|
|
271
|
+
console.log(c.muted(` PrimoCode v4.1 — Cross-Platform`));
|
|
272
|
+
console.log(c.muted(` Servidor: ${state.config.server}`));
|
|
273
|
+
return;
|
|
274
|
+
case '/status': {
|
|
275
|
+
const sp = new Spinner('Verificando').start();
|
|
276
|
+
try {
|
|
277
|
+
const res = await api.request(state.config.server, '/health', {});
|
|
278
|
+
sp.stop();
|
|
279
|
+
if (res.status === 'ok') {
|
|
280
|
+
console.log(c.ok(' Online') + c.muted(` ${state.config.server}`));
|
|
281
|
+
} else {
|
|
282
|
+
console.log(c.err(' Indisponivel'));
|
|
283
|
+
}
|
|
284
|
+
} catch (e) {
|
|
285
|
+
sp.stop();
|
|
286
|
+
console.log(c.err(' Indisponivel'));
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
case '/mode': {
|
|
291
|
+
const valid = ['Primo Code', 'Chat'];
|
|
292
|
+
let target = arg;
|
|
293
|
+
if (!valid.includes(target)) {
|
|
294
|
+
console.log(c.warn(' /mode Primo Code | Chat'));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
state.mode = target;
|
|
298
|
+
state.config.model = cfg.resolveModel(target);
|
|
299
|
+
console.log(c.muted(` Modo: ${target}`));
|
|
300
|
+
refreshPrompt();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
case '/attach': {
|
|
304
|
+
if (!arg) { console.log(c.warn(' /attach <arquivo>')); return; }
|
|
305
|
+
try {
|
|
306
|
+
const content = tools.readFileSafe(arg);
|
|
307
|
+
state.attachedFiles.push({ path: arg, content });
|
|
308
|
+
console.log(c.muted(` Anexado: ${arg}`));
|
|
309
|
+
refreshPrompt();
|
|
310
|
+
} catch (e) { console.log(c.err(` ${e.message}`)); }
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
case '/detach':
|
|
314
|
+
state.attachedFiles = []; console.log(c.muted(' Anexos removidos')); refreshPrompt(); return;
|
|
315
|
+
case '/project': {
|
|
316
|
+
if (state.currentProject) {
|
|
317
|
+
console.log(c.muted(` Projeto atual: ${state.currentProject}`));
|
|
318
|
+
const projMem = memory.loadProjectMemory(state.currentProject);
|
|
319
|
+
if (projMem) {
|
|
320
|
+
console.log(c.muted(' Memória do projeto carregada'));
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
console.log(c.muted(' Nenhum projeto ativo'));
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
case '/projects': {
|
|
328
|
+
const projects = memory.listProjects();
|
|
329
|
+
if (projects.length) {
|
|
330
|
+
console.log(c.muted(' Projetos existentes:'));
|
|
331
|
+
for (const p of projects) {
|
|
332
|
+
console.log(c.muted(` 📁 ${p}`));
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
console.log(c.muted(' Nenhum projeto encontrado'));
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
case '/app': {
|
|
340
|
+
if (!arg) { console.log(c.warn(' /app <nome-do-aplicativo>')); return; }
|
|
341
|
+
try {
|
|
342
|
+
const result = await mouse.openApp(arg);
|
|
343
|
+
console.log(result.ok ? c.ok(` ✓ Aplicativo aberto: ${arg}`) : c.err(` Erro: ${result.error}`));
|
|
344
|
+
} catch (e) {
|
|
345
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
case '/terminal': {
|
|
350
|
+
try {
|
|
351
|
+
const result = platform.openTerminal();
|
|
352
|
+
console.log(result.ok ? c.ok(' ✓ Terminal aberto') : c.muted(` ${result.message || 'Terminal'}`));
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
case '/run': {
|
|
359
|
+
if (!arg) { console.log(c.warn(' /run <arquivo>')); return; }
|
|
360
|
+
try {
|
|
361
|
+
const ext = path.extname(arg).toLowerCase();
|
|
362
|
+
if (ext === '.html' || ext === '.htm') {
|
|
363
|
+
platform.openBrowser(arg);
|
|
364
|
+
console.log(c.ok(` ✓ Aberto no navegador: ${arg}`));
|
|
365
|
+
} else {
|
|
366
|
+
const result = platform.openApp(arg);
|
|
367
|
+
console.log(result.ok ? c.ok(` ✓ Executando: ${arg}`) : c.err(` Erro: ${result.error}`));
|
|
368
|
+
}
|
|
369
|
+
} catch (e) {
|
|
370
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
371
|
+
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
case '/screenshot': {
|
|
375
|
+
try {
|
|
376
|
+
const result = await mouse.screenshot();
|
|
377
|
+
console.log(result.ok ? c.ok(` ✓ Screenshot salvo: ${result.path}`) : c.err(` Erro: ${result.error}`));
|
|
378
|
+
} catch (e) {
|
|
379
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
case '/mouse': {
|
|
384
|
+
const [x, y] = arg.split(/\s+/).map(Number);
|
|
385
|
+
if (isNaN(x) || isNaN(y)) { console.log(c.warn(' /mouse <x> <y>')); return; }
|
|
386
|
+
try {
|
|
387
|
+
const result = await mouse.moveTo(x, y);
|
|
388
|
+
console.log(result.ok !== false ? c.ok(` ✓ Mouse movido para (${x}, ${y})`) : c.err(` Erro`));
|
|
389
|
+
} catch (e) {
|
|
390
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
case '/click': {
|
|
395
|
+
const [cx, cy] = arg.split(/\s+/).map(Number);
|
|
396
|
+
if (isNaN(cx) || isNaN(cy)) { console.log(c.warn(' /click <x> <y>')); return; }
|
|
397
|
+
try {
|
|
398
|
+
const result = await mouse.clickAt(cx, cy);
|
|
399
|
+
console.log(result.ok ? c.ok(` ✓ Clicado em (${cx}, ${cy})`) : c.err(` Erro: ${result.error}`));
|
|
400
|
+
} catch (e) {
|
|
401
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
case '/search': {
|
|
406
|
+
if (!arg) { console.log(c.warn(' /search <termo>')); return; }
|
|
407
|
+
try {
|
|
408
|
+
process.stdout.write(mascot.setState('searching', 'Pesquisando...'));
|
|
409
|
+
const result = await webSearch(arg);
|
|
410
|
+
console.log(result.ok ? c.ok(` ✓ Pesquisa iniciada: ${arg}`) : c.err(` Erro: ${result.error}`));
|
|
411
|
+
process.stdout.write(mascot.setState('idle'));
|
|
412
|
+
} catch (e) {
|
|
413
|
+
console.log(c.err(` Erro: ${e.message}`));
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
case '/uninstall': {
|
|
418
|
+
console.log(c.muted(' Removendo PrimoCode...'));
|
|
419
|
+
try {
|
|
420
|
+
const installDir = path.join(os.homedir(), '.primocode');
|
|
421
|
+
const globalLink = path.join(os.homedir(), '.local/bin/primocode');
|
|
422
|
+
if (fs.existsSync(globalLink)) fs.unlinkSync(globalLink);
|
|
423
|
+
if (fs.existsSync(installDir)) fs.rmSync(installDir, { recursive: true, force: true });
|
|
424
|
+
console.log(c.ok(' PrimoCode removido'));
|
|
425
|
+
} catch (e) { console.log(c.err(` Erro: ${e.message}`)); }
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
default:
|
|
429
|
+
console.log(c.warn(` Comando desconhecido: ${cmd}`));
|
|
430
|
+
console.log(c.muted(' /help'));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ---- One-shot ----
|
|
435
|
+
async function runOneShot(argv) {
|
|
436
|
+
const args = argv.slice(2);
|
|
437
|
+
let mode = 'Primo Code';
|
|
438
|
+
let attach = [];
|
|
439
|
+
const prompt = [];
|
|
440
|
+
for (let i = 0; i < args.length; i++) {
|
|
441
|
+
const a = args[i];
|
|
442
|
+
if (a === '--agent' || a === '-a' || a === '--act' || a === '-x' || a === '--primocode' || a === '-p') mode = 'Primo Code';
|
|
443
|
+
else if (a === '--chat' || a === '-c') mode = 'Chat';
|
|
444
|
+
else if (a === '--attach' || a === '-f') attach.push(args[++i]);
|
|
445
|
+
else if (a === '--help' || a === '-h') { printCliHelp(); process.exit(0); }
|
|
446
|
+
else if (a === '--version' || a === '-v') { console.log('PrimoCode v4.1 — Windows | macOS | Linux | Termux'); process.exit(0); }
|
|
447
|
+
else if (a === '--server') state.config.server = args[++i];
|
|
448
|
+
else prompt.push(a);
|
|
449
|
+
}
|
|
450
|
+
if (!prompt.length) return false;
|
|
451
|
+
state.mode = mode;
|
|
452
|
+
for (const p of attach) {
|
|
453
|
+
try { state.attachedFiles.push({ path: p, content: tools.readFileSafe(p) }); }
|
|
454
|
+
catch (e) { console.log(c.err(` Anexo ${p}: ${e.message}`)); }
|
|
455
|
+
}
|
|
456
|
+
await sendPrompt(prompt.join(' '));
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function printCliHelp() {
|
|
461
|
+
console.log(welcomeBox(state.config));
|
|
462
|
+
console.log(`${c.bold('Uso')}`);
|
|
463
|
+
console.log(` primocode Interativo (Multi-Agente)`);
|
|
464
|
+
console.log(` primocode "crie um site" Pipeline completo`);
|
|
465
|
+
console.log(` primocode --chat "duvida" Modo conversa`);
|
|
466
|
+
console.log(HELP);
|
|
467
|
+
console.log(`${c.bold('Comandos de Sistema')}`);
|
|
468
|
+
console.log(` /app <nome> Abrir aplicativo`);
|
|
469
|
+
console.log(` /terminal Abrir terminal`);
|
|
470
|
+
console.log(` /run <arquivo> Executar arquivo (Python, JS, HTML)`);
|
|
471
|
+
console.log(` /screenshot Capturar tela`);
|
|
472
|
+
console.log(` /mouse <x> <y> Mover mouse`);
|
|
473
|
+
console.log(` /click <x> <y> Clicar`);
|
|
474
|
+
console.log(` /search <termo> Pesquisar na web`);
|
|
475
|
+
console.log(` /project Ver projeto atual`);
|
|
476
|
+
console.log(` /projects Listar projetos`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ---- Verificar Termux na inicialização ----
|
|
480
|
+
async function checkTermux() {
|
|
481
|
+
if (platform.isTermux()) {
|
|
482
|
+
const setup = platform.checkTermuxSetup();
|
|
483
|
+
if (!setup.ok) {
|
|
484
|
+
console.log(c.warn('
|
|
485
|
+
⚠ Termux detectado!'));
|
|
486
|
+
console.log(c.muted(' Configurações necessárias:'));
|
|
487
|
+
for (const issue of setup.issues) {
|
|
488
|
+
console.log(c.muted(` • ${issue}`));
|
|
489
|
+
}
|
|
490
|
+
console.log(c.muted('
|
|
491
|
+
Projetos serão salvos em: /storage/shared/primocode/'));
|
|
492
|
+
} else {
|
|
493
|
+
console.log(c.ok('
|
|
494
|
+
✓ Termux configurado!'));
|
|
495
|
+
console.log(c.muted(' Projetos em: /storage/shared/primocode/'));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ---- Verificar projeto existente na inicialização ----
|
|
501
|
+
async function checkExistingProject() {
|
|
502
|
+
const projects = memory.listProjects();
|
|
503
|
+
if (projects.length > 0) {
|
|
504
|
+
console.log(c.muted(`\n 📁 Projetos existentes encontrados:`));
|
|
505
|
+
for (const p of projects) {
|
|
506
|
+
console.log(c.muted(` • ${p}`));
|
|
507
|
+
}
|
|
508
|
+
console.log(c.muted(`\n Use /project <nome> para continuar ou comece um novo projeto.`));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ---- Interactive ----
|
|
513
|
+
function startInteractive() {
|
|
514
|
+
process.stdout.write(welcomeBox(state.config));
|
|
515
|
+
process.stdout.write(mascot.setState('idle'));
|
|
516
|
+
|
|
517
|
+
// Verificar Termux
|
|
518
|
+
checkTermux();
|
|
519
|
+
// Verificar projetos existentes
|
|
520
|
+
checkExistingProject();
|
|
521
|
+
|
|
522
|
+
rl = readline.createInterface({
|
|
523
|
+
input: process.stdin,
|
|
524
|
+
output: process.stdout,
|
|
525
|
+
prompt: makePrompt(),
|
|
526
|
+
historySize: 200,
|
|
527
|
+
});
|
|
528
|
+
rl.prompt();
|
|
529
|
+
|
|
530
|
+
const queue = [];
|
|
531
|
+
let busy = false;
|
|
532
|
+
async function drain() {
|
|
533
|
+
if (busy) return;
|
|
534
|
+
busy = true;
|
|
535
|
+
while (queue.length) {
|
|
536
|
+
const input = queue.shift();
|
|
537
|
+
try {
|
|
538
|
+
if (input.startsWith('/')) await handleSlash(input);
|
|
539
|
+
else await sendPrompt(input);
|
|
540
|
+
} catch (e) { console.log(c.err(`Erro: ${e.message}`)); }
|
|
541
|
+
}
|
|
542
|
+
busy = false;
|
|
543
|
+
rl.prompt();
|
|
544
|
+
}
|
|
545
|
+
rl.on('line', (line) => {
|
|
546
|
+
const input = line.trim();
|
|
547
|
+
if (!input) { if (!busy) rl.prompt(); return; }
|
|
548
|
+
queue.push(input);
|
|
549
|
+
drain();
|
|
550
|
+
});
|
|
551
|
+
rl.on('SIGINT', () => { console.log(c.muted('\nUse /exit para sair')); rl.prompt(); });
|
|
552
|
+
rl.on('close', () => {
|
|
553
|
+
const check = () => {
|
|
554
|
+
if (!busy && queue.length === 0) { console.log(c.muted('\nEncerrado.\n')); process.exit(0); }
|
|
555
|
+
setTimeout(check, 100);
|
|
556
|
+
};
|
|
557
|
+
check();
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// ---- Entry ----
|
|
562
|
+
(async () => {
|
|
563
|
+
const flags = process.argv.slice(2);
|
|
564
|
+
const hasPrompt = flags.some(a => !a.startsWith('-'));
|
|
565
|
+
const helpFlag = flags.some(a => a === '-h' || a === '--help');
|
|
566
|
+
const versionFlag = flags.some(a => a === '-v' || a === '--version');
|
|
567
|
+
if (helpFlag) { printCliHelp(); process.exit(0); }
|
|
568
|
+
if (versionFlag) { console.log('PrimoCode v4.1 — Windows | macOS | Linux | Termux'); process.exit(0); }
|
|
569
|
+
if (hasPrompt) { await runOneShot(process.argv); process.exit(0); }
|
|
570
|
+
startInteractive();
|
|
571
|
+
})().catch((err) => {
|
|
572
|
+
console.error(c.err('Erro fatal: ') + err.message);
|
|
573
|
+
process.exit(1);
|
|
574
|
+
});
|