primocode 8.0.1 → 8.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/primocode.js +12 -0
- package/lib/act.js +7 -2
- package/lib/api.js +13 -1
- package/lib/catalogo.js +5 -0
- package/lib/config.js +13 -1
- package/lib/modo.js +8 -0
- package/lib/tools.js +11 -0
- package/package.json +1 -1
package/bin/primocode.js
CHANGED
|
@@ -24,6 +24,18 @@ const pasta = require('../lib/pasta');
|
|
|
24
24
|
const catalogo = require('../lib/catalogo');
|
|
25
25
|
const effort = require('../lib/effort');
|
|
26
26
|
|
|
27
|
+
// Rede de segurança de último recurso: sem isto, uma exceção fora de um
|
|
28
|
+
// try/catch (ex.: dentro de um callback de socket, como o parser de SSE) não
|
|
29
|
+
// era pega por ninguém e derrubava o processo INTEIRO em silêncio — o usuário
|
|
30
|
+
// via a resposta parar no meio e cair de volta pro shell, sem mensagem
|
|
31
|
+
// nenhuma. Agora vira um erro visível, e o processo continua de pé.
|
|
32
|
+
process.on('uncaughtException', (e) => {
|
|
33
|
+
console.log('\n' + c.err(ui.friendlyError(e)));
|
|
34
|
+
});
|
|
35
|
+
process.on('unhandledRejection', (e) => {
|
|
36
|
+
console.log('\n' + c.err(ui.friendlyError(e instanceof Error ? e : new Error(String(e)))));
|
|
37
|
+
});
|
|
38
|
+
|
|
27
39
|
// A pasta de onde o comando foi dado. Não é mais onde ele trabalha (o padrão é
|
|
28
40
|
// ~/primocode), mas continua servindo de ponto de partida para achar pasta que
|
|
29
41
|
// o usuário mencionar por nome relativo.
|
package/lib/act.js
CHANGED
|
@@ -165,7 +165,7 @@ function buildMemoryContext(projectDir, projectName) {
|
|
|
165
165
|
const geral = cfg.readGeneralMemory();
|
|
166
166
|
if (geral.trim()) parts.push(`[memory-geral.md]\n${geral}`);
|
|
167
167
|
const projeto = cfg.readProjectMemory(projectDir);
|
|
168
|
-
if (projeto.trim()) parts.push(`[${projectName}/memory.md]\n${projeto}`);
|
|
168
|
+
if (projeto.trim()) parts.push(`[${projectName || 'projeto'}/memory.md]\n${projeto}`);
|
|
169
169
|
return parts.join('\n\n');
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -342,7 +342,12 @@ async function runAct(userPrompt, opts) {
|
|
|
342
342
|
// Nada de arquivo mexido = nada para lembrar. E grava em `pastaAtual`, não em
|
|
343
343
|
// `projectDir`: quando a pasta do projeto nasceu no meio do caminho, a memória
|
|
344
344
|
// precisa ir para ela, não para a raiz onde a sessão começou.
|
|
345
|
-
|
|
345
|
+
//
|
|
346
|
+
// `projectName` NÃO entra na condição: ele só rotula a memória no prompt
|
|
347
|
+
// (buildMemoryContext), e sessão sem projeto nomeado (ex.: primeiro pedido
|
|
348
|
+
// antes de /project) tem `projectName` undefined mesmo já tendo mexido em
|
|
349
|
+
// arquivo de verdade — a memória era descartada nesse caso, sempre.
|
|
350
|
+
if (!subagent && temPasta) {
|
|
346
351
|
cfg.appendProjectMemory(pastaAtual, `**Pedido:** ${userPrompt.slice(0, 300)}\n**Resultado:** ${finalText.slice(0, 500)}`);
|
|
347
352
|
}
|
|
348
353
|
|
package/lib/api.js
CHANGED
|
@@ -90,7 +90,19 @@ function requestStream(serverUrl, endpoint, payload, onEvent, token) {
|
|
|
90
90
|
if (!line.startsWith('data: ')) continue;
|
|
91
91
|
let evt;
|
|
92
92
|
try { evt = JSON.parse(line.slice(6)); } catch { continue; }
|
|
93
|
-
onEvent
|
|
93
|
+
// onEvent roda dentro de um callback de socket, fora de
|
|
94
|
+
// qualquer try/catch de quem chamou requestStream. Uma
|
|
95
|
+
// exceção aqui não seria pega por ninguém — sem process.on
|
|
96
|
+
// ('uncaughtException') no CLI, isso derrubava o processo
|
|
97
|
+
// inteiro em silêncio (era o "cai fora" sem mensagem
|
|
98
|
+
// nenhuma). Agora vira rejeição normal da Promise.
|
|
99
|
+
try {
|
|
100
|
+
onEvent(evt);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
settled = true;
|
|
103
|
+
reject(e);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
94
106
|
if (evt.done) finish();
|
|
95
107
|
}
|
|
96
108
|
});
|
package/lib/catalogo.js
CHANGED
|
@@ -91,6 +91,11 @@ const GRUPOS = [
|
|
|
91
91
|
itens: [
|
|
92
92
|
{ tool: 'write_todos', escreve: false, faz: 'monta o plano e vai marcando o que terminou' },
|
|
93
93
|
{ tool: 'pesquisar_referencia', escreve: false, faz: 'lê docs e exemplos antes de escrever — sobe a qualidade' },
|
|
94
|
+
// Vai para memory-geral.md, FORA da pasta do projeto — por isso não
|
|
95
|
+
// é write_file/edit_file (a sandbox de resolveInProject bloqueia
|
|
96
|
+
// qualquer caminho fora do projeto, e é assim mesmo que tem que
|
|
97
|
+
// ser para os outros casos). Esta tool é a única porta pra lá.
|
|
98
|
+
{ tool: 'remember_general', escreve: false, faz: 'grava algo sobre o usuário que vale para todo projeto (memory-geral.md)' },
|
|
94
99
|
// Estas duas o loop do agente atende (lib/act.js), não o tools.execute.
|
|
95
100
|
{ tool: 'spawn_agent', escreve: false, faz: 'chama um subagente para uma parte da tarefa', noLoop: true },
|
|
96
101
|
{ tool: 'finish', escreve: false, faz: 'encerra dizendo o que fez', noLoop: true },
|
package/lib/config.js
CHANGED
|
@@ -151,6 +151,18 @@ function appendProjectMemory(dir, entry) {
|
|
|
151
151
|
} catch {}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// A contraparte de appendProjectMemory, para o que vale em TODOS os projetos
|
|
155
|
+
// (nome do usuário, preferência, decisão de longo prazo). Existia a promessa
|
|
156
|
+
// no prompt — "escreve aqui o que descobrir sobre você" — mas nenhuma função
|
|
157
|
+
// gravava de fato: só ensureStructure() criava o template uma vez.
|
|
158
|
+
function appendGeneralMemory(entry) {
|
|
159
|
+
if (!entry) return;
|
|
160
|
+
try {
|
|
161
|
+
ensureStructure();
|
|
162
|
+
fs.appendFileSync(GENERAL_MEMORY, `\n### ${today()}\n${entry}\n`, 'utf-8');
|
|
163
|
+
} catch {}
|
|
164
|
+
}
|
|
165
|
+
|
|
154
166
|
function loadConfig() {
|
|
155
167
|
ensureStructure();
|
|
156
168
|
let file = {};
|
|
@@ -198,5 +210,5 @@ module.exports = {
|
|
|
198
210
|
CONFIG_DIR, CONFIG_FILE, HISTORY_FILE, ROOT_DIR, GENERAL_MEMORY,
|
|
199
211
|
ensureStructure, slugify,
|
|
200
212
|
createProjectDir, getProjectDir, listProjects, findSimilarProject,
|
|
201
|
-
readGeneralMemory, readProjectMemory, appendProjectMemory,
|
|
213
|
+
readGeneralMemory, readProjectMemory, appendProjectMemory, appendGeneralMemory,
|
|
202
214
|
};
|
package/lib/modo.js
CHANGED
|
@@ -37,6 +37,13 @@ const VERBOS = [
|
|
|
37
37
|
];
|
|
38
38
|
const RE_VERBO = new RegExp(`(^|\\s)(${VERBOS.join('|')})\\b`, 'i');
|
|
39
39
|
|
|
40
|
+
// Pedido sem verbo de comando, só desejo: "quero um jogo da velha", "preciso
|
|
41
|
+
// de uma landing page". Muito comum em português — e sem isto caía direto no
|
|
42
|
+
// "sem pista de tarefa" porque não tem 'criar'/'fazer' escrito. A diferença
|
|
43
|
+
// para RE_CONCEITUAL é que aqui não vem pergunta ("como", "por que", "o que
|
|
44
|
+
// é") — só o pedido do que a pessoa quer que exista.
|
|
45
|
+
const RE_DESEJO = /\b(quero|queria|querendo|gostaria de|necessito de|precis[ao] de|precisava de)\b/i;
|
|
46
|
+
|
|
40
47
|
// Coisas que só existem quando há trabalho de arquivo/máquina envolvido.
|
|
41
48
|
const RE_ARTEFATO = /\.(js|ts|tsx|jsx|html|css|json|md|py|sh|yml|yaml|txt|csv|env)\b|[\w-]+\/[\w-]/i;
|
|
42
49
|
const RE_FERRAMENTA = /\b(navegador|browser|desktop|terminal|shell|git|npm|node|deploy|servidor|localhost|porta \d+|screenshot|print da tela|mouse|teclado|janela|[áa]rea de trabalho|aplicativo|[íi]cone)\b/i;
|
|
@@ -65,6 +72,7 @@ function escolherModo(texto) {
|
|
|
65
72
|
|
|
66
73
|
if (RE_ARTEFATO.test(t)) return { modo: 'primocode', motivo: 'cita arquivo ou caminho' };
|
|
67
74
|
if (RE_VERBO.test(t)) return { modo: 'primocode', motivo: 'pede uma ação' };
|
|
75
|
+
if (RE_DESEJO.test(t)) return { modo: 'primocode', motivo: 'expressa o que quer que exista' };
|
|
68
76
|
if (RE_FERRAMENTA.test(t)) return { modo: 'primocode', motivo: 'menciona ferramenta ou máquina' };
|
|
69
77
|
if (RE_PASTA.test(t)) return { modo: 'primocode', motivo: 'fala de arquivo ou projeto' };
|
|
70
78
|
|
package/lib/tools.js
CHANGED
|
@@ -15,6 +15,7 @@ const browser = require('./browser');
|
|
|
15
15
|
const desktop = require('./desktop');
|
|
16
16
|
const referencia = require('./referencia');
|
|
17
17
|
const studio = require('./studio');
|
|
18
|
+
const cfg = require('./config');
|
|
18
19
|
|
|
19
20
|
const IGNORAR = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.cache', 'vendor']);
|
|
20
21
|
const MAX_ARQUIVOS_VARREDURA = 5000;
|
|
@@ -399,6 +400,16 @@ async function despachar(name, args, ctx) {
|
|
|
399
400
|
paginas: Math.max(1, Math.min(5, Number(args.paginas) || 3)),
|
|
400
401
|
});
|
|
401
402
|
|
|
403
|
+
// memory-geral.md fica FORA da pasta do projeto — write_file/edit_file
|
|
404
|
+
// não alcançam por design (resolveInProject bloqueia ../ de propósito).
|
|
405
|
+
// Esta é a única porta para lá, sem reabrir a sandbox.
|
|
406
|
+
case 'remember_general': {
|
|
407
|
+
const entry = String(args.fato || args.entry || '').trim();
|
|
408
|
+
if (!entry) return { ok: false, error: 'Diga o que lembrar (fato).' };
|
|
409
|
+
cfg.appendGeneralMemory(entry);
|
|
410
|
+
return { ok: true, gravado: entry };
|
|
411
|
+
}
|
|
412
|
+
|
|
402
413
|
// ── Navegador controlado (CDP) — o agente age numa página real ──
|
|
403
414
|
case 'browser_open': {
|
|
404
415
|
const alvo = /^https?:\/\//i.test(args.url || '')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.2",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
|
|
5
5
|
"main": "bin/primocode.js",
|
|
6
6
|
"bin": {
|