primocode 11.1.0 → 11.2.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 +16 -2
- package/bin/primocode-rpc.js +100 -0
- package/lib/agentes-pessoais.js +117 -0
- package/lib/app-server/cli.js +61 -0
- package/lib/app-server/index.js +3 -0
- package/lib/app-server/pareamento.js +61 -0
- package/lib/app-server/protocolo.js +86 -0
- package/lib/app-server/servidor.js +685 -0
- package/lib/app-server/websocket.js +87 -0
- package/lib/avisos.js +64 -0
- package/lib/cli/sessao.js +28 -1
- package/lib/cli-nova.js +237 -0
- package/lib/config/config.js +7 -2
- package/lib/motor/aprovacao.js +114 -0
- package/lib/motor/eventos.js +77 -0
- package/lib/motor/index.js +8 -0
- package/lib/motor/tela-terminal.js +64 -0
- package/lib/motor/turno.js +43 -0
- package/lib/motor/voz.js +65 -0
- package/lib/piloto.js +39 -0
- package/lib/plugin/estudio/criar.js +113 -0
- package/lib/plugin/estudio/historico.js +60 -0
- package/lib/plugin/estudio/index.js +14 -1
- package/lib/plugin/estudio/miniatura.js +56 -0
- package/lib/plugin/video/auto.js +1 -1
- package/lib/plugin/video/catalogo.js +20 -0
- package/lib/plugin/video/mutar.js +28 -0
- package/lib/plugin/video/ops-fx.js +6 -6
- package/lib/plugin/video/ops.js +2 -13
- package/lib/plugin/video/pedido.js +1 -1
- package/lib/plugin/video/zoom.js +1 -1
- package/lib/projetos-meta.js +90 -0
- package/lib/qr.js +209 -0
- package/lib/session/runner/act.js +104 -64
- package/lib/skill/catalogo.js +3 -1
- package/lib/tool/tools.js +6 -0
- package/lib/tui/ui.js +1 -1
- package/package.json +6 -2
|
@@ -40,6 +40,7 @@ const tempos = require('../../tempos');
|
|
|
40
40
|
const preLeitura = require('../../pre-leitura');
|
|
41
41
|
const travado = require('../../travado');
|
|
42
42
|
const auditoria = require('../../auditoria');
|
|
43
|
+
const eventosMotor = require('../../motor/eventos');
|
|
43
44
|
const chamadaTorta = require('../../chamada-torta');
|
|
44
45
|
const idioma = require('../../idioma');
|
|
45
46
|
const entregaveis = require('../../plugin/entregaveis');
|
|
@@ -103,10 +104,14 @@ function trabalhando() { return TRABALHANDO; }
|
|
|
103
104
|
/** O que ficou na caixa sem ser lido — o terminal roda como pedido novo. */
|
|
104
105
|
function sobrasDaCaixa() { return CAIXA.splice(0); }
|
|
105
106
|
/* Esvazia a caixa dentro da conversa. Devolve quantos recados entraram. */
|
|
106
|
-
function entregarRecados(messages, profundidade) {
|
|
107
|
+
function entregarRecados(messages, profundidade, emitir) {
|
|
107
108
|
if (!CAIXA.length) return 0;
|
|
108
109
|
const recados = CAIXA.splice(0);
|
|
109
|
-
for (const r of recados)
|
|
110
|
+
for (const r of recados) {
|
|
111
|
+
const texto = `recado recebido — entrando na tarefa: ${r}`;
|
|
112
|
+
if (emitir) emitir(eventosMotor.montar('pensamento', { profundidade, texto }));
|
|
113
|
+
else fala.pensamento(texto, profundidade);
|
|
114
|
+
}
|
|
110
115
|
messages.push({
|
|
111
116
|
role: 'user',
|
|
112
117
|
content: (recados.length === 1 ? 'MENSAGEM NOVA do usuário, enviada enquanto você trabalhava:\n'
|
|
@@ -258,6 +263,14 @@ function limparParaOModelo(result) {
|
|
|
258
263
|
return limpo;
|
|
259
264
|
}
|
|
260
265
|
|
|
266
|
+
/* Os eventos do subagente, do diretor e do pipeline vão para o mesmo emissor
|
|
267
|
+
do pedido principal (vem no ctx). Sem ele, uma tela de terminal nova. */
|
|
268
|
+
function emitirDe(ctx) {
|
|
269
|
+
if (!ctx.emitir) ctx.emitir = require('../../motor/tela-terminal.js').criar();
|
|
270
|
+
return ctx.emitir;
|
|
271
|
+
}
|
|
272
|
+
const telaDe = (ctx, vista, dados) => emitirDe(ctx)(eventosMotor.montar(vista, { profundidade: ctx.profundidade, ...dados }));
|
|
273
|
+
|
|
261
274
|
function logResult(kind, result, profundidade) {
|
|
262
275
|
const p = fala.resultado(profundidade);
|
|
263
276
|
if (!result) return;
|
|
@@ -920,7 +933,28 @@ async function runActInterno(userPrompt, opts) {
|
|
|
920
933
|
: () => require('../../browser.js').aquecerRevisor(),
|
|
921
934
|
/* Quem é este agente no livro de ações (auditoria.js). */
|
|
922
935
|
rotulo = subagent ? 'subagente' : 'principal',
|
|
936
|
+
/* Para onde vão os eventos do turno (lib/motor/eventos.js). O motor
|
|
937
|
+
não escreve na tela: quem desenha assina. Sem ninguém de fora, nasce
|
|
938
|
+
uma tela de terminal (lib/motor/tela-terminal.js) — a saída de sempre. */
|
|
939
|
+
emitir: emitirDeFora = null,
|
|
923
940
|
} = opts || {};
|
|
941
|
+
const emitir = emitirDeFora || require('../../motor/tela-terminal.js').criar();
|
|
942
|
+
const ev = (vista, dados) => emitir(eventosMotor.montar(vista, { profundidade, ...dados }));
|
|
943
|
+
/* Spinner por evento: a tela decide se gira (terminal) ou se mostra "trabalhando" (app). */
|
|
944
|
+
const girar = (rotulo) => {
|
|
945
|
+
const id = eventosMotor.novoId();
|
|
946
|
+
ev('spinner-inicia', { id, rotulo });
|
|
947
|
+
return {
|
|
948
|
+
set detalhe(v) { ev('spinner-detalhe', { id, detalhe: v }); },
|
|
949
|
+
stop() { ev('spinner-para', { id }); },
|
|
950
|
+
};
|
|
951
|
+
};
|
|
952
|
+
/* O texto que corre na tela conforme chega: mesma interface do fala.fluxo(). */
|
|
953
|
+
const abrirFluxo = (texto) => {
|
|
954
|
+
const id = eventosMotor.novoId();
|
|
955
|
+
ev('fluxo-inicia', { id, texto });
|
|
956
|
+
return { escrever: (t) => ev('fluxo-escreve', { id, texto: t }), fechar: () => ev('fluxo-fecha', { id }) };
|
|
957
|
+
};
|
|
924
958
|
|
|
925
959
|
// A pasta de projeto só nasce quando o agente for mesmo MEXER EM ARQUIVO.
|
|
926
960
|
// Antes ela nascia na primeira tool qualquer — pedir "abre o Chrome" criava
|
|
@@ -968,7 +1002,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
968
1002
|
const t = Date.now();
|
|
969
1003
|
// Com spinner: eram segundos de tela parada entre o último arquivo e o
|
|
970
1004
|
// "pronto", e parecia que tinha travado ali.
|
|
971
|
-
const sp =
|
|
1005
|
+
const sp = girar('abrindo a página escondido para conferir');
|
|
972
1006
|
try {
|
|
973
1007
|
medida = await medirPagina(pastaAtual);
|
|
974
1008
|
} catch { medida = null; } finally { sp.stop(); }
|
|
@@ -977,7 +1011,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
977
1011
|
if (!medida || !medida.testou) return null;
|
|
978
1012
|
avisoDaPagina = medida.limpo ? ''
|
|
979
1013
|
: String(medida.relato || '').split('\n').filter((l) => /·|BRANCO/.test(l)).slice(0, 3).join(' ');
|
|
980
|
-
if (medida.limpo) {
|
|
1014
|
+
if (medida.limpo) { ev('pensamento', { texto: 'a página abriu sem erro' }); return null; }
|
|
981
1015
|
return conferencias >= 2 ? null : medida.relato;
|
|
982
1016
|
};
|
|
983
1017
|
/* ── RODAR OS TESTES ANTES DE DIZER "PRONTO" (lib/conferir.js) ──────
|
|
@@ -1000,7 +1034,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1000
1034
|
const achados = K.procurar([...arquivosDaTarefa], { raiz: pastaAtual });
|
|
1001
1035
|
avisoDaChave = K.relato(achados);
|
|
1002
1036
|
if (!avisoDaChave) return null;
|
|
1003
|
-
|
|
1037
|
+
ev('linha', { texto: ` ⚠ ${avisoDaChave}`, nivel: 'aviso' });
|
|
1004
1038
|
return conferiuChaves >= 2 ? null : `${avisoDaChave} Tire a chave do código: leia de um .env (process.env) ou do servidor, e ponha o .env no .gitignore.`;
|
|
1005
1039
|
};
|
|
1006
1040
|
const conferirTestes = async () => {
|
|
@@ -1022,14 +1056,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1022
1056
|
if (r.ok) {
|
|
1023
1057
|
avisoDosTestes = '';
|
|
1024
1058
|
if (erroQueHavia) notaDoConserto = `O que estava errado: os testes falhavam (${erroQueHavia}); depois do conserto, passaram.`;
|
|
1025
|
-
|
|
1059
|
+
ev('linha', { texto: ` ✓ ${ex.titulo} (${como.comando})`, nivel: 'ok' });
|
|
1026
1060
|
try { checkpoint.marcarFuncionando(pastaAtual, `${como.comando}, pelo agente`); } catch { /* sem ponto pra marcar */ }
|
|
1027
1061
|
return null;
|
|
1028
1062
|
}
|
|
1029
1063
|
avisoDosTestes = `${ex.titulo} ${ex.linhas.slice(0, 2).join(' ')}`.trim();
|
|
1030
1064
|
// a frase que explica (a do Error:, se houver), não a do stack: é a que a pessoa entende
|
|
1031
1065
|
if (!erroQueHavia) erroQueHavia = String(ex.linhas.find((l) => /error|erro|assert|fail|falh/i.test(l)) || ex.linhas[0] || '').trim().slice(0, 140);
|
|
1032
|
-
|
|
1066
|
+
ev('linha', { texto: ` ✗ ${ex.titulo} (${como.comando})`, nivel: 'aviso' });
|
|
1033
1067
|
return rodadasDeTeste >= 2 ? null : `Rodei \`${como.comando}\` e falhou:\n${r.saida}`;
|
|
1034
1068
|
};
|
|
1035
1069
|
/* O ARQUIVO QUE O PEDIDO MANDA GRAVAR (lib/plugin/entregaveis.js): "grave o
|
|
@@ -1098,7 +1132,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1098
1132
|
if (decisao !== 'recusada' && decisao !== 'negada') require('../../plugin/voz-ponte').ferramenta(tc.name, tc.arguments);
|
|
1099
1133
|
const id = auditoria.antes({ agente: rotulo, ferramenta: tc.name, args: tc.arguments, decisao, motivo });
|
|
1100
1134
|
const falhou = auditoria.problema();
|
|
1101
|
-
if (falhou)
|
|
1135
|
+
if (falhou) ev('linha', { texto: `⚠ ${falhou}`, nivel: 'aviso' });
|
|
1102
1136
|
return id;
|
|
1103
1137
|
};
|
|
1104
1138
|
// O que read_file/list_dir devolveram, pela chave da época (repeticao.js).
|
|
@@ -1339,10 +1373,10 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1339
1373
|
}
|
|
1340
1374
|
// Recado digitado durante o passo anterior entra AGORA, antes da ida
|
|
1341
1375
|
// ao modelo — o resultado das ferramentas já está na conversa.
|
|
1342
|
-
if (!subagent) entregarRecados(messages, profundidade);
|
|
1376
|
+
if (!subagent) entregarRecados(messages, profundidade, emitir);
|
|
1343
1377
|
// Tarefa em segundo plano que acabou desde a última volta (lib/fundo.js): o modelo fica sabendo.
|
|
1344
1378
|
if (!subagent) for (const av of require('../../fundo').pegarAvisos()) messages.push({ role: 'user', content: av.texto });
|
|
1345
|
-
const sp =
|
|
1379
|
+
const sp = girar(subagent ? 'subagente trabalhando' : PENSANDO);
|
|
1346
1380
|
|
|
1347
1381
|
const toolCalls = [];
|
|
1348
1382
|
let assistantText = '';
|
|
@@ -1375,7 +1409,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1375
1409
|
resumir: ({ resumo, mensagens }, todos) => comp.resumirComFatos({ resumo, mensagens },
|
|
1376
1410
|
(p) => motor.requestResumo(server, { ...p, token }).then((x) => x && x.resumo), todos),
|
|
1377
1411
|
});
|
|
1378
|
-
if (r.ok)
|
|
1412
|
+
if (r.ok) ev('linha', { texto: ` ⎿ conversa passou de 80% da janela: ${r.resumidas} mensagens viraram resumo (${r.antes} → ${r.depois} tokens)`, nivel: 'dim' });
|
|
1379
1413
|
}
|
|
1380
1414
|
} catch { /* resumo é economia, não requisito */ }
|
|
1381
1415
|
}
|
|
@@ -1396,8 +1430,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1396
1430
|
if (!subagent && !toolCalls.length) {
|
|
1397
1431
|
if (!fluxo && assistantText.length > fala.LIMIAR_PENSAMENTO) {
|
|
1398
1432
|
sp.stop();
|
|
1399
|
-
fluxo =
|
|
1400
|
-
fluxo.escrever(assistantText);
|
|
1433
|
+
fluxo = abrirFluxo(assistantText);
|
|
1401
1434
|
} else if (fluxo) fluxo.escrever(evt.delta);
|
|
1402
1435
|
}
|
|
1403
1436
|
}
|
|
@@ -1429,14 +1462,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1429
1462
|
}
|
|
1430
1463
|
}
|
|
1431
1464
|
if (subagent) return { assistantMessage: { role: 'assistant', content: `Subagente falhou: ${fala.frase(e)}` } };
|
|
1432
|
-
|
|
1465
|
+
ev('problema', { erro: e });
|
|
1433
1466
|
throw e;
|
|
1434
1467
|
}
|
|
1435
1468
|
sp.stop();
|
|
1436
1469
|
emVoo = null;
|
|
1437
1470
|
if (!subagent && !avisouReserva && modeloDaVolta && modeloDaVolta.reserva) {
|
|
1438
1471
|
avisouReserva = true;
|
|
1439
|
-
|
|
1472
|
+
ev('linha', { texto: ` ⎿ ${linhaDaReserva(modeloDaVolta.modelo)}`, nivel: 'dim' });
|
|
1440
1473
|
}
|
|
1441
1474
|
traco(`volta ${voltas} voltou em ${Math.round((Date.now() - tRodada) / 1000)}s · ${toolCalls.map((t) => t.name).join(', ') || 'texto'}${streamError ? ' · erro: ' + String(streamError).slice(0, 160) : ''}`);
|
|
1442
1475
|
tempos.somar(subagent ? 'rodada do subagente' : 'rodada do modelo', Date.now() - tRodada);
|
|
@@ -1519,7 +1552,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1519
1552
|
if (fluxo) fluxo.fechar();
|
|
1520
1553
|
const amigavel = fala.frase(streamError);
|
|
1521
1554
|
if (subagent) return { assistantMessage: { role: 'assistant', content: `Subagente falhou: ${amigavel}` }, concluiu: false };
|
|
1522
|
-
|
|
1555
|
+
ev('problema', { erro: streamError });
|
|
1523
1556
|
// concluiu: false — o plano de passos não pode tratar um erro de
|
|
1524
1557
|
// provedor como resultado e passá-lo adiante como "fato".
|
|
1525
1558
|
return { assistantMessage: { role: 'assistant', content: amigavel }, concluiu: false };
|
|
@@ -1561,7 +1594,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1561
1594
|
if (pendentes) cobrouPlano = true; // este empurrão já cobrou o plano aberto
|
|
1562
1595
|
if (!subagent) {
|
|
1563
1596
|
if (fluxo) fluxo.fechar();
|
|
1564
|
-
else
|
|
1597
|
+
else ev('pensamento', { texto: finalText });
|
|
1565
1598
|
}
|
|
1566
1599
|
messages.push({ role: 'assistant', content: finalText });
|
|
1567
1600
|
messages.push({
|
|
@@ -1582,7 +1615,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1582
1615
|
try { const K = require('../../plugin/kits.js'); if (K.continuaVideo(userPrompt)) falta = K.batidasFaltando(pastaAtual); } catch { /* sem kits */ }
|
|
1583
1616
|
if (falta.length) {
|
|
1584
1617
|
empurroesDoKit++;
|
|
1585
|
-
if (fluxo) fluxo.fechar(); else
|
|
1618
|
+
if (fluxo) fluxo.fechar(); else ev('pensamento', { texto: finalText });
|
|
1586
1619
|
messages.push({ role: 'assistant', content: finalText });
|
|
1587
1620
|
messages.push({ role: 'user', content: `O vídeo ainda não está montado: faltam as batidas ${falta.join(', ')} em motion/plano.json. Não explique — mande agora a batida ${falta[0]}: run_shell "primocode kit batida ${falta[0]} --camadas '[…]'" (o formulário: "primocode kit batida ${falta[0]}"), e siga até o cartão (primocode kit plano).` });
|
|
1588
1621
|
finalText = '';
|
|
@@ -1645,7 +1678,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1645
1678
|
}
|
|
1646
1679
|
if (!subagent) {
|
|
1647
1680
|
if (fluxo) fluxo.fechar();
|
|
1648
|
-
else
|
|
1681
|
+
else ev('resposta', { texto: finalText });
|
|
1649
1682
|
}
|
|
1650
1683
|
/* Ia terminar, mas chegou recado no meio: a tarefa não acaba com
|
|
1651
1684
|
a pessoa esperando resposta. A volta seguinte entrega o recado. */
|
|
@@ -1661,7 +1694,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1661
1694
|
// O que o modelo disse ANTES de agir é pensamento, e sai como tal.
|
|
1662
1695
|
if (!subagent) {
|
|
1663
1696
|
if (fluxo) fluxo.fechar();
|
|
1664
|
-
else if (assistantText.trim())
|
|
1697
|
+
else if (assistantText.trim()) ev('pensamento', { texto: assistantText });
|
|
1665
1698
|
}
|
|
1666
1699
|
|
|
1667
1700
|
// Registra o turno do assistente COM os tool_calls. A API exige que
|
|
@@ -1694,14 +1727,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1694
1727
|
/* O plano decide quantos subagentes rodam JUNTOS (lib/config/limites.js; grátis e
|
|
1695
1728
|
Primo: um de cada vez, o resto continua em fila). Nunca recusa: só diz o porquê. */
|
|
1696
1729
|
const par = spawns.length > 1 ? await limites.paralelo() : null;
|
|
1697
|
-
if (par) { const av = par.aviso(spawns.length); if (av && !avisouParalelo) { avisouParalelo = true;
|
|
1730
|
+
if (par) { const av = par.aviso(spawns.length); if (av && !avisouParalelo) { avisouParalelo = true; ev('linha', { texto: ` ${av}` }); } }
|
|
1698
1731
|
if (spawns.length > 1 && par.max > 1 && !PARAR) {
|
|
1699
1732
|
const juntos = spawns.slice(0, par.max);
|
|
1700
|
-
|
|
1733
|
+
ev('paralelo', { quantos: juntos.length });
|
|
1701
1734
|
const ids = juntos.map((tc) => anotar(tc, 'permitida'));
|
|
1702
1735
|
const rs = await Promise.all(juntos.map((tc) =>
|
|
1703
1736
|
runSubagent(tc.arguments || {}, {
|
|
1704
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1737
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir,
|
|
1705
1738
|
}).catch((e) => `Subagente falhou: ${e && e.message}`)));
|
|
1706
1739
|
juntos.forEach((tc, i) => { resultadosSpawn.set(tc.id, rs[i]); auditoria.depois(ids[i], { ok: true }); });
|
|
1707
1740
|
}
|
|
@@ -1824,7 +1857,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1824
1857
|
usuário (o bin). Sem a resposta dele, não troca. */
|
|
1825
1858
|
if (tc.name === 'mudar_pasta') {
|
|
1826
1859
|
algoRodou = true;
|
|
1827
|
-
|
|
1860
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1828
1861
|
const idA = anotar(tc, 'permitida');
|
|
1829
1862
|
const r = await require('../../outra-pasta.js').pedirTroca(tc.arguments || {}, {
|
|
1830
1863
|
atual: pastaAtual, subagent, trocar: mudarPasta,
|
|
@@ -1834,7 +1867,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1834
1867
|
criarPasta = null;
|
|
1835
1868
|
temPasta = true;
|
|
1836
1869
|
}
|
|
1837
|
-
|
|
1870
|
+
ev('resultado', { nome: tc.name, resultado: r });
|
|
1838
1871
|
auditoria.depois(idA, r);
|
|
1839
1872
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1840
1873
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
@@ -1847,7 +1880,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1847
1880
|
const r = resultadosSpawn.has(tc.id)
|
|
1848
1881
|
? resultadosSpawn.get(tc.id)
|
|
1849
1882
|
: await runSubagent(tc.arguments || {}, {
|
|
1850
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1883
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir,
|
|
1851
1884
|
});
|
|
1852
1885
|
if (idA) auditoria.depois(idA, { ok: true });
|
|
1853
1886
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
@@ -1859,12 +1892,12 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1859
1892
|
nova, e quem sabe abrir conversa é este arquivo. */
|
|
1860
1893
|
if (tc.name === 'planejar_producao') {
|
|
1861
1894
|
algoRodou = true;
|
|
1862
|
-
|
|
1895
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1863
1896
|
const idA = anotar(tc, 'permitida');
|
|
1864
1897
|
const r = await rodarDiretor(tc.arguments || {}, {
|
|
1865
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1898
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir,
|
|
1866
1899
|
});
|
|
1867
|
-
|
|
1900
|
+
ev('resultado', { nome: tc.name, resultado: r });
|
|
1868
1901
|
auditoria.depois(idA, r);
|
|
1869
1902
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
1870
1903
|
continue;
|
|
@@ -1879,12 +1912,12 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1879
1912
|
if (tc.name === 'construir_app') {
|
|
1880
1913
|
algoRodou = true;
|
|
1881
1914
|
garantirPasta();
|
|
1882
|
-
|
|
1915
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1883
1916
|
const idA = anotar(tc, 'permitida');
|
|
1884
1917
|
const r = await rodarPipeline(tc.arguments || {}, {
|
|
1885
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, livro, autorizar, controle: portaoControle,
|
|
1918
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir, livro, autorizar, controle: portaoControle,
|
|
1886
1919
|
});
|
|
1887
|
-
|
|
1920
|
+
ev('resultado', { nome: tc.name, resultado: r });
|
|
1888
1921
|
auditoria.depois(idA, r);
|
|
1889
1922
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1890
1923
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
@@ -1957,8 +1990,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1957
1990
|
igual = !!e && voltas - e.volta <= 1 && require('fs').readFileSync(abs, 'utf8') === e.texto;
|
|
1958
1991
|
} catch { igual = false; }
|
|
1959
1992
|
if (igual) {
|
|
1960
|
-
|
|
1961
|
-
|
|
1993
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1994
|
+
ev('resultado-linha', { texto: 'igual ao que ele escreveu — não precisou ler' });
|
|
1962
1995
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({
|
|
1963
1996
|
ok: true, igualAoQueVoceEscreveu: true,
|
|
1964
1997
|
aviso: 'Este arquivo está EXATAMENTE como você o escreveu nesta tarefa — o conteúdo é o da sua '
|
|
@@ -1990,8 +2023,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1990
2023
|
const porta = await permissao.pedir(portaoControle, tc.name, tc.arguments, autorizar);
|
|
1991
2024
|
if (!porta.ok) {
|
|
1992
2025
|
anotar(tc, 'recusada', porta.error);
|
|
1993
|
-
|
|
1994
|
-
|
|
2026
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2027
|
+
ev('resultado-linha', { texto: 'não autorizado', nivel: 'aviso' });
|
|
1995
2028
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: porta.error });
|
|
1996
2029
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: porta.error }) });
|
|
1997
2030
|
continue;
|
|
@@ -2008,8 +2041,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2008
2041
|
});
|
|
2009
2042
|
if (!risco.ok) {
|
|
2010
2043
|
anotar(tc, 'recusada', risco.motivo || risco.error);
|
|
2011
|
-
|
|
2012
|
-
|
|
2044
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2045
|
+
ev('resultado-linha', { texto: `não autorizado — ${risco.motivo}`, nivel: 'aviso' });
|
|
2013
2046
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: risco.error });
|
|
2014
2047
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: risco.error }) });
|
|
2015
2048
|
continue;
|
|
@@ -2019,8 +2052,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2019
2052
|
const gancho = hooks.rodar('PreToolUse', { nome: tc.name, args: tc.arguments, projectDir: pastaAtual });
|
|
2020
2053
|
if (gancho.bloqueado) {
|
|
2021
2054
|
anotar(tc, 'recusada', gancho.motivo);
|
|
2022
|
-
|
|
2023
|
-
|
|
2055
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2056
|
+
ev('resultado-linha', { texto: `bloqueado por hook — ${gancho.motivo.slice(0, 80)}`, nivel: 'aviso' });
|
|
2024
2057
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: gancho.motivo });
|
|
2025
2058
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: `Bloqueado por hook: ${gancho.motivo}` }) });
|
|
2026
2059
|
continue;
|
|
@@ -2031,13 +2064,13 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2031
2064
|
if (['run_shell', 'run_in_new_terminal', 'run_background'].includes(tc.name) && perigo.apagaBancoNoComando((tc.arguments || {}).command)) {
|
|
2032
2065
|
try {
|
|
2033
2066
|
const copia = require('../../backup-banco.js').fazer(pastaAtual);
|
|
2034
|
-
if (copia)
|
|
2067
|
+
if (copia) ev('linha', { texto: ` ⎿ fiz uma cópia do banco antes (${copia.arquivos.join(', ')}) · /undo devolve`, nivel: 'ok' });
|
|
2035
2068
|
} catch { /* sem cópia o comando segue: ela é rede de segurança, não condição */ }
|
|
2036
2069
|
}
|
|
2037
2070
|
|
|
2038
2071
|
algoRodou = true;
|
|
2039
2072
|
rodouTool = true;
|
|
2040
|
-
|
|
2073
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2041
2074
|
/* Antes de mexer no arquivo, guarda o original — é o que o /desfazer
|
|
2042
2075
|
devolve. Caminho inválido não é problema nosso: a tool recusa. */
|
|
2043
2076
|
let antesDaEdicao = null, arquivoDaEdicao = null; // pro alarme do arquivo que encolheu (lib/session/encolheu.js)
|
|
@@ -2052,7 +2085,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2052
2085
|
catch (e) {
|
|
2053
2086
|
// Caminho inválido a tool recusa sozinha. O resto (disco cheio, sem permissão)
|
|
2054
2087
|
// quer dizer que o /desfazer não vai devolver este arquivo: a pessoa tem que saber.
|
|
2055
|
-
if (tc.arguments && tc.arguments.path)
|
|
2088
|
+
if (tc.arguments && tc.arguments.path) ev('linha', { texto: ` ⚠ não guardei o original de ${tc.arguments.path}: o /undo não vai voltar este arquivo (${e.message})`, nivel: 'aviso' });
|
|
2056
2089
|
}
|
|
2057
2090
|
}
|
|
2058
2091
|
/* O servidor, o token e o cliente da API vão junto: a legenda do
|
|
@@ -2061,7 +2094,9 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2061
2094
|
traco(`executando ${tc.name}`);
|
|
2062
2095
|
const idAcao = idsAdiantados.get(tc.id) || anotar(tc, 'permitida');
|
|
2063
2096
|
const result = adiantadas.has(tc.id) ? adiantadas.get(tc.id)
|
|
2064
|
-
: await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual, server, token, api, escolher: subagent ? undefined : escolher
|
|
2097
|
+
: await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual, server, token, api, escolher: subagent ? undefined : escolher,
|
|
2098
|
+
// estudio_criar conta o andamento: a tela desenha a barra do item `estudio`.
|
|
2099
|
+
progresso: tc.name === 'estudio_criar' ? (pct, etapa) => ev('estudio-progresso', { pct, etapa }) : undefined });
|
|
2065
2100
|
auditoria.depois(idAcao, result);
|
|
2066
2101
|
if (sinais && result && result.ok && /^(write_file|edit_file|multi_edit)$/.test(tc.name)) sinais.diagSujo = !!result.diagnosticos; // diagnóstico depois da edição
|
|
2067
2102
|
if (antesDaEdicao != null && result && result.ok !== false) {
|
|
@@ -2069,19 +2104,21 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2069
2104
|
try { depois = require('fs').readFileSync(arquivoDaEdicao, 'utf8'); } catch { depois = null; }
|
|
2070
2105
|
const encolheu = require('../encolheu.js').julgar(antesDaEdicao, depois);
|
|
2071
2106
|
if (encolheu) {
|
|
2072
|
-
|
|
2107
|
+
ev('linha', { texto: ` ⚠ ${tc.arguments.path}: ${encolheu.texto}. Se sumiu código sem querer, /undo volta.`, nivel: 'aviso' });
|
|
2073
2108
|
result.aviso_encolheu = `ATENÇÃO: ${tc.arguments.path} ${encolheu.texto}. Confira se você não apagou código sem querer; se apagou, refaça a edição sem cortar nada.`;
|
|
2074
2109
|
}
|
|
2075
2110
|
}
|
|
2076
|
-
for (const aviso of hooks.rodar('PostToolUse', { nome: tc.name, args: tc.arguments, resultado: result, projectDir: pastaAtual }).avisos)
|
|
2111
|
+
for (const aviso of hooks.rodar('PostToolUse', { nome: tc.name, args: tc.arguments, resultado: result, projectDir: pastaAtual }).avisos) ev('linha', { texto: ` ⎿ ${aviso}`, nivel: 'dim' });
|
|
2077
2112
|
traco(`${tc.name} devolveu ok=${result && result.ok}`);
|
|
2078
2113
|
if (/^estudio_|^planejar_producao$/.test(tc.name) && result && result.ok !== false) {
|
|
2079
2114
|
const id = result.id || (result.projeto && result.projeto.id) || (tc.arguments && tc.arguments.id);
|
|
2080
2115
|
if (typeof id === 'string' && id) estudioDaTarefa = id;
|
|
2081
2116
|
}
|
|
2082
|
-
|
|
2117
|
+
ev('resultado', { nome: tc.name, resultado: result });
|
|
2118
|
+
// Render é lento e roda solto: ao fim, o sino avisa (vira notice/created no app).
|
|
2119
|
+
if (tc.name === 'estudio_criar' && result && result.ok !== false && result.arquivo) ev('aviso', { texto: `${result.resumo || 'Pronto'} ${require('path').basename(result.arquivo)}` });
|
|
2083
2120
|
if (/^estudio_render/.test(tc.name) && result && result.conferencia && !subagent) {
|
|
2084
|
-
for (const l of require('../../plugin/estudio/conferencia.js').linhasPraPessoa(result.conferencia))
|
|
2121
|
+
for (const l of require('../../plugin/estudio/conferencia.js').linhasPraPessoa(result.conferencia)) ev('linha', { texto: ` ⎿ ${l}`, nivel: 'aviso' });
|
|
2085
2122
|
}
|
|
2086
2123
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
2087
2124
|
if (RELEITURA.has(tc.name) && result && result.ok !== false) {
|
|
@@ -2202,7 +2239,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2202
2239
|
|
|
2203
2240
|
if (finished) {
|
|
2204
2241
|
finalText = finished;
|
|
2205
|
-
if (!subagent)
|
|
2242
|
+
if (!subagent) ev('resposta', { texto: finished });
|
|
2206
2243
|
// Recado chegou enquanto ele fechava: entrega e segue.
|
|
2207
2244
|
if (!subagent && CAIXA.length && !PARAR) { finalText = ''; continue; }
|
|
2208
2245
|
break;
|
|
@@ -2220,14 +2257,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2220
2257
|
let passagemDaParada = null;
|
|
2221
2258
|
if (paradoPorTravar && !finalText) {
|
|
2222
2259
|
finalText = paradoPorTravar;
|
|
2223
|
-
if (!subagent)
|
|
2260
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2224
2261
|
// Parou sem terminar: a memória leva o estado, não só "parei" (lib/passagem.js).
|
|
2225
2262
|
passagemDaParada = require('../../passagem').montar({ pedido: userPrompt, motivo: finalText, registro: livro, falhasPendentes });
|
|
2226
2263
|
}
|
|
2227
2264
|
|
|
2228
2265
|
if (PARAR && !finalText) {
|
|
2229
2266
|
finalText = 'Parado por você (Esc Esc). O que já foi feito, ficou feito.';
|
|
2230
|
-
if (!subagent)
|
|
2267
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2231
2268
|
}
|
|
2232
2269
|
|
|
2233
2270
|
// Nunca terminar em silêncio. Quando o modelo devolvia resposta vazia sem
|
|
@@ -2236,7 +2273,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2236
2273
|
// depois de a primeira tentativa de seletor falhar.
|
|
2237
2274
|
if (!finalText) {
|
|
2238
2275
|
finalText = motivoDaParada({ voltas, voltasEmFalso, falhasPendentes, travou: rodadasParadas >= MAX_RODADAS_PARADAS, teto });
|
|
2239
|
-
if (!subagent)
|
|
2276
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2240
2277
|
passagemDaParada = require('../../passagem').montar({ pedido: userPrompt, motivo: finalText, registro: livro, falhasPendentes });
|
|
2241
2278
|
}
|
|
2242
2279
|
|
|
@@ -2299,11 +2336,13 @@ async function runSubagent(args, ctx) {
|
|
|
2299
2336
|
// O ofício: o modelo diz quem faz (`especialista`), ou a tarefa sugere. O
|
|
2300
2337
|
// briefing vai na frente da tarefa, na mesma mensagem — ver especialistas.js.
|
|
2301
2338
|
// Novo catálogo (agentes/): "id" ou "id1,id2" (até 3 olhares na mesma tarefa); o que não existe cai no legado.
|
|
2302
|
-
|
|
2303
|
-
const
|
|
2339
|
+
// `agente`: um agente da pessoa (~/.primocode/agentes, lib/agentes-pessoais.js) ganha do catálogo e do legado.
|
|
2340
|
+
const meu = args.agente ? require('../../agentes-pessoais').ler(args.agente) : null;
|
|
2341
|
+
const novos = meu ? [] : require('../../agentes').refinar(args.especialista, args.prompt); // o catálogo confere o chute do modelo
|
|
2342
|
+
const esp = meu ? require('../../agentes-pessoais').comoEspecialista(meu) : novos.length ? null : especialistas.escolher(args.especialista, args.prompt);
|
|
2304
2343
|
const agentes = require('../../agentes');
|
|
2305
|
-
const rot = novos.length ? agentes.rotulo(novos) : especialistas.rotulo(esp);
|
|
2306
|
-
|
|
2344
|
+
const rot = meu ? meu.nome : novos.length ? agentes.rotulo(novos) : especialistas.rotulo(esp);
|
|
2345
|
+
telaDe(ctx, 'agente', { rotulo: rot, descricao });
|
|
2307
2346
|
|
|
2308
2347
|
// modelo_sugerido do briefing vale quando o placar ainda não tem dado desse tipo de tarefa.
|
|
2309
2348
|
let modeloDoAgente = model;
|
|
@@ -2322,6 +2361,7 @@ async function runSubagent(args, ctx) {
|
|
|
2322
2361
|
subagent: true,
|
|
2323
2362
|
motor,
|
|
2324
2363
|
permissoes,
|
|
2364
|
+
emitir: emitirDe(ctx),
|
|
2325
2365
|
autorizar: ctx.autorizar,
|
|
2326
2366
|
controle: ctx.controle, // o mesmo objeto: a resposta vale para todos
|
|
2327
2367
|
rotulo: `subagente: ${String(descricao).slice(0, 60)}`,
|
|
@@ -2334,7 +2374,7 @@ async function runSubagent(args, ctx) {
|
|
|
2334
2374
|
if (ctx.livro && r.registro) registro.juntar(ctx.livro, r.registro);
|
|
2335
2375
|
|
|
2336
2376
|
const resumo = r.assistantMessage.content || 'sem resultado';
|
|
2337
|
-
|
|
2377
|
+
telaDe(ctx, 'agente-voltou', { texto: resumo });
|
|
2338
2378
|
return entregaDoAgente(rot, resumo, r.registro, { pasta: projectDir });
|
|
2339
2379
|
}
|
|
2340
2380
|
|
|
@@ -2417,10 +2457,10 @@ async function rodarDiretor(args, ctx) {
|
|
|
2417
2457
|
const diretor = require('../../plugin/estudio/diretor.js');
|
|
2418
2458
|
const correr = async (prompt, rotulo) => {
|
|
2419
2459
|
if (PARAR) return '';
|
|
2420
|
-
|
|
2460
|
+
telaDe(ctx, 'agente', { rotulo: 'Diretor', descricao: rotulo });
|
|
2421
2461
|
const r = await runAct(prompt, {
|
|
2422
2462
|
server, model, token, projectDir, projectName,
|
|
2423
|
-
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes,
|
|
2463
|
+
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes, emitir: emitirDe(ctx),
|
|
2424
2464
|
});
|
|
2425
2465
|
return (r && r.assistantMessage && r.assistantMessage.content) || '';
|
|
2426
2466
|
};
|
|
@@ -2431,7 +2471,7 @@ async function rodarDiretor(args, ctx) {
|
|
|
2431
2471
|
formato: args.formato === 'apresentacao' ? 'apresentacao' : 'video',
|
|
2432
2472
|
contexto: args.contexto || '',
|
|
2433
2473
|
correr,
|
|
2434
|
-
mostrar: (t) =>
|
|
2474
|
+
mostrar: (t) => telaDe(ctx, 'agente', { rotulo: 'Diretor', descricao: t }),
|
|
2435
2475
|
});
|
|
2436
2476
|
if (!r.ok) return r;
|
|
2437
2477
|
|
|
@@ -2473,10 +2513,10 @@ async function rodarPipeline(args, ctx) {
|
|
|
2473
2513
|
|
|
2474
2514
|
const correr = async (prompt, rotulo) => {
|
|
2475
2515
|
if (PARAR) return '';
|
|
2476
|
-
|
|
2516
|
+
telaDe(ctx, 'agente', { rotulo: 'Agente', descricao: rotulo });
|
|
2477
2517
|
const r = await runAct(prompt, {
|
|
2478
2518
|
server, model, token, projectDir: pasta, projectName,
|
|
2479
|
-
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes,
|
|
2519
|
+
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes, emitir: emitirDe(ctx),
|
|
2480
2520
|
});
|
|
2481
2521
|
return (r && r.assistantMessage && r.assistantMessage.content) || '';
|
|
2482
2522
|
};
|
|
@@ -2486,7 +2526,7 @@ async function rodarPipeline(args, ctx) {
|
|
|
2486
2526
|
pasta,
|
|
2487
2527
|
rodadas: args.rodadas,
|
|
2488
2528
|
correr,
|
|
2489
|
-
mostrar: (linha) =>
|
|
2529
|
+
mostrar: (linha) => telaDe(ctx, 'resultado-linha', { texto: linha }),
|
|
2490
2530
|
abrir: tools.openInBrowser,
|
|
2491
2531
|
});
|
|
2492
2532
|
}
|
|
@@ -2518,5 +2558,5 @@ function novoPortaoDeProva() {
|
|
|
2518
2558
|
};
|
|
2519
2559
|
}
|
|
2520
2560
|
|
|
2521
|
-
module.exports = { summarizeForModel, mesmoComando, EDICAO_DE_VIDEO_FALADO, novoPortaoDeProva, runAct, erroDaCorrente, linhaDaReserva, videoForaDeEdicao, entregaDoAgente, lembreteDeParalelo, buildMemoryContext, leituraPeloShell, historicoEnxuto, interromper, interrompido, mandarDuranteATarefa, trabalhando, sobrasDaCaixa, entregarRecados, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
2561
|
+
module.exports = { summarizeForModel, mesmoComando, EDICAO_DE_VIDEO_FALADO, novoPortaoDeProva, runAct, logResult, interrompido: () => PARAR, erroDaCorrente, linhaDaReserva, videoForaDeEdicao, entregaDoAgente, lembreteDeParalelo, buildMemoryContext, leituraPeloShell, historicoEnxuto, interromper, interrompido, mandarDuranteATarefa, trabalhando, sobrasDaCaixa, entregarRecados, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
2522
2562
|
anotacaoDoQueJaFoi, resumoDoQueJaFoi, encolherParaContinuar, recadoDoPlanoAberto, MAX_ITERATIONS };
|
package/lib/skill/catalogo.js
CHANGED
|
@@ -82,6 +82,8 @@ const GRUPOS = [
|
|
|
82
82
|
nome: 'Estúdio',
|
|
83
83
|
resumo: 'vídeo, motion, slides e arte — a IA faz, e no fim abre o resultado pronto',
|
|
84
84
|
itens: [
|
|
85
|
+
{ tool: 'estudio_criar', escreve: true, faz: 'a porta simples: cria slides, vídeo, motion, anúncio ou abertura do pedido ao arquivo, e abre o resultado',
|
|
86
|
+
args: 'tipo (slides|video|motion|anuncio|abertura). briefing (uma frase). duracao (s, opcional). formato (9:16|16:9|1:1, opcional). Para controle fino use as outras estudio_*.' },
|
|
85
87
|
{ tool: 'estudio_projeto', escreve: true, faz: 'abre, acha e lista os projetos do estúdio — cada peça é uma pasta que não some',
|
|
86
88
|
args: 'acao: criar | abrir | procurar | listar | anotar | editar | assinatura. '
|
|
87
89
|
+ 'assinatura: quando o usuário diz o que achou de um vídeo ("adorei a virada", "texto demais"), grave com texto (e o id) — o próximo vídeo parte disso; sem texto, lê as faixas e as notas. '
|
|
@@ -282,7 +284,7 @@ const GRUPOS = [
|
|
|
282
284
|
+ 'Para só LER lá não precisa trocar: read_file/list_dir com o caminho absoluto, glob/grep com padrão absoluto.' },
|
|
283
285
|
// Estas duas o loop do agente atende (lib/session/runner/act.js), não o tools.execute.
|
|
284
286
|
{ tool: 'spawn_agent', escreve: false, faz: 'chama um subagente especialista para uma parte da tarefa', noLoop: true,
|
|
285
|
-
args: 'description: 3 a 5 palavras, aparece na tela do usuário. prompt: instrução completa e autossuficiente — o subagente não vê esta conversa. especialista (opcional): id EXATO que agentes_buscar devolveu — não chute o nome (o mais específico vence: css-layout, pagamentos, kubernetes, regex…, não frontend/backend/devops) ou dois separados por vírgula — cada um chega com o briefing da profissão (lib/skill/especialistas.js); sem ele, a tarefa sugere.' },
|
|
287
|
+
args: 'description: 3 a 5 palavras, aparece na tela do usuário. prompt: instrução completa e autossuficiente — o subagente não vê esta conversa. especialista (opcional): id EXATO que agentes_buscar devolveu — não chute o nome (o mais específico vence: css-layout, pagamentos, kubernetes, regex…, não frontend/backend/devops) ou dois separados por vírgula — cada um chega com o briefing da profissão (lib/skill/especialistas.js); sem ele, a tarefa sugere. agente (opcional): nome de um agente da pessoa (@nome); ganha do especialista.' },
|
|
286
288
|
{ tool: 'construir_app', escreve: true, noLoop: true,
|
|
287
289
|
faz: 'constrói um app ou site inteiro com quatro papéis: engenheiro de prompt → protótipo → MVP → revisor que testa escondido, em laço até passar',
|
|
288
290
|
args: 'pedido: o que o usuário quer, com as palavras dele. pasta (opcional): onde construir — sem ela, uma pasta nova com o nome do pedido. rodadas (opcional, 1 a 4, padrão 2): quantas voltas de correção. '
|
package/lib/tool/tools.js
CHANGED
|
@@ -943,6 +943,12 @@ async function despachar(name, args, ctx) {
|
|
|
943
943
|
return estudio.ferramentaMidia(args);
|
|
944
944
|
case 'estudio_ae':
|
|
945
945
|
return estudio.ferramentaAE(args);
|
|
946
|
+
case 'estudio_criar':
|
|
947
|
+
// A porta simples: abre o projeto e chama plano, motion e render por dentro. ctx.progresso alimenta a tela.
|
|
948
|
+
return estudio.ferramentaCriar(args, {
|
|
949
|
+
...ctx, projectDir,
|
|
950
|
+
abrir: (arquivo) => { try { openInBrowser(arquivo); } catch {} },
|
|
951
|
+
});
|
|
946
952
|
case 'estudio_motion':
|
|
947
953
|
return estudio.ferramentaMotion(args);
|
|
948
954
|
case 'estudio_editar':
|
package/lib/tui/ui.js
CHANGED
|
@@ -566,7 +566,7 @@ const ROTULOS = {
|
|
|
566
566
|
estudio_site:'Estúdio·marca',
|
|
567
567
|
desktop_windows:'Desktop·janelas', desktop_focus:'Desktop·foco',
|
|
568
568
|
desktop_elements:'Desktop·elementos', desktop_click_element:'Desktop·click',
|
|
569
|
-
estudio_midia:'Estúdio·mídia', estudio_ae:'Estúdio·After Effects', estudio_motion:'Estúdio·Motion', estudio_editar:'Estúdio·Editar', construir_app:'Construir',
|
|
569
|
+
estudio_midia:'Estúdio·mídia', estudio_ae:'Estúdio·After Effects', estudio_motion:'Estúdio·Motion', estudio_editar:'Estúdio·Editar', estudio_criar:'Estúdio·Criar', construir_app:'Construir',
|
|
570
570
|
remember_general:'Lembrar', lembrar:'Memória',
|
|
571
571
|
mcp_procurar:'MCP·procurar', mcp_conectar:'MCP·conectar', mcp_usar:'MCP',
|
|
572
572
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.2.0",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Requer conta Conecta Primo AI (Premium ou Super). 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": {
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
"start": "node bin/primocode.js",
|
|
11
11
|
"postinstall": "node lib/preparar.js || true",
|
|
12
12
|
"test:server": "cd server && npm test",
|
|
13
|
-
"test": "node scripts/testes.js"
|
|
13
|
+
"test": "node scripts/testes.js",
|
|
14
|
+
"test:app": "node test/app-fluxo.test.mjs && node test/app-primeira.test.mjs && node test/app-entrar.test.mjs",
|
|
15
|
+
"app:build": "cd app && npm run dist",
|
|
16
|
+
"app:dev": "cd app && npm start",
|
|
17
|
+
"mobile:build": "cd mobile && npm run build"
|
|
14
18
|
},
|
|
15
19
|
"engines": {
|
|
16
20
|
"node": ">=18.17.0"
|