primocode 11.3.3 → 11.5.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/LICENSES/edit2-terceiros.md +31 -0
- package/LICENSES/models-dev-MIT.txt +25 -0
- package/README.md +2 -2
- package/editor/SKILL.md +24 -0
- package/editor/THIRD_PARTY_NOTICES.md +1 -0
- package/editor/assets/baixar.json +9 -1
- package/editor/helpers/df_limpar.py +29 -0
- package/editor/helpers/edit2.py +155 -0
- package/editor/helpers/enquadrar_camera.py +77 -0
- package/editor/helpers/face_layout.py +20 -0
- package/editor/helpers/face_mediapipe.py +147 -0
- package/editor/helpers/instalar_df.sh +7 -0
- package/editor/helpers/legenda_ass.py +124 -0
- package/editor/helpers/look_ffmpeg.py +105 -0
- package/editor/helpers/motor_palavras.py +157 -0
- package/editor/helpers/primeiro_passe.py +214 -0
- package/editor/helpers/som_edit2.py +119 -0
- package/editor/helpers/transcribe.py +31 -2
- package/editor/pyproject.toml +9 -0
- package/editor/references/edit2.md +22 -0
- package/editor/uv.lock +3342 -214
- package/kits/3d/LICENSES/README.md +11 -0
- package/kits/3d/animacoes.js +87 -0
- package/kits/3d/base.js +44 -0
- package/kits/3d/catalogo.js +38 -0
- package/kits/3d/cenario.js +162 -0
- package/kits/3d/exemplos/asfalto.mjs +91 -0
- package/kits/3d/maquinas.js +97 -0
- package/kits/3d/montador.js +142 -0
- package/kits/3d/package.json +1 -0
- package/kits/3d/personagem.js +86 -0
- package/kits/3d/remotion/Maquete.jsx +42 -0
- package/kits/3d/remotion/Root.jsx +10 -0
- package/kits/3d/render.mjs +73 -0
- package/lib/cli/comando-api.js +30 -8
- package/lib/cli/modelos-lista.js +58 -0
- package/lib/cli/sessao.js +11 -0
- package/lib/cli/subcomandos.js +3 -2
- package/lib/outra-pasta.js +1 -1
- package/lib/permissao-sistema.js +60 -0
- package/lib/permission/perigo.js +2 -1
- package/lib/permission/permissao.js +1 -0
- package/lib/permission/sem-perguntar.js +29 -0
- package/lib/plugin/desktop.js +5 -4
- package/lib/plugin/estudio/cota.js +4 -2
- package/lib/plugin/estudio/dialogo3d.js +71 -0
- package/lib/plugin/estudio/effectcraft.js +1 -1
- package/lib/plugin/estudio/glifos.js +2 -0
- package/lib/plugin/estudio/gsap-local.js +43 -0
- package/lib/plugin/estudio/index.js +63 -10
- package/lib/plugin/estudio/kit-ae/base.jsx +1 -1
- package/lib/plugin/estudio/kit-ae/cenas.jsx +7 -4
- package/lib/plugin/estudio/kit-ae/destrava.jsx +211 -0
- package/lib/plugin/estudio/maquina-diretor.js +1 -1
- package/lib/plugin/estudio/maquina-elementos.js +2 -2
- package/lib/plugin/estudio/maquina.js +26 -8
- package/lib/plugin/estudio/motion-plano/ajustar.js +139 -0
- package/lib/plugin/estudio/motion-plano/critico.js +59 -0
- package/lib/plugin/estudio/motion-plano/estrutura.js +111 -0
- package/lib/plugin/estudio/motion-plano/fatos.js +137 -0
- package/lib/plugin/estudio/motion-plano/index.js +75 -0
- package/lib/plugin/estudio/motion-plano/llm.js +59 -0
- package/lib/plugin/estudio/motion-plano/motor.js +50 -0
- package/lib/plugin/estudio/motion-plano/passos.js +38 -0
- package/lib/plugin/estudio/motion-plano/pele.js +36 -0
- package/lib/plugin/estudio/motion-plano/plano.js +152 -0
- package/lib/plugin/estudio/motion-plano/regua.js +100 -0
- package/lib/plugin/estudio/personagem.js +3 -1
- package/lib/plugin/estudio/referencias.js +1 -1
- package/lib/provider/catalogo.js +161 -0
- package/lib/provider/local/catalogo-embutido.json.gz +0 -0
- package/lib/provider/local/cerebro.json +1 -1
- package/lib/provider/local/entrada.js +10 -0
- package/lib/provider/local/motor.js +140 -27
- package/lib/provider/local/orcamento.js +193 -0
- package/lib/provider/local/provedor.js +127 -14
- package/lib/provider/local/recorte.js +170 -0
- package/lib/provider/local/revezamento.js +121 -0
- package/lib/session/runner/act.js +28 -3
- package/lib/skill/catalogo.js +9 -1
- package/lib/tool/reparar-entrada.js +1 -0
- package/lib/tool/tools.js +58 -1
- package/lib/tui/ui.js +1 -1
- package/package.json +2 -2
- package/vendor/fontes/ttf/ORIGEM.txt +1 -0
- package/vendor/fontes/ttf/inter-800.ttf +0 -0
- package/vendor/gsap/ORIGEM.txt +6 -0
- package/vendor/gsap/gsap.min.js +11 -0
- package/voz/primo_voz/chat.html +206 -5
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recorte.js — quais ferramentas o pedido precisa, por assunto.
|
|
3
|
+
*
|
|
4
|
+
* Reaproveita os gatilhos de entrada.js (os mesmos do servidor) e completa o
|
|
5
|
+
* que o cliente tem a mais: cada ferramenta cai num assunto, e o que nenhum
|
|
6
|
+
* gatilho pegou continua a UM comando de distância, pela ferramenta pequena
|
|
7
|
+
* `mais_ferramentas(grupo)`.
|
|
8
|
+
*
|
|
9
|
+
* Sempre vai o núcleo (arquivos, terminal, to-dos, finish). O modo atual pode
|
|
10
|
+
* forçar assuntos: o Estúdio de motion só precisa de motion + arquivos.
|
|
11
|
+
*
|
|
12
|
+
* Sem estado: o que o modelo ligou antes aparece na própria conversa (a
|
|
13
|
+
* chamada a `mais_ferramentas`, ou o uso de uma ferramenta de outro assunto),
|
|
14
|
+
* e é lido de lá a cada volta.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const entrada = require('./entrada.js');
|
|
20
|
+
|
|
21
|
+
/** Ferramentas do cliente que os grupos de entrada.js não conhecem. */
|
|
22
|
+
const EXTRAS = { estudio: ['estudio_criar'], 'after-effects': ['estudio_criar'], gravacao: ['editor_pro'] };
|
|
23
|
+
const NOVOS = [
|
|
24
|
+
{ nome: 'web', tools: ['buscar_web', 'ler_url'],
|
|
25
|
+
gatilho: /\b(https?:|www\.|link|pesquis\w*|busca\w*|internet|not[íi]cia|site de|documenta[çc][ãa]o)\b/i },
|
|
26
|
+
{ nome: 'memoria', tools: ['lembrar', 'remember_general'],
|
|
27
|
+
gatilho: /\b(lembr\w*|mem[óo]ria|guarda (isso|que)|anota (isso|que)|n[ãa]o esque[çc]a)\b/i },
|
|
28
|
+
{ nome: 'ajuda', tools: ['perguntar', 'agentes_buscar', 'capacidade_buscar'], gatilho: null },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** Os nomes que o modelo vê. 'after-effects' aparece como 'motion'. */
|
|
32
|
+
const ALIAS = { motion: 'after-effects', 'after-effects': 'after-effects' };
|
|
33
|
+
|
|
34
|
+
const RESUMO = {
|
|
35
|
+
navegador: 'controlar um navegador (abrir, clicar, ler, fotografar)',
|
|
36
|
+
computador: 'controlar o computador (janelas, mouse, teclado, tela)',
|
|
37
|
+
mcp: 'conectar serviços (GitHub, Notion, Slack, banco...)',
|
|
38
|
+
pastas: 'trabalhar em outra pasta',
|
|
39
|
+
pesquisa: 'pesquisar documentação e referências',
|
|
40
|
+
pipeline: 'construir um app ou site inteiro',
|
|
41
|
+
exportar: 'renderizar o vídeo em arquivo',
|
|
42
|
+
delegar: 'chamar subagentes',
|
|
43
|
+
terminal: 'servidor ou watch em segundo plano',
|
|
44
|
+
estudio: 'estúdio: slides, vídeo e arte',
|
|
45
|
+
gravacao: 'gravação do usuário: cortar, legendar, editar',
|
|
46
|
+
motion: 'motion: After Effects, estudio_motion, editar o vídeo',
|
|
47
|
+
web: 'buscar na web e ler links',
|
|
48
|
+
memoria: 'guardar memória entre sessões',
|
|
49
|
+
ajuda: 'perguntar ao usuário, achar agente ou receita',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function tabela() {
|
|
53
|
+
const mapa = new Map(); // nome interno → ferramentas
|
|
54
|
+
for (const g of entrada.GRUPOS) {
|
|
55
|
+
if (!g.nome) continue;
|
|
56
|
+
mapa.set(g.nome, [...g.tools, ...(EXTRAS[g.nome] || [])]);
|
|
57
|
+
}
|
|
58
|
+
for (const n of NOVOS) mapa.set(n.nome, n.tools);
|
|
59
|
+
return mapa;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** O que o modo atual força. Estúdio de motion: só motion (os arquivos já são o núcleo). */
|
|
63
|
+
function forcarDoModo(modo) {
|
|
64
|
+
return /^(estudio|motion)$/i.test(String(modo || '')) ? ['after-effects'] : [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function chamadasDaConversa(mensagens) {
|
|
68
|
+
const lista = [];
|
|
69
|
+
for (const m of mensagens || []) {
|
|
70
|
+
for (const tc of (m && m.tool_calls) || []) {
|
|
71
|
+
lista.push({ nome: (tc.function && tc.function.name) || tc.name, args: (tc.function && tc.function.arguments) || tc.arguments });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return lista;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Os assuntos deste pedido. `usados` = o que a conversa já ligou: o grupo que
|
|
79
|
+
* o modelo pediu em `mais_ferramentas` e o de qualquer ferramenta que ele
|
|
80
|
+
* chamou (se chamou, vai querer chamar de novo).
|
|
81
|
+
*/
|
|
82
|
+
function gruposDoPedido(texto, mensagens = [], forcar = []) {
|
|
83
|
+
const grupos = new Set(entrada.gruposDe(texto, forcar));
|
|
84
|
+
for (const n of NOVOS) if (n.gatilho && n.gatilho.test(String(texto || ''))) grupos.add(n.nome);
|
|
85
|
+
const usados = new Set();
|
|
86
|
+
const t = tabela();
|
|
87
|
+
for (const c of chamadasDaConversa(mensagens)) {
|
|
88
|
+
if (c.nome === 'mais_ferramentas') {
|
|
89
|
+
let a = c.args;
|
|
90
|
+
if (typeof a === 'string') { try { a = JSON.parse(a); } catch { a = {}; } }
|
|
91
|
+
const g = ALIAS[String(a && a.grupo || '').toLowerCase()] || String(a && a.grupo || '').toLowerCase();
|
|
92
|
+
if (t.has(g)) usados.add(g);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const [g, tools] of t) if (tools.includes(c.nome) && g !== 'nucleo') usados.add(g);
|
|
96
|
+
}
|
|
97
|
+
for (const g of usados) grupos.add(g);
|
|
98
|
+
return { grupos, usados };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** As ferramentas dos grupos, na ordem original; o núcleo sempre. */
|
|
102
|
+
function escolher(todas, grupos) {
|
|
103
|
+
const t = tabela();
|
|
104
|
+
const permitidas = new Set();
|
|
105
|
+
for (const [g, tools] of t) if (g === 'nucleo' || grupos.has(g)) tools.forEach((x) => permitidas.add(x));
|
|
106
|
+
permitidas.add('finish');
|
|
107
|
+
const lista = (todas || []).filter((x) => permitidas.has(x.function.name));
|
|
108
|
+
return lista.length >= 5 ? lista : todas; // rede de segurança, como em entrada.js
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A ferramenta pequena que liga um grupo na próxima volta. Só lista o que ficou de fora. */
|
|
112
|
+
function maisFerramentas(todas, grupos) {
|
|
113
|
+
const t = tabela();
|
|
114
|
+
const nomes = new Set((todas || []).map((x) => x.function.name));
|
|
115
|
+
const fora = [];
|
|
116
|
+
for (const [g, tools] of t) {
|
|
117
|
+
if (g === 'nucleo' || grupos.has(g) || !tools.some((x) => nomes.has(x))) continue;
|
|
118
|
+
fora.push(g === 'after-effects' ? 'motion' : g);
|
|
119
|
+
}
|
|
120
|
+
return { type: 'function', function: {
|
|
121
|
+
name: 'mais_ferramentas',
|
|
122
|
+
description: `Liga um grupo de ferramentas na próxima volta. Grupos: ${fora.map((g) => `${g} (${RESUMO[g] || g})`).join('; ')}.`,
|
|
123
|
+
parameters: { type: 'object', properties: { grupo: { type: 'string', enum: fora } }, required: ['grupo'] },
|
|
124
|
+
} };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Sem caber nem no nível mais curto: o assunto secundário sai, o principal fica (motion manda sobre estúdio). */
|
|
128
|
+
function principais(grupos) {
|
|
129
|
+
if (grupos.has('after-effects')) { const g = new Set(grupos); g.delete('estudio'); g.delete('gravacao'); return g; }
|
|
130
|
+
return grupos;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ── O PEDIDO DE VERDADE ──────────────────────────────────────────────────
|
|
134
|
+
* O CLI embrulha o que a pessoa escreveu: na frente vão o menu de estilos, a tabela de comandos e a lista de
|
|
135
|
+
* skills ("PEDIDO: <o que ela escreveu>"), e atrás, o aviso de escopo. Casar gatilho contra o embrulho inteiro
|
|
136
|
+
* liga todos os assuntos (a tabela cita desktop, navegador, motion...) e o recorte não recorta nada. */
|
|
137
|
+
function pedidoReal(texto) {
|
|
138
|
+
let s = String(texto || '');
|
|
139
|
+
const i = s.lastIndexOf('PEDIDO:');
|
|
140
|
+
if (i >= 0) s = s.slice(i + 7);
|
|
141
|
+
const j = s.lastIndexOf('RESPOSTA DA PESSOA:');
|
|
142
|
+
if (j >= 0) s = s.slice(j + 19);
|
|
143
|
+
return s.split(/\n\n(?:ESCOPO:|\[)/)[0].trim();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Teto, em caracteres, da memória persistente em cada nível apertado. */
|
|
147
|
+
const TETO_MEMORIA = { 3: 2500, 4: 1200, 5: 1200 };
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* O contexto que o CLI cola na conversa, encolhido nos níveis fundos: a frente do último pedido
|
|
151
|
+
* (tudo antes de "PEDIDO:") sai, e a memória persistente é cortada. O pedido em si nunca é tocado.
|
|
152
|
+
*/
|
|
153
|
+
function enxugarContexto(mensagens, nivel) {
|
|
154
|
+
if (nivel < 3) return mensagens;
|
|
155
|
+
const ultimoUser = (() => { for (let i = mensagens.length - 1; i >= 0; i--) if (mensagens[i] && mensagens[i].role === 'user') return i; return -1; })();
|
|
156
|
+
return mensagens.map((m, i) => {
|
|
157
|
+
if (!m || typeof m.content !== 'string') return m;
|
|
158
|
+
if (i === ultimoUser) {
|
|
159
|
+
const k = m.content.lastIndexOf('PEDIDO:');
|
|
160
|
+
return k > 0 ? { ...m, content: m.content.slice(k + 7).trim() } : m;
|
|
161
|
+
}
|
|
162
|
+
if (m.role === 'system' && /^Memória persistente:/.test(m.content)) {
|
|
163
|
+
const max = TETO_MEMORIA[nivel] || 1200;
|
|
164
|
+
return m.content.length > max ? { ...m, content: `${m.content.slice(0, max)}\n…(memória cortada: o provedor grátis limita os tokens por minuto)` } : m;
|
|
165
|
+
}
|
|
166
|
+
return m;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = { pedidoReal, enxugarContexto, principais, RESUMO, forcarDoModo, gruposDoPedido, escolher, maisFerramentas, tabela };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* revezamento.js — revezar provedores grátis em vez de esperar o limite por minuto.
|
|
3
|
+
*
|
|
4
|
+
* O plano grátis do Groq para 6–37 s a cada 429/413. Se a pessoa tem a chave de
|
|
5
|
+
* outro provedor grátis no ambiente, o pedido segue nele; só espera se nenhum estiver livre.
|
|
6
|
+
*
|
|
7
|
+
* ordem padrão: groq → cerebras → gemini (endpoint compatível com OpenAI do Google)
|
|
8
|
+
* entra só quem tem chave no ambiente (GROQ_API_KEY, CEREBRAS_API_KEY, GEMINI_API_KEY)
|
|
9
|
+
* e um modelo com ferramentas no catálogo (lib/provider/catalogo.js)
|
|
10
|
+
*
|
|
11
|
+
* Config da pessoa, opcional: ~/.primocode/revezamento.json
|
|
12
|
+
* { "ativo": true, "ordem": ["groq","cerebras","gemini"], "modelos": { "cerebras": "gpt-oss-120b" } }
|
|
13
|
+
* "ativo": false (ou PRIMOCODE_SEM_REVEZAMENTO=1) desliga: volta a esperar, como antes.
|
|
14
|
+
*
|
|
15
|
+
* Nenhuma chave nem URL privada mora aqui: as bases vêm do catálogo/provedor.js.
|
|
16
|
+
* O provedor atual só revezam se ele mesmo está na ordem (quem usa gateway pago não é tocado).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const os = require('os');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const provedor = require('./provedor.js');
|
|
25
|
+
const catalogo = require('../catalogo');
|
|
26
|
+
|
|
27
|
+
const ORDEM_PADRAO = ['groq', 'cerebras', 'gemini'];
|
|
28
|
+
const ARQUIVO = () => path.join(os.homedir(), '.primocode', 'revezamento.json');
|
|
29
|
+
const PAUSA_PADRAO_MS = 30000;
|
|
30
|
+
const PAUSA_DIA_MS = 6 * 60 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
const OCUPADOS = new Map(); // id do provedor → até quando está em pausa (ms)
|
|
33
|
+
|
|
34
|
+
function config() {
|
|
35
|
+
let j = {};
|
|
36
|
+
try { j = JSON.parse(fs.readFileSync(ARQUIVO(), 'utf8')) || {}; } catch { /* sem arquivo: padrão */ }
|
|
37
|
+
const ordem = Array.isArray(j.ordem) ? j.ordem.map(String).filter(Boolean) : ORDEM_PADRAO;
|
|
38
|
+
return {
|
|
39
|
+
ativo: j.ativo !== false && process.env.PRIMOCODE_SEM_REVEZAMENTO !== '1',
|
|
40
|
+
ordem: ordem.length ? ordem : ORDEM_PADRAO,
|
|
41
|
+
modelos: j.modelos && typeof j.modelos === 'object' ? j.modelos : {},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function capDe(p, modelo) {
|
|
46
|
+
const k = p.noCatalogo || p.id;
|
|
47
|
+
return catalogo.capacidades(k, modelo);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** O melhor modelo do provedor `p` (resolvido): com ferramentas e, se pedido, com visão. null se não há. */
|
|
51
|
+
function modeloDe(p, { visao = false, preferido = '' } = {}) {
|
|
52
|
+
const lista = [preferido, ...(p.modelos || [])].filter(Boolean);
|
|
53
|
+
const k = p.noCatalogo || p.id;
|
|
54
|
+
const doCat = catalogo.carregarSync().provedores[k];
|
|
55
|
+
if (doCat) for (const m of Object.keys(doCat.modelos)) lista.push(m);
|
|
56
|
+
for (const m of lista) {
|
|
57
|
+
const c = capDe(p, m);
|
|
58
|
+
if (!c) continue; // fora do catálogo: não dá para garantir ferramentas
|
|
59
|
+
if (c.ferramentas && (!visao || c.visao)) return m;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A pausa vale até quando? `esperaMs` vem do header do provedor. */
|
|
65
|
+
function marcarOcupado(id, esperaMs, { dia = false } = {}) {
|
|
66
|
+
OCUPADOS.set(id, Date.now() + (dia ? PAUSA_DIA_MS : (esperaMs && esperaMs > 0 ? esperaMs + 500 : PAUSA_PADRAO_MS)));
|
|
67
|
+
}
|
|
68
|
+
function livre(id) {
|
|
69
|
+
const ate = OCUPADOS.get(id);
|
|
70
|
+
if (!ate) return true;
|
|
71
|
+
if (ate <= Date.now()) { OCUPADOS.delete(id); return true; }
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
function esquecer() { OCUPADOS.clear(); }
|
|
75
|
+
|
|
76
|
+
/** Os provedores do revezamento, na ordem, prontos para uso. `atual` é o que a pessoa escolheu. */
|
|
77
|
+
function candidatos(atual, { visao = false } = {}) {
|
|
78
|
+
const cfg = config();
|
|
79
|
+
if (!cfg.ativo || !atual || !cfg.ordem.includes(atual.id)) return [];
|
|
80
|
+
const saida = [];
|
|
81
|
+
for (const id of cfg.ordem) {
|
|
82
|
+
let p;
|
|
83
|
+
if (id === atual.id) {
|
|
84
|
+
p = { ...atual };
|
|
85
|
+
const resolvido = provedor.resolver(id) || {};
|
|
86
|
+
p.modelos = [atual.modelo, ...(resolvido.modelos || [])];
|
|
87
|
+
p.noCatalogo = atual.noCatalogo || resolvido.noCatalogo || (resolvido.catalogo ? id : '');
|
|
88
|
+
} else {
|
|
89
|
+
const r = provedor.resolver(id);
|
|
90
|
+
const chave = r && provedor.chaveDoAmbiente(r);
|
|
91
|
+
if (!r || r.cli || !chave) continue;
|
|
92
|
+
p = { id, nome: r.nome, base: r.base, chave, modelos: r.modelos || [], noCatalogo: r.noCatalogo || (r.catalogo ? id : '') };
|
|
93
|
+
}
|
|
94
|
+
const modelo = modeloDe(p, { visao, preferido: id === atual.id ? atual.modelo : (cfg.modelos[id] || '') });
|
|
95
|
+
if (!modelo) continue;
|
|
96
|
+
saida.push({ id: p.id, nome: p.nome, cli: false, base: p.base, modelo, chave: p.chave, daAmbiente: p.daAmbiente, noCatalogo: p.noCatalogo });
|
|
97
|
+
}
|
|
98
|
+
return saida;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Com qual provedor o pedido sai agora? Devolve { p, trocou, de } — `p` é o próprio `atual`
|
|
103
|
+
* quando nada muda. Pula quem está em pausa; com imagem, prefere quem enxerga.
|
|
104
|
+
*/
|
|
105
|
+
function escolher(atual, { visao = false, evitar = [] } = {}) {
|
|
106
|
+
const livres = candidatos(atual, { visao }).filter((c) => livre(c.id) && !evitar.includes(c.id));
|
|
107
|
+
if (!livres.length) return { p: atual, trocou: false, de: atual };
|
|
108
|
+
// o provedor da pessoa tem preferência enquanto estiver livre (e, com imagem, se enxergar)
|
|
109
|
+
const proprio = livres.find((c) => c.id === atual.id);
|
|
110
|
+
const p = proprio || livres[0];
|
|
111
|
+
if (!visao && proprio) return { p: atual, trocou: false, de: atual };
|
|
112
|
+
return { p, trocou: p.id !== atual.id || p.modelo !== atual.modelo, de: atual };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A frase para a pessoa. */
|
|
116
|
+
function frase(de, para, motivo = 'pausa') {
|
|
117
|
+
if (motivo === 'visao') return `${para.nome} enxerga imagens; segui nele.`;
|
|
118
|
+
return `O ${de.nome} pediu pausa; segui no ${para.nome}.`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = { ORDEM_PADRAO, ARQUIVO, config, candidatos, escolher, frase, marcarOcupado, livre, esquecer, modeloDe };
|
|
@@ -29,6 +29,7 @@ const api = require('../../provider/api');
|
|
|
29
29
|
const cfg = require('../../config/config');
|
|
30
30
|
const catalogo = require('../../skill/catalogo');
|
|
31
31
|
const repeticao = require('../../repeticao');
|
|
32
|
+
const permissaoSistema = require('../../permissao-sistema');
|
|
32
33
|
const diff = require('../../diff');
|
|
33
34
|
const registro = require('../../registro');
|
|
34
35
|
const permissao = require('../../permission/permissao');
|
|
@@ -1146,6 +1147,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1146
1147
|
mesmo arquivo por uma hora). Empurra uma vez; na segunda, para. */
|
|
1147
1148
|
const circulo = travado.novo();
|
|
1148
1149
|
let paradoPorTravar = null;
|
|
1150
|
+
/* Falta permissão do macOS (lib/permissao-sistema.js): para na PRIMEIRA falha, sem tentar de novo. */
|
|
1151
|
+
let paradoPorPermissao = null;
|
|
1149
1152
|
/* Anota ANTES de agir (lib/auditoria.js): recusado também fica no livro. */
|
|
1150
1153
|
const anotar = (tc, decisao, motivo) => {
|
|
1151
1154
|
if (decisao !== 'recusada' && decisao !== 'negada') require('../../plugin/voz-ponte').ferramenta(tc.name, tc.arguments);
|
|
@@ -1435,7 +1438,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1435
1438
|
}
|
|
1436
1439
|
const enviadas = compactar(messages, { folgado });
|
|
1437
1440
|
traco(`volta ${voltas}${subagent ? ' (agente)' : ''} · ${enviadas.length} mensagens · ${JSON.stringify(enviadas).length} caracteres`);
|
|
1438
|
-
await motor.requestAct(server, { messages: enviadas, model, preferido: preferido || undefined, effort, token, projectDir: pastaAtual, projectName, subagent, sinal: emVoo.signal }, (evt) => {
|
|
1441
|
+
await motor.requestAct(server, { messages: enviadas, model, preferido: preferido || undefined, effort, token, projectDir: pastaAtual, projectName, subagent, modo: opts.modo, sinal: emVoo.signal }, (evt) => {
|
|
1442
|
+
if (evt.aviso) { if (!subagent) ev('linha', { texto: ` ⎿ ${evt.aviso}`, nivel: 'aviso' }); return; }
|
|
1439
1443
|
if (evt.erro) { streamError = folgado ? evt.erro : erroDaCorrente(evt.erro); return; }
|
|
1440
1444
|
if (evt.modelo) { modeloDaVolta = evt; ultimoModelo = evt.modelo; if (sinais) sinais.modelo = evt.modelo; require('../../cli/statusline').registrarModelo(evt.modelo); return; }
|
|
1441
1445
|
if (evt.tool_call) {
|
|
@@ -1791,6 +1795,12 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1791
1795
|
|
|
1792
1796
|
let motivoTravou = null;
|
|
1793
1797
|
for (const tc of toolCalls) {
|
|
1798
|
+
// A permissão já faltou nesta volta: o resto do lote não roda, mas cada chamada precisa de resposta.
|
|
1799
|
+
if (paradoPorPermissao) {
|
|
1800
|
+
anotar(tc, 'recusada', 'falta permissão do macOS');
|
|
1801
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: permissaoSistema.mensagem(paradoPorPermissao) }) });
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1794
1804
|
/* O JSON da chamada veio quebrado (lib/chamada-torta.js): a
|
|
1795
1805
|
ferramenta não roda, e o modelo recebe o erro com os argumentos
|
|
1796
1806
|
crus para se corrigir — em vez de rodar com `{}` e repetir. */
|
|
@@ -2113,10 +2123,19 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2113
2123
|
respondia "a transcrição precisa do servidor". */
|
|
2114
2124
|
traco(`executando ${tc.name}`);
|
|
2115
2125
|
const idAcao = idsAdiantados.get(tc.id) || anotar(tc, 'permitida');
|
|
2116
|
-
|
|
2126
|
+
let result = adiantadas.has(tc.id) ? adiantadas.get(tc.id)
|
|
2117
2127
|
: await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual, server, token, api, escolher: subagent ? undefined : escolher,
|
|
2118
2128
|
// estudio_criar conta o andamento: a tela desenha a barra do item `estudio`.
|
|
2119
|
-
progresso: tc.name === 'estudio_criar' ? (pct, etapa) => ev('estudio-progresso', { pct, etapa }) : undefined
|
|
2129
|
+
progresso: tc.name === 'estudio_criar' ? (pct, etapa) => ev('estudio-progresso', { pct, etapa }) : undefined,
|
|
2130
|
+
// estudio_motion: o "Plano atualizado" das etapas (ajustar, estruturar, planejar) chega como linha na conversa.
|
|
2131
|
+
linha: tc.name === 'estudio_motion' ? (texto) => ev('linha', { texto }) : undefined });
|
|
2132
|
+
const faltaPermissao = permissaoSistema.detectar(tc.name, result);
|
|
2133
|
+
if (faltaPermissao) {
|
|
2134
|
+
result = permissaoSistema.comoErro(faltaPermissao, result);
|
|
2135
|
+
paradoPorPermissao = faltaPermissao;
|
|
2136
|
+
// no app desktop, esta linha no stderr abre a tela de permissões
|
|
2137
|
+
if (process.env.PRIMOCODE_APP === '1' && !subagent) { try { process.stderr.write(permissaoSistema.linhaProtocolo(faltaPermissao)); } catch { /* sem stderr */ } }
|
|
2138
|
+
}
|
|
2120
2139
|
auditoria.depois(idAcao, result);
|
|
2121
2140
|
if (sinais && result && result.ok && /^(write_file|edit_file|multi_edit)$/.test(tc.name)) sinais.diagSujo = !!result.diagnosticos; // diagnóstico depois da edição
|
|
2122
2141
|
if (antesDaEdicao != null && result && result.ok !== false) {
|
|
@@ -2225,6 +2244,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2225
2244
|
|
|
2226
2245
|
/* ANDANDO EM CÍRCULO (lib/travado.js): a primeira vez, um empurrão
|
|
2227
2246
|
com o que ele está repetindo; a segunda, para — antes da cota. */
|
|
2247
|
+
if (paradoPorPermissao) break; // a primeira falha de permissão encerra: repetir não adianta
|
|
2248
|
+
|
|
2228
2249
|
const giro = travado.fimDaVolta(circulo);
|
|
2229
2250
|
const travou = finished ? null : travado.decidir(circulo, motivoTravou || giro);
|
|
2230
2251
|
if (travou && travou.acao === 'empurrar') {
|
|
@@ -2275,6 +2296,10 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2275
2296
|
const concluiu = Boolean(finalText) && !PARAR;
|
|
2276
2297
|
|
|
2277
2298
|
let passagemDaParada = null;
|
|
2299
|
+
if (paradoPorPermissao && !finalText) {
|
|
2300
|
+
finalText = permissaoSistema.mensagem(paradoPorPermissao);
|
|
2301
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2302
|
+
}
|
|
2278
2303
|
if (paradoPorTravar && !finalText) {
|
|
2279
2304
|
finalText = paradoPorTravar;
|
|
2280
2305
|
if (!subagent) ev('aviso', { texto: finalText });
|
package/lib/skill/catalogo.js
CHANGED
|
@@ -26,6 +26,8 @@ const GRUPOS = [
|
|
|
26
26
|
args: 'path. old_string: trecho exato a substituir, precisa ser único no arquivo. new_string: texto que entra no lugar. replace_all (opcional, bool, padrão false): troca todas as ocorrências.' },
|
|
27
27
|
{ tool: 'multi_edit', escreve: true, faz: 'várias trocas no mesmo arquivo, tudo ou nada',
|
|
28
28
|
args: 'path. edits: lista de {old_string, new_string, replace_all (opcional)}, aplicadas em ordem.' },
|
|
29
|
+
{ tool: 'notebook_edit', escreve: true, faz: 'muda uma célula de um notebook .ipynb pelo índice: troca, insere ou apaga',
|
|
30
|
+
args: 'path: o .ipynb. cell_number: índice da célula (começa em 0). edit_mode (opcional): replace (padrão) | insert | delete. new_source: texto novo (replace e insert). cell_type (opcional): code | markdown (obrigatório no insert).' },
|
|
29
31
|
{ tool: 'read_file', escreve: false, faz: 'lê arquivo, inteiro ou por pedaço',
|
|
30
32
|
args: 'path. offset (opcional, número): primeira linha a ler. limit (opcional, número): quantas linhas.' },
|
|
31
33
|
{ tool: 'list_dir', escreve: false, faz: 'mostra a árvore de pastas',
|
|
@@ -149,7 +151,7 @@ const GRUPOS = [
|
|
|
149
151
|
args: 'id. script (o JSX: app.project.items.addComp, camadas, setValueAtTime com ease). comp (o nome da composição). fontes [{familia, peso}]. '
|
|
150
152
|
+ 'audio (caminho relativo, juntado ao vídeo). conferir [segundos]: quadros PNG para olhar antes de dar por pronto. Leia a skill after-effects. Sai em video-ae.mp4.' },
|
|
151
153
|
{ tool: 'estudio_motion', escreve: true, faz: 'a máquina de motion: do assunto ao vídeo de motion pronto (cenas no After Effects, sons no tempo, trilha e voz opcionais, −14 LUFS)',
|
|
152
|
-
args: 'id. assunto. formato (9:16|1:1|16:9). duracao (6 a 60 s). receita (id de estudio_midia acao:"projetos": usa o formato e a duração da ficha). '
|
|
154
|
+
args: 'id. assunto (ou pedido: as palavras do usuário; sem cenas, passa por 3 etapas antes do render: ajustar o pedido, estruturar em batidas e montar o plano cena por cena; etapas:false volta ao caminho antigo). formato (9:16|1:1|16:9). duracao (6 a 60 s). receita (id de estudio_midia acao:"projetos": usa o formato e a duração da ficha). '
|
|
153
155
|
+ 'marca {cores:{fundo,destaque,tinta}, fontes:{titulo,texto,destaque}}. cenas [{papel, tipo?, fala?, titulo, itens, valor, rotulo, antes, depois, texto, autor, botao}] (o diretor escolhe entre 38 primitivas, 15 transições, 8 câmeras e 8 tipografias, com assinatura própria; a palavra-chave vai entre *asteriscos*). '
|
|
154
156
|
+ 'elementos (termos reais do assunto). transicoes. camera. tipografia. semente. voz (texto ou {}; o whisper mede cada palavra). trilha (true ou o que buscar; false desliga). Sem cenas, o diretor monta. Prefira esta a escrever JSX. Sai em video-motion.mp4. previa:true = rascunho em segundos (meia resolução, sem mix final) para aprovar antes do final. gosto:false pula a tela de gosto do primeiro pedido; o gosto salvo da pessoa (~/.primocode/gosto-motion.json) já entra como peso no diretor — trate como direção, não molde.' },
|
|
155
157
|
{ tool: 'estudio_editar', escreve: true, faz: 'o editor por comando: muda um vídeo do estudio_motion (texto, cor, fonte, ritmo, cenas, trilha, volume, formato) e guarda versões',
|
|
@@ -262,6 +264,8 @@ const GRUPOS = [
|
|
|
262
264
|
args: 'consulta: poucas palavras. limite (opcional, número, padrão 6).' },
|
|
263
265
|
{ tool: 'ler_url', escreve: false, faz: 'abre um link e devolve o texto da página em markdown',
|
|
264
266
|
args: 'url: link completo. max_chars (opcional), desde (opcional, número: continua uma leitura cortada).' },
|
|
267
|
+
{ tool: 'skill', escreve: false, faz: 'carrega o SKILL.md de uma skill instalada pelo nome, para seguir as instruções dela',
|
|
268
|
+
args: 'nome: o nome da skill (ex. "editar-video"). Sem argumentos: lista as instaladas.' },
|
|
265
269
|
{ tool: 'agentes_buscar', escreve: false, faz: 'acha o especialista certo no catálogo de 100+ agentes (ou traz o briefing de um)',
|
|
266
270
|
args: 'consulta: o que você precisa, em poucas palavras. ver (opcional): id de um agente para ler o briefing completo. Sem argumentos: lista as áreas.' },
|
|
267
271
|
{ tool: 'capacidade_buscar', escreve: false, faz: 'acha uma receita pronta entre 1000+ (22 grupos: git, shell, depuração, código, frontend, backend, banco, devops, testes, mobile, ia-llm, design, video-motion, marketing, produto-negocio, windows-linux, web, segurança…)',
|
|
@@ -290,6 +294,10 @@ const GRUPOS = [
|
|
|
290
294
|
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. '
|
|
291
295
|
+ 'USE quando pedirem um app, um site, uma página ou uma ferramenta INTEIRA, do zero. NÃO use para mexer no que já existe, para um arquivo só, nem para conserto: são seis a oito chamadas de agente, '
|
|
292
296
|
+ 'e gastá-las para trocar a cor de um botão come o dia do usuário. O revisor ABRE a página num navegador invisível e mede — página em branco e erro de JavaScript reprovam sozinhos, o que ele achar volta para o construtor.' },
|
|
297
|
+
// Só vai ao modelo quando o recorte por assunto tirou ferramentas (provedor com limite de TPM):
|
|
298
|
+
// lib/provider/local/recorte.js monta o schema real; aqui é o registro, e o tools.js atende.
|
|
299
|
+
{ tool: 'mais_ferramentas', escreve: false, faz: 'liga um grupo de ferramentas que ficou de fora, na próxima volta',
|
|
300
|
+
args: 'grupo: o nome do grupo.' },
|
|
293
301
|
{ tool: 'finish', escreve: false, faz: 'encerra dizendo o que fez', noLoop: true,
|
|
294
302
|
args: 'summary (opcional): uma frase do que foi feito.' },
|
|
295
303
|
],
|
|
@@ -18,6 +18,7 @@ const SCHEMAS = {
|
|
|
18
18
|
read_file: { offset: 'number', limit: 'number' },
|
|
19
19
|
edit_file: { replace_all: 'boolean' },
|
|
20
20
|
multi_edit: { edits: 'array' },
|
|
21
|
+
notebook_edit: { cell_number: 'number' },
|
|
21
22
|
write_todos: { todos: 'array' },
|
|
22
23
|
glob: { limit: 'number' },
|
|
23
24
|
run_shell: { timeout_ms: 'number', em_segundo_plano: 'boolean' },
|
package/lib/tool/tools.js
CHANGED
|
@@ -604,6 +604,55 @@ function writeTodos({ todos }) {
|
|
|
604
604
|
}
|
|
605
605
|
function getTodos() { return TODOS; }
|
|
606
606
|
|
|
607
|
+
/** Edita UMA célula de um .ipynb pelo índice: replace, insert ou delete. Só mexe na célula; o resto do JSON fica como estava. */
|
|
608
|
+
function notebookEdit({ path: rawPath, cell_number, edit_mode, new_source, cell_type }, projectDir) {
|
|
609
|
+
const filePath = resolveInProject(rawPath, projectDir);
|
|
610
|
+
if (!/\.ipynb$/i.test(filePath)) return { ok: false, error: 'notebook_edit só edita arquivo .ipynb' };
|
|
611
|
+
if (!fs.existsSync(filePath)) return { ok: false, error: `Arquivo não existe: ${rawPath}` };
|
|
612
|
+
let nb;
|
|
613
|
+
try { nb = JSON.parse(fs.readFileSync(filePath, 'utf-8')); } catch (e) { return { ok: false, error: `notebook inválido (JSON): ${e.message}` }; }
|
|
614
|
+
if (!nb || !Array.isArray(nb.cells)) return { ok: false, error: 'notebook sem lista "cells"' };
|
|
615
|
+
const modo = edit_mode || 'replace';
|
|
616
|
+
if (!['replace', 'insert', 'delete'].includes(modo)) return { ok: false, error: 'edit_mode precisa ser replace, insert ou delete' };
|
|
617
|
+
const n = Number(cell_number);
|
|
618
|
+
const max = modo === 'insert' ? nb.cells.length : nb.cells.length - 1;
|
|
619
|
+
if (!Number.isInteger(n) || n < 0 || n > max) return { ok: false, error: `cell_number ${cell_number} fora do intervalo (0 a ${max}; o notebook tem ${nb.cells.length} células)` };
|
|
620
|
+
if (modo !== 'delete' && typeof new_source !== 'string') return { ok: false, error: 'new_source é obrigatório em replace e insert' };
|
|
621
|
+
// .ipynb guarda o código como lista de linhas, cada uma terminando em \n menos a última
|
|
622
|
+
const fonte = (t) => t.split('\n').map((l, i, a) => (i < a.length - 1 ? l + '\n' : l)).filter((l, i, a) => l !== '' || a.length === 1);
|
|
623
|
+
if (modo === 'delete') nb.cells.splice(n, 1);
|
|
624
|
+
else if (modo === 'insert') {
|
|
625
|
+
if (!['code', 'markdown'].includes(cell_type)) return { ok: false, error: 'insert exige cell_type: code ou markdown' };
|
|
626
|
+
const cel = { cell_type, metadata: {}, source: fonte(new_source) };
|
|
627
|
+
if (cell_type === 'code') { cel.execution_count = null; cel.outputs = []; }
|
|
628
|
+
nb.cells.splice(n, 0, cel);
|
|
629
|
+
} else {
|
|
630
|
+
const cel = nb.cells[n];
|
|
631
|
+
cel.source = fonte(new_source);
|
|
632
|
+
if (cell_type && cell_type !== cel.cell_type) {
|
|
633
|
+
if (!['code', 'markdown'].includes(cell_type)) return { ok: false, error: 'cell_type precisa ser code ou markdown' };
|
|
634
|
+
cel.cell_type = cell_type;
|
|
635
|
+
if (cell_type === 'code') { cel.execution_count = null; cel.outputs = []; } else { delete cel.execution_count; delete cel.outputs; }
|
|
636
|
+
} else if (cel.cell_type === 'code') { cel.execution_count = null; cel.outputs = []; }
|
|
637
|
+
}
|
|
638
|
+
fs.writeFileSync(filePath, JSON.stringify(nb, null, (nb.nbformat && nb.nbformat >= 4 ? 1 : 2)) + '\n', 'utf-8');
|
|
639
|
+
return { ok: true, path: rawPath, modo, cell_number: n, celulas: nb.cells.length };
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** Carrega o SKILL.md de uma skill instalada. Sem nome, lista as que existem. */
|
|
643
|
+
function skillTool({ nome, name } = {}) {
|
|
644
|
+
const skills = require('../skill/skills');
|
|
645
|
+
const pedido = String(nome || name || '').trim();
|
|
646
|
+
if (!pedido) {
|
|
647
|
+
const lista = skills.listar().filter((s) => s.ligada).map((s) => ({ nome: s.nome, descricao: s.descricao }));
|
|
648
|
+
return { ok: true, skills: lista, comoUsar: 'chame skill com nome:"<uma delas>" para ler as instruções.' };
|
|
649
|
+
}
|
|
650
|
+
const s = skills.ler(skills.apelidar(pedido));
|
|
651
|
+
if (!s) return { ok: false, error: `não há skill "${pedido}"`, disponiveis: skills.listar().map((x) => x.nome) };
|
|
652
|
+
if (!s.ligada) return { ok: false, error: `a skill "${s.nome}" está desligada` };
|
|
653
|
+
return { ok: true, nome: s.nome, titulo: s.titulo, descricao: s.descricao, instrucoes: s.corpo };
|
|
654
|
+
}
|
|
655
|
+
|
|
607
656
|
/**
|
|
608
657
|
* Executa uma ferramenta e NUNCA lança.
|
|
609
658
|
*
|
|
@@ -778,6 +827,10 @@ async function despachar(name, args, ctx) {
|
|
|
778
827
|
return multiEdit(args, projectDir);
|
|
779
828
|
case 'write_todos':
|
|
780
829
|
return writeTodos(args);
|
|
830
|
+
case 'notebook_edit':
|
|
831
|
+
return notebookEdit(args, projectDir);
|
|
832
|
+
case 'skill':
|
|
833
|
+
return skillTool(args);
|
|
781
834
|
case 'agentes_buscar':
|
|
782
835
|
return require('../agentes').ferramentaBuscar(args || {});
|
|
783
836
|
case 'capacidade_buscar':
|
|
@@ -951,7 +1004,7 @@ async function despachar(name, args, ctx) {
|
|
|
951
1004
|
abrir: (arquivo) => { try { openInBrowser(arquivo); } catch {} },
|
|
952
1005
|
});
|
|
953
1006
|
case 'estudio_motion':
|
|
954
|
-
return estudio.ferramentaMotion(args);
|
|
1007
|
+
return estudio.ferramentaMotion(args, ctx);
|
|
955
1008
|
case 'estudio_editar':
|
|
956
1009
|
return estudio.ferramentaEditar(args);
|
|
957
1010
|
case 'estudio_narrar':
|
|
@@ -1030,6 +1083,10 @@ async function despachar(name, args, ctx) {
|
|
|
1030
1083
|
return { ok: true, clicado: alvo.nome, tipo: alvo.tipo, x: alvo.x, y: alvo.y, duplo: !!args.double };
|
|
1031
1084
|
}
|
|
1032
1085
|
|
|
1086
|
+
case 'mais_ferramentas':
|
|
1087
|
+
// O grupo é ligado pelo motor na próxima volta, lendo esta chamada na conversa (recorte.js).
|
|
1088
|
+
return { ok: true, grupo: String(args.grupo || ''), aviso: 'grupo ligado: as ferramentas dele estão disponíveis a partir da próxima chamada' };
|
|
1089
|
+
|
|
1033
1090
|
default:
|
|
1034
1091
|
/* O MODELO PRECISA CONSEGUIR SE CORRIGIR AQUI.
|
|
1035
1092
|
"Tool não implementada: studio_criar" não diz o que existe,
|
package/lib/tui/ui.js
CHANGED
|
@@ -621,7 +621,7 @@ const agora = { texto: '', desde: 0 };
|
|
|
621
621
|
|
|
622
622
|
// O rótulo de cada tool na tela — o que a pessoa lê no lugar do nome interno.
|
|
623
623
|
const ROTULOS = {
|
|
624
|
-
read_file:'Read', write_file:'Write', edit_file:'Edit', multi_edit:'MultiEdit',
|
|
624
|
+
read_file:'Read', write_file:'Write', edit_file:'Edit', multi_edit:'MultiEdit', notebook_edit:'Notebook', skill:'Skill',
|
|
625
625
|
list_dir:'List', glob:'Glob', grep:'Grep', run_shell:'Bash',
|
|
626
626
|
run_in_new_terminal:'Terminal', run_background:'Fundo', background_output:'Fundo·log', stop_background:'Fundo·stop', open_in_browser:'Open', spawn_agent:'Agent',
|
|
627
627
|
write_todos:'Plano', perguntar:'Pergunta', finish:'Finish', pesquisar_referencia:'Pesquisa',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.5.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": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"postinstall": "node lib/preparar.js || true",
|
|
12
12
|
"test:server": "cd server && npm test",
|
|
13
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 && node test/app-tela-estudio.test.mjs && node test/app-celular-telas.test.mjs && node test/app-
|
|
14
|
+
"test:app": "node test/app-fluxo.test.mjs && node test/app-primeira.test.mjs && node test/app-entrar.test.mjs && node test/app-tela-estudio.test.mjs && node test/app-celular-telas.test.mjs && node test/app-render-celular.test.mjs && node test/app-nuvem-estudio.test.mjs",
|
|
15
15
|
"app:build": "cd app && npm run dist",
|
|
16
16
|
"app:dev": "cd app && npm start",
|
|
17
17
|
"mobile:build": "cd mobile && npm run build"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Inter 800 (ExtraBold), Google Fonts, licença SIL OFL 1.1. Embutida para a pele do Destrava funcionar sem rede (glifos.baixarFonte olha aqui primeiro).
|
|
Binary file
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
GSAP 3.15.0 (gsap.min.js, núcleo).
|
|
2
|
+
Origem: copiado de hyperframes@0.8.154 (dist/vendor/npm/gsap@3.15.0), o mesmo pacote que o PrimoCode já usa para renderizar.
|
|
3
|
+
Licença: GreenSock "Standard License" (https://gsap.com/standard-license). Desde a 3.13 o GSAP inteiro é gratuito, inclusive
|
|
4
|
+
uso comercial; a licença proíbe só montar um produto concorrente de ferramenta visual de animação. O cabeçalho original
|
|
5
|
+
com o aviso fica no topo do arquivo e não pode ser removido.
|
|
6
|
+
Por que está aqui: o HTML que a IA escreve para o Hyperframes não precisa de URL de CDN. O motor injeta/troca por este arquivo.
|