primocode 10.15.2 → 10.17.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/edit-ia-pro.txt +32 -0
- package/LICENSES/fontes-OFL.txt +476 -0
- package/LICENSES/hyperframes-Apache-2.0.txt +205 -0
- package/LICENSES/permanent-marker-Apache-2.0.txt +204 -0
- package/LICENSES/selfie-multiclass-Apache-2.0.txt +227 -0
- package/LICENSES/three-MIT.txt +23 -0
- package/LICENSES/video-use-MIT.txt +24 -0
- package/LICENSES/woven-sfx-CC0.txt +16 -0
- package/LICENSES/yunet-MIT.txt +21 -0
- package/README.md +2 -2
- package/bin/primocode.js +74 -9
- package/editor/LICENSE.md +11 -0
- package/editor/README.md +321 -0
- package/editor/SKILL.md +307 -0
- package/editor/THIRD_PARTY_NOTICES.md +228 -0
- package/editor/assets/baixar.json +591 -0
- package/editor/assets/fonts/anton/OFL.txt +93 -0
- package/editor/assets/fonts/caveat/OFL.txt +93 -0
- package/editor/assets/fonts/figtree/OFL.txt +93 -0
- package/editor/assets/fonts/fonts.json +10 -0
- package/editor/assets/fonts/montserrat/OFL.txt +93 -0
- package/editor/assets/fonts/permanentmarker/LICENSE.txt +202 -0
- package/editor/assets/models/SELFIE_MULTICLASS_LICENSE.txt +227 -0
- package/editor/assets/models/YUNET_LICENSE.txt +21 -0
- package/editor/assets/sfx/licencas/bass_drop.txt +32 -0
- package/editor/assets/sfx/licencas/cash.txt +76 -0
- package/editor/assets/sfx/licencas/charge.txt +31 -0
- package/editor/assets/sfx/licencas/click.txt +46 -0
- package/editor/assets/sfx/licencas/coin.txt +50 -0
- package/editor/assets/sfx/licencas/count.txt +47 -0
- package/editor/assets/sfx/licencas/data.txt +31 -0
- package/editor/assets/sfx/licencas/error.txt +32 -0
- package/editor/assets/sfx/licencas/flash.txt +31 -0
- package/editor/assets/sfx/licencas/gears.txt +82 -0
- package/editor/assets/sfx/licencas/keyboard.txt +48 -0
- package/editor/assets/sfx/licencas/metal_hit.txt +62 -0
- package/editor/assets/sfx/licencas/metal_riser.txt +61 -0
- package/editor/assets/sfx/licencas/mouse_click.txt +50 -0
- package/editor/assets/sfx/licencas/notification.txt +54 -0
- package/editor/assets/sfx/licencas/pop.txt +38 -0
- package/editor/assets/sfx/licencas/rewind.txt +32 -0
- package/editor/assets/sfx/licencas/rise.txt +46 -0
- package/editor/assets/sfx/licencas/sinteticos.txt +21 -0
- package/editor/assets/sfx/licencas/snap.txt +50 -0
- package/editor/assets/sfx/licencas/success.txt +40 -0
- package/editor/assets/sfx/licencas/synth_hit.txt +32 -0
- package/editor/assets/sfx/licencas/tick.txt +14 -0
- package/editor/assets/sfx/licencas/whoosh.txt +47 -0
- package/editor/assets/sfx/licencas/wind.txt +31 -0
- package/editor/assets/sfx/sfx.json +832 -0
- package/editor/assets/sfx/sfx_sinteticos.json +317 -0
- package/editor/assets/sfx/sfx_woven.json +387 -0
- package/editor/assets/sfx/woven/LICENCA.md +16 -0
- package/editor/fichas/briefing-do-video.md +45 -0
- package/editor/fichas/conferencia-da-amostra.md +56 -0
- package/editor/fichas/matriz-de-ganchos.csv +3 -0
- package/editor/fichas/padrao.json +16 -0
- package/editor/fichas/planilha-de-cenas.csv +4 -0
- package/editor/fichas/planilha-de-versoes.csv +4 -0
- package/editor/fichas/plano-em-blocos.md +32 -0
- package/editor/fichas/registro-de-materiais.json +27 -0
- package/editor/helpers/assets_ocr/ocr_vision.swift +41 -0
- package/editor/helpers/bordas_precisas.py +272 -0
- package/editor/helpers/camera_viva.py +426 -0
- package/editor/helpers/captions.py +601 -0
- package/editor/helpers/common.py +308 -0
- package/editor/helpers/compose_final.py +3555 -0
- package/editor/helpers/conferir_corte.py +234 -0
- package/editor/helpers/conferir_dados.py +405 -0
- package/editor/helpers/contact_sheet.py +282 -0
- package/editor/helpers/corte_por_onda.py +433 -0
- package/editor/helpers/corte_seco.py +595 -0
- package/editor/helpers/denoise.py +971 -0
- package/editor/helpers/efeitos.py +408 -0
- package/editor/helpers/efeitos_gerar.py +159 -0
- package/editor/helpers/elevenlabs_api.py +442 -0
- package/editor/helpers/face_layout.py +570 -0
- package/editor/helpers/fetch_images.py +423 -0
- package/editor/helpers/ganho_por_trecho.py +214 -0
- package/editor/helpers/gen_image.py +312 -0
- package/editor/helpers/gen_music.py +316 -0
- package/editor/helpers/gen_sfx.py +148 -0
- package/editor/helpers/grade.py +307 -0
- package/editor/helpers/icloud.py +119 -0
- package/editor/helpers/make_sfx.py +940 -0
- package/editor/helpers/matte.py +1342 -0
- package/editor/helpers/medir_referencia.py +2232 -0
- package/editor/helpers/mixar_trilha.py +354 -0
- package/editor/helpers/montar_corte.py +295 -0
- package/editor/helpers/motion/__init__.py +13 -0
- package/editor/helpers/motion/engine.py +1915 -0
- package/editor/helpers/motion/registry.py +510 -0
- package/editor/helpers/motion/render.py +665 -0
- package/editor/helpers/motion/templates/barra_progresso.py +370 -0
- package/editor/helpers/motion/templates/carimbo.py +132 -0
- package/editor/helpers/motion/templates/checklist.py +271 -0
- package/editor/helpers/motion/templates/comentario_cta.py +380 -0
- package/editor/helpers/motion/templates/contador.py +222 -0
- package/editor/helpers/motion/templates/destaque.py +263 -0
- package/editor/helpers/motion/templates/etiqueta_preco.py +461 -0
- package/editor/helpers/motion/templates/grafico_barras.py +510 -0
- package/editor/helpers/motion/templates/grafico_linha.py +462 -0
- package/editor/helpers/motion/templates/notificacao.py +266 -0
- package/editor/helpers/motion/templates/palavra_impacto.py +410 -0
- package/editor/helpers/motion/templates/passos.py +321 -0
- package/editor/helpers/motion/templates/riscado.py +298 -0
- package/editor/helpers/motion/templates/selo_garantia.py +150 -0
- package/editor/helpers/motion_render.py +241 -0
- package/editor/helpers/motion_sugerir.py +961 -0
- package/editor/helpers/noise_check.py +134 -0
- package/editor/helpers/pack_transcripts.py +223 -0
- package/editor/helpers/palavras_na_onda.py +176 -0
- package/editor/helpers/plano_sfx.py +839 -0
- package/editor/helpers/portao.py +427 -0
- package/editor/helpers/preview_server.py +1177 -0
- package/editor/helpers/rastrear_rosto.py +298 -0
- package/editor/helpers/render_cut.py +1256 -0
- package/editor/helpers/respiros.py +89 -0
- package/editor/helpers/sincronizar_cameras.py +571 -0
- package/editor/helpers/speech_regions.py +423 -0
- package/editor/helpers/style_catalog.json +231 -0
- package/editor/helpers/style_samples.py +246 -0
- package/editor/helpers/tela_verde.py +2113 -0
- package/editor/helpers/text_render.py +2059 -0
- package/editor/helpers/texto_na_tela.py +366 -0
- package/editor/helpers/timeline_view.py +292 -0
- package/editor/helpers/tira_de_quadros.py +113 -0
- package/editor/helpers/transcribe.py +734 -0
- package/editor/helpers/transcribe_batch.py +129 -0
- package/editor/helpers/trilhas.py +1146 -0
- package/editor/helpers/trilhas_humores.json +66 -0
- package/editor/helpers/valores.py +187 -0
- package/editor/helpers/verify_cut.py +820 -0
- package/editor/helpers/voice_levels.py +233 -0
- package/editor/helpers/voz_check.py +386 -0
- package/editor/helpers/voz_limpa.py +81 -0
- package/editor/helpers/watch_preview.py +372 -0
- package/editor/modelo-projeto/LEIA.md +402 -0
- package/editor/modelo-projeto/assets/fontes/OFL-Caveat.txt +93 -0
- package/editor/modelo-projeto/assets/fontes/OFL-JetBrainsMono.txt +93 -0
- package/editor/modelo-projeto/assets/fontes/OFL-Montserrat.txt +93 -0
- package/editor/modelo-projeto/assets/fontes/larguras.json +1 -0
- package/editor/modelo-projeto/assets/three/BufferGeometryUtils.js +1438 -0
- package/editor/modelo-projeto/assets/three/GLTFLoader.js +4895 -0
- package/editor/modelo-projeto/assets/three/LICENSE +21 -0
- package/editor/modelo-projeto/assets/three/ORIGEM.txt +17 -0
- package/editor/modelo-projeto/assets/three/RoomEnvironment.js +185 -0
- package/editor/modelo-projeto/assets/three/three.core.min.js +6 -0
- package/editor/modelo-projeto/assets/three/three.module.min.js +6 -0
- package/editor/modelo-projeto/build.py +4828 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_antes_depois.html +199 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_arquivo.html +232 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_busca.html +315 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_comando.html +304 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_conversa.html +217 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_esquema.html +289 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_medidor.html +249 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_notas.html +233 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_caminho.html +197 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_explodida.html +152 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_tela.html +167 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_volume.html +124 -0
- package/editor/modelo-projeto/compositions/cenas/3d/motor-three.js +728 -0
- package/editor/modelo-projeto/compositions/cenas/LEIA.md +177 -0
- package/editor/modelo-projeto/ferramentas/medir_fontes.py +65 -0
- package/editor/modelo-projeto/ferramentas/pessoa_recortada.py +176 -0
- package/editor/modelo-projeto/ferramentas/rastrear_maos.py +189 -0
- package/editor/modelo-projeto/hyperframes.json +12 -0
- package/editor/modelo-projeto/package.json +11 -0
- package/editor/modelo-projeto/projeto.json +16 -0
- package/editor/preview/app.css +1009 -0
- package/editor/preview/app.js +2122 -0
- package/editor/preview/index.html +307 -0
- package/editor/pyproject.toml +23 -0
- package/editor/references/3d.md +236 -0
- package/editor/references/animacao-catalogo.md +672 -0
- package/editor/references/animacao.md +498 -0
- package/editor/references/comandos.md +92 -0
- package/editor/references/corte-seco-como-funciona.md +50 -0
- package/editor/references/corte-seco.md +88 -0
- package/editor/references/corte.md +290 -0
- package/editor/references/direcao-por-blocos.md +218 -0
- package/editor/references/enquadramento.md +654 -0
- package/editor/references/estilos-prontos.md +355 -0
- package/editor/references/estudio.md +100 -0
- package/editor/references/exemplo-eventos.json +67 -0
- package/editor/references/lettering.md +252 -0
- package/editor/references/modelos.md +494 -0
- package/editor/references/phase2.md +1007 -0
- package/editor/references/referencia-aplicar.md +203 -0
- package/editor/references/referencia-medir.md +375 -0
- package/editor/references/revisao.md +335 -0
- package/editor/references/som-e-trilha.md +146 -0
- package/editor/references/tabela-de-sons.md +130 -0
- package/editor/references/variacoes.md +234 -0
- package/editor/uv.lock +1141 -0
- package/lib/act.js +38 -8
- package/lib/app-chat.js +97 -0
- package/lib/catalogo.js +7 -1
- package/lib/chamada-torta.js +81 -0
- package/lib/editor-pro.js +786 -0
- package/lib/entrega.js +58 -0
- package/lib/estudio/conferencia-visual.js +76 -0
- package/lib/estudio/diretor.js +6 -1
- package/lib/estudio/exportar.js +3 -1
- package/lib/estudio/gosto/tela.html +1349 -0
- package/lib/estudio/gosto.js +305 -0
- package/lib/estudio/index.js +12 -1
- package/lib/estudio/kit-ae/base.jsx +119 -13
- package/lib/estudio/kit-ae/cenas.jsx +47 -15
- package/lib/estudio/kit-ae/guarda.jsx +274 -0
- package/lib/estudio/kit-ae/primitivas.jsx +180 -24
- package/lib/estudio/maquina-diretor.js +70 -12
- package/lib/estudio/maquina-foto.js +177 -0
- package/lib/estudio/maquina-guarda.js +207 -0
- package/lib/estudio/maquina-layout.js +22 -1
- package/lib/estudio/maquina.js +64 -11
- package/lib/estudio/referencias.js +1 -1
- package/lib/estudio/visao/visao.swift +97 -0
- package/lib/estudio/visao.js +60 -0
- package/lib/headless.js +15 -3
- package/lib/local/cerebro.json +285 -7
- package/lib/local/motor.js +5 -2
- package/lib/memoria-usuario.js +164 -0
- package/lib/nuvem.js +33 -2
- package/lib/passagem.js +50 -0
- package/lib/perigo.js +19 -11
- package/lib/protecao.js +134 -0
- package/lib/repeticao.js +1 -1
- package/lib/sandbox.js +57 -9
- package/lib/titulo-conversa.js +46 -0
- package/lib/tools.js +23 -4
- package/lib/ui.js +2 -2
- package/package.json +3 -2
- package/skills/editar-meu-video/SKILL.md +6 -2
- package/skills/editar-video/SKILL.md +113 -0
- package/studio/pessoa.py +52 -0
- package/voz/primo_voz/__main__.py +55 -40
- package/voz/primo_voz/app.py +500 -4
- package/voz/primo_voz/chat.html +1773 -0
- package/voz/primo_voz/conversa.py +0 -26
- package/voz/primo_voz/hud.html +8 -2
- package/voz/primo_voz/hud.py +129 -5
- package/voz/primo_voz/identidade.py +1 -1
- package/voz/primo_voz/painel_dados.py +134 -0
package/lib/act.js
CHANGED
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
+
/* A EDIÇÃO COMPLETA de vídeo falado (20/10/2026): o motor do EDIT IA PRO, pela
|
|
14
|
+
ferramenta editor_pro ou, com servidor antigo, pelo `primocode editor` no
|
|
15
|
+
run_shell. É o caminho SE a pessoa quer o vídeo editado; quem decide é o modelo. */
|
|
16
|
+
const EDICAO_DE_VIDEO_FALADO = 'SE ELA QUER A GRAVAÇÃO DELA FALANDO EDITADA DE VERDADE (a edição completa: "edita meu vídeo", "faz um reels disso", '
|
|
17
|
+
+ '"coloca legenda e efeitos", corte + legenda + gancho + efeitos + sons + trilha), o caminho é o editor_pro (motor EDIT IA PRO; a skill editar-video explica): '
|
|
18
|
+
+ 'acao:"cortar" {video} → leia a fala que volta, com os tempos → acao:"montar" {pasta, plano:{gancho, pecas, broll}} → entregue <pasta>/final.mp4 só com o portão PASSOU '
|
|
19
|
+
+ '(atalho sem plano: acao:"editar"). Se editor_pro não estiver na sua lista de ferramentas, rode o mesmo pelo run_shell: '
|
|
20
|
+
+ '`primocode editor cortar --json \'{"video":"<arquivo>"}\'` (montar e editar do mesmo jeito; a saída é uma linha de JSON).\n';
|
|
21
|
+
|
|
13
22
|
const { c, Spinner, PENSANDO } = require('./ui');
|
|
14
23
|
const fala = require('./fala');
|
|
15
24
|
const especialistas = require('./especialistas');
|
|
@@ -31,6 +40,7 @@ const tempos = require('./tempos');
|
|
|
31
40
|
const preLeitura = require('./pre-leitura');
|
|
32
41
|
const travado = require('./travado');
|
|
33
42
|
const auditoria = require('./auditoria');
|
|
43
|
+
const chamadaTorta = require('./chamada-torta');
|
|
34
44
|
|
|
35
45
|
// 40, não 15: com o freio de repetição (repeticao.js) e o de rodada parada,
|
|
36
46
|
// volta a mais não é volta em círculo — é trabalho. Quinze acabavam no meio
|
|
@@ -714,6 +724,8 @@ function buildMemoryContext(projectDir, projectName, pedido = '', { memoriaDaPas
|
|
|
714
724
|
const daPasta = require('./agents-md.js').ler(projectDir);
|
|
715
725
|
if (daPasta) parts.push(`[AGENTS.md — memória desta pasta: de onde a conversa parou]\n${curto(daPasta)}`);
|
|
716
726
|
}
|
|
727
|
+
// A memória entre sessões (lib/memoria-usuario.js): a instrução e o índice; no servidor, só as 25 primeiras.
|
|
728
|
+
try { parts.push(require('./memoria-usuario.js').blocoDoContexto(undefined, { linhas: proprio ? undefined : 25 })); } catch { /* memória é conveniência */ }
|
|
717
729
|
const geral = cfg.readGeneralMemory();
|
|
718
730
|
if (geral.trim()) parts.push(`[memory-geral.md]\n${curto(geral)}`);
|
|
719
731
|
const projeto = cfg.readProjectMemory(projectDir);
|
|
@@ -937,13 +949,18 @@ async function runAct(userPrompt, opts) {
|
|
|
937
949
|
// Vídeo que o Estúdio fez se edita no Remotion Studio, não no editor por linha do tempo.
|
|
938
950
|
const EDITAR_DO_ESTUDIO = 'SE ELA QUER EDITAR UM VÍDEO QUE O ESTÚDIO FEZ (não uma gravação dela): '
|
|
939
951
|
+ 'estudio_projeto acao:"editar" com o id do projeto abre o Remotion Studio no navegador; diga a pasta a ela e pare.\n';
|
|
952
|
+
const EDICAO_COMPLETA = EDICAO_DE_VIDEO_FALADO;
|
|
940
953
|
const producao = diretor.precisaDeProducao(userPrompt);
|
|
941
954
|
if (ehEdicaoDaGravacao) {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
955
|
+
/* A gravação da pessoa FALANDO: a edição completa é do motor do EDIT IA PRO
|
|
956
|
+
(lib/editor-pro.js), decisão do Joel em 20/10 depois de comparar os dois no
|
|
957
|
+
mesmo vídeo. O editor por linha do tempo (lib/video/) fica para as operações
|
|
958
|
+
soltas. Quem escolhe entre os dois é o modelo, pelo que a pessoa pediu. */
|
|
959
|
+
messages.push({ role: 'system', content: EDITAR_DO_ESTUDIO + EDICAO_COMPLETA
|
|
960
|
+
+ 'SE ela pediu UMA operação solta na gravação (cortar um trecho, juntar, exportar, trocar o fundo, um efeito que segue a mão), use o editor por linha do tempo via run_shell: '
|
|
961
|
+
+ 'SE A GRAVAÇÃO TRAZ PEDIDOS FALADOS ("separe em camadas", "me coloca numa praia"): `primocode video import --json \'{"path":"<arquivo>"}\'`, '
|
|
945
962
|
+ 'depois `primocode video auto --json \'{}\'` (lê o que ela FALA na gravação e aplica cada efeito no momento em que diz), inspect, export e abra o mp4. '
|
|
946
|
-
+ '
|
|
963
|
+
+ 'Nos outros casos: '
|
|
947
964
|
+ 'rode `primocode video help` UMA vez (lista as operações), depois import → remove_silence/remove_words → add_captions → inspect (olhe o png) → export. '
|
|
948
965
|
+ 'Cada edição devolve um delta curto e tem undo: edite sem pedir licença a cada passo.\n'
|
|
949
966
|
/* "deixar os vídeos cinematográficos… fazer um color grading sozinha" (13/10). */
|
|
@@ -977,7 +994,7 @@ async function runAct(userPrompt, opts) {
|
|
|
977
994
|
resposta é o modelo. Então isto é o caminho SE for produzir, e não
|
|
978
995
|
mais uma ordem de produzir. */
|
|
979
996
|
messages.push({ role: 'system', content:
|
|
980
|
-
EDITAR_DO_ESTUDIO + 'VOCÊ DECIDE o que entregar, pelo que a pessoa pediu: se ela quer só o roteiro, a ideia, um texto ou uma resposta, ESCREVA isso e pare — não produza vídeo. '
|
|
997
|
+
EDITAR_DO_ESTUDIO + EDICAO_COMPLETA + 'VOCÊ DECIDE o que entregar, pelo que a pessoa pediu: se ela quer só o roteiro, a ideia, um texto ou uma resposta, ESCREVA isso e pare — não produza vídeo. '
|
|
981
998
|
+ `SE ela quer a peça pronta (formato "${producao.formato === 'apresentacao' ? 'slides' : 'video'}"), faça em três passos: `
|
|
982
999
|
+ `estudio_projeto acao:"criar" com formato "${producao.formato === 'apresentacao' ? 'slides' : 'video'}" → `
|
|
983
1000
|
+ 'estudio_plano com "roteiro" (partes com tipo e fala; o PrimoCode faz layout, voz, ícones, música e sons) → '
|
|
@@ -1354,6 +1371,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1354
1371
|
const chavesDoLote = new Set();
|
|
1355
1372
|
const leituras = toolCalls.filter(ehLeitura).filter((tc) => {
|
|
1356
1373
|
if (tc.name === 'pesquisar_referencia' && pesquisasSecas >= 3) return false;
|
|
1374
|
+
if (chamadaTorta.ehTorta(tc.arguments)) return false;
|
|
1357
1375
|
const { repetida, chave } = repeticao.espiar(jaChamado, tc.name, tc.arguments);
|
|
1358
1376
|
if (repetida || chavesDoLote.has(chave)) return false;
|
|
1359
1377
|
chavesDoLote.add(chave);
|
|
@@ -1371,6 +1389,14 @@ async function runAct(userPrompt, opts) {
|
|
|
1371
1389
|
|
|
1372
1390
|
let motivoTravou = null;
|
|
1373
1391
|
for (const tc of toolCalls) {
|
|
1392
|
+
/* O JSON da chamada veio quebrado (lib/chamada-torta.js): a
|
|
1393
|
+
ferramenta não roda, e o modelo recebe o erro com os argumentos
|
|
1394
|
+
crus para se corrigir — em vez de rodar com `{}` e repetir. */
|
|
1395
|
+
if (chamadaTorta.ehTorta(tc.arguments)) {
|
|
1396
|
+
anotar(tc, 'recusada', 'argumentos não são JSON válido');
|
|
1397
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: chamadaTorta.erroParaOModelo(tc.name, tc.arguments) });
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1374
1400
|
if (PARAR) break;
|
|
1375
1401
|
prova.ver(tc);
|
|
1376
1402
|
if (tc.name === 'finish') {
|
|
@@ -1767,9 +1793,12 @@ async function runAct(userPrompt, opts) {
|
|
|
1767
1793
|
// que "parou sem concluir" não pode virar fato para o passo seguinte.
|
|
1768
1794
|
const concluiu = Boolean(finalText) && !PARAR;
|
|
1769
1795
|
|
|
1796
|
+
let passagemDaParada = null;
|
|
1770
1797
|
if (paradoPorTravar && !finalText) {
|
|
1771
1798
|
finalText = paradoPorTravar;
|
|
1772
1799
|
if (!subagent) fala.aviso(finalText);
|
|
1800
|
+
// Parou sem terminar: a memória leva o estado, não só "parei" (lib/passagem.js).
|
|
1801
|
+
passagemDaParada = require('./passagem').montar({ pedido: userPrompt, motivo: finalText, registro: livro, falhasPendentes });
|
|
1773
1802
|
}
|
|
1774
1803
|
|
|
1775
1804
|
if (PARAR && !finalText) {
|
|
@@ -1784,6 +1813,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1784
1813
|
if (!finalText) {
|
|
1785
1814
|
finalText = motivoDaParada({ voltas, voltasEmFalso, falhasPendentes, travou: rodadasParadas >= MAX_RODADAS_PARADAS, teto });
|
|
1786
1815
|
if (!subagent) fala.aviso(finalText);
|
|
1816
|
+
passagemDaParada = require('./passagem').montar({ pedido: userPrompt, motivo: finalText, registro: livro, falhasPendentes });
|
|
1787
1817
|
}
|
|
1788
1818
|
|
|
1789
1819
|
// Nada de arquivo mexido = nada para lembrar. E grava em `pastaAtual`, não em
|
|
@@ -1797,7 +1827,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1797
1827
|
// A raiz de trabalho não é projeto: um memory.md solto em ~/primocode
|
|
1798
1828
|
// era o que um pedido de vídeo (que trabalha na raiz) deixava para trás.
|
|
1799
1829
|
if (!subagent && temPasta && require('path').resolve(pastaAtual) !== require('path').resolve(cfg.ROOT_DIR)) {
|
|
1800
|
-
cfg.appendProjectMemory(pastaAtual, `**Pedido:** ${userPrompt.slice(0, 300)}\n**Resultado:** ${finalText.slice(0, 500)}`);
|
|
1830
|
+
cfg.appendProjectMemory(pastaAtual, passagemDaParada || `**Pedido:** ${userPrompt.slice(0, 300)}\n**Resultado:** ${finalText.slice(0, 500)}`);
|
|
1801
1831
|
}
|
|
1802
1832
|
|
|
1803
1833
|
// A memória GERAL é gravada sempre, tenha havido pasta ou não.
|
|
@@ -1890,7 +1920,7 @@ function lembreteDeParalelo(todos, naRodada) {
|
|
|
1890
1920
|
dele, e é o que deixa o pai mandar a falha de volta para conserto (o laço
|
|
1891
1921
|
do Qwen: planeja → despacha → confere → conserta). Pura, para o teste. */
|
|
1892
1922
|
function entregaDoAgente(quem, texto, reg) {
|
|
1893
|
-
const linhas = [`ENTREGA DO ${String(quem).toUpperCase()}`,
|
|
1923
|
+
const linhas = [`ENTREGA DO ${String(quem).toUpperCase()}`, require('./entrega').limitar(texto, { quem }).texto];
|
|
1894
1924
|
if (reg) {
|
|
1895
1925
|
const lista = (nome, xs) => { if (xs && xs.length) linhas.push(`${nome}: ${xs.slice(0, 15).join(', ')}`); };
|
|
1896
1926
|
lista('criou', reg.criados);
|
|
@@ -2040,5 +2070,5 @@ function novoPortaoDeProva() {
|
|
|
2040
2070
|
};
|
|
2041
2071
|
}
|
|
2042
2072
|
|
|
2043
|
-
module.exports = { novoPortaoDeProva, runAct, videoForaDeEdicao, entregaDoAgente, lembreteDeParalelo, buildMemoryContext, leituraPeloShell, historicoEnxuto, interromper, mandarDuranteATarefa, trabalhando, sobrasDaCaixa, entregarRecados, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
2073
|
+
module.exports = { EDICAO_DE_VIDEO_FALADO, novoPortaoDeProva, runAct, videoForaDeEdicao, entregaDoAgente, lembreteDeParalelo, buildMemoryContext, leituraPeloShell, historicoEnxuto, interromper, mandarDuranteATarefa, trabalhando, sobrasDaCaixa, entregarRecados, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
2044
2074
|
anotacaoDoQueJaFoi, encolherParaContinuar, MAX_ITERATIONS };
|
package/lib/app-chat.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// `primocode conversas --json` e `primocode conversa <id> --json`: a ponte do app (docs/app-chat.md).
|
|
3
|
+
const nuvem = require('./nuvem');
|
|
4
|
+
const conta = require('./conta');
|
|
5
|
+
|
|
6
|
+
const PAPEIS = { user: 'voce', assistant: 'primo' };
|
|
7
|
+
|
|
8
|
+
/* O texto de uma mensagem: string pura, ou as partes de texto juntas. */
|
|
9
|
+
function textoDe(conteudo) {
|
|
10
|
+
if (typeof conteudo === 'string') return conteudo;
|
|
11
|
+
if (!Array.isArray(conteudo)) return '';
|
|
12
|
+
return conteudo.map((p) => (typeof p === 'string' ? p : (p && p.type === 'text' && p.text) || '')).filter(Boolean).join('\n');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* O pedido feito por voz leva instruções para o agente ("(Modo voz. Termine SEMPRE…)"): no chat fica só o que a pessoa disse. */
|
|
16
|
+
function semInstrucaoDeVoz(texto) {
|
|
17
|
+
return String(texto || '').split(/\s*\((?:Modo voz\.|Pedido por VOZ\.)/)[0].trim();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* A resposta da voz vem como {"falar": "...", "painel": "..."}: no chat vale o painel (a resposta escrita). */
|
|
21
|
+
function respostaEscrita(texto) {
|
|
22
|
+
const t = String(texto || '');
|
|
23
|
+
const i = t.indexOf('{"falar"');
|
|
24
|
+
if (i < 0) return t;
|
|
25
|
+
const antes = t.slice(0, i).trim();
|
|
26
|
+
try {
|
|
27
|
+
const d = JSON.parse(t.slice(i, t.lastIndexOf('}') + 1));
|
|
28
|
+
return String(d.painel || d.falar || antes || t).trim();
|
|
29
|
+
} catch {
|
|
30
|
+
const m = t.slice(i).match(/"painel"\s*:\s*"((?:[^"\\]|\\.)*)"/) || t.slice(i).match(/"falar"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
31
|
+
if (!m) return antes || t;
|
|
32
|
+
try { return JSON.parse(`"${m[1]}"`).trim(); } catch { return m[1].trim(); }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Só voce/primo passam; system e ferramenta ficam de fora. */
|
|
37
|
+
function mensagensApp(mensagens) {
|
|
38
|
+
return (mensagens || []).filter((m) => PAPEIS[m.papel]).map((m) => {
|
|
39
|
+
const papel = PAPEIS[m.papel];
|
|
40
|
+
const texto = textoDe(m.texto);
|
|
41
|
+
return { papel, texto: papel === 'voce' ? semInstrucaoDeVoz(texto) : respostaEscrita(texto) };
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* `conversas --json [--limite N]` -> { id, limite }. */
|
|
46
|
+
function analisar(argv) {
|
|
47
|
+
const out = { json: false, limite: 30, id: null, apagar: false, apagarTodas: false };
|
|
48
|
+
for (let i = 0; i < argv.length; i++) {
|
|
49
|
+
const a = argv[i];
|
|
50
|
+
if (a === '--json') out.json = true;
|
|
51
|
+
else if (a === '--apagar') out.apagar = true;
|
|
52
|
+
else if (a === '--apagar-todas') out.apagarTodas = true;
|
|
53
|
+
else if (a === '--limite' || a.startsWith('--limite=')) {
|
|
54
|
+
const n = Number(a.includes('=') ? a.slice(a.indexOf('=') + 1) : argv[++i]);
|
|
55
|
+
if (Number.isInteger(n) && n > 0) out.limite = n;
|
|
56
|
+
} else if (!a.startsWith('-') && !out.id) out.id = a;
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function main(sub, argv) {
|
|
62
|
+
const o = analisar(argv);
|
|
63
|
+
const dizer = (j) => { console.log(JSON.stringify(j)); return j.ok ? 0 : 1; };
|
|
64
|
+
if (!o.json) { console.log(`Uso: primocode ${sub}${sub === 'conversa' ? ' <id>' : ''} --json${sub === 'conversas' ? ' [--limite N]' : ''}`); return 1; }
|
|
65
|
+
if (!conta.atual()) return dizer({ ok: false, erro: 'não conectado' });
|
|
66
|
+
if (sub === 'conversa') {
|
|
67
|
+
if (!o.id) return dizer({ ok: false, erro: 'faltou o id da conversa' });
|
|
68
|
+
if (o.apagar) {
|
|
69
|
+
const a = await nuvem.apagarConversa(o.id);
|
|
70
|
+
return dizer(a.ok ? { ok: true, apagada: o.id } : { ok: false, erro: a.error || 'não consegui apagar' });
|
|
71
|
+
}
|
|
72
|
+
const l = await nuvem.listarConversas({ limite: 100 });
|
|
73
|
+
const r = await nuvem.lerConversa(o.id);
|
|
74
|
+
if (!r.ok) return dizer({ ok: false, erro: r.error || 'não consegui abrir a conversa' });
|
|
75
|
+
const achada = l.ok && l.conversas.find((x) => String(x.id) === o.id);
|
|
76
|
+
return dizer({ ok: true, id: o.id, titulo: achada ? semInstrucaoDeVoz(achada.titulo) : '(sem título)', mensagens: mensagensApp(r.mensagens) });
|
|
77
|
+
}
|
|
78
|
+
if (o.apagarTodas) { // em voltas de 100: listarConversas não passa disso por vez
|
|
79
|
+
let apagadas = 0;
|
|
80
|
+
for (let volta = 0; volta < 50; volta++) {
|
|
81
|
+
const l = await nuvem.listarConversas({ limite: 100 });
|
|
82
|
+
if (!l.ok) return dizer({ ok: false, erro: l.error || 'não consegui listar', apagadas });
|
|
83
|
+
if (!l.conversas.length) break;
|
|
84
|
+
for (const c of l.conversas) {
|
|
85
|
+
const a = await nuvem.apagarConversa(c.id);
|
|
86
|
+
if (!a.ok) return dizer({ ok: false, erro: a.error || 'não consegui apagar', apagadas });
|
|
87
|
+
apagadas++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return dizer({ ok: true, apagadas });
|
|
91
|
+
}
|
|
92
|
+
const l = await nuvem.listarConversas({ limite: o.limite });
|
|
93
|
+
if (!l.ok) return dizer({ ok: false, erro: l.error || 'não consegui listar' });
|
|
94
|
+
return dizer({ ok: true, conversas: l.conversas.map((x) => ({ id: x.id, titulo: semInstrucaoDeVoz(x.titulo) || x.titulo, atualizada: x.atualizada })) });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { textoDe, semInstrucaoDeVoz, respostaEscrita, mensagensApp, analisar, main };
|
package/lib/catalogo.js
CHANGED
|
@@ -143,7 +143,7 @@ const GRUPOS = [
|
|
|
143
143
|
{ 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)',
|
|
144
144
|
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). '
|
|
145
145
|
+ '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*). '
|
|
146
|
-
+ '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.' },
|
|
146
|
+
+ '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.' },
|
|
147
147
|
{ 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',
|
|
148
148
|
args: 'id_video. mudancas [{op, ...}]: texto {cena,valor} · cor {fundo,destaque,tinta|paleta} · fonte {titulo,texto,destaque} · ritmo {fator} · duracao {cena,segundos|total} · remover/mover/duplicar {cena,para} · trilha {valor:false|busca} · volume {alvo:voz|trilha|sfx,db} · formato {valor}. '
|
|
149
149
|
+ 'previa (rascunho rápido). final (render final da versão atual). voltar_para (n da versão). listar. "Deixa mais rápido" = ritmo fator 0.8; "tira a música" = trilha valor false. Sai em versoes/vN.' },
|
|
@@ -152,6 +152,10 @@ const GRUPOS = [
|
|
|
152
152
|
+ 'vozes: true lista as vozes. '
|
|
153
153
|
+ 'Use os tempos que voltam para a duração das cenas — estimar por número de palavras erra em segundos.' },
|
|
154
154
|
|
|
155
|
+
{ tool: 'editor_pro', escreve: true, faz: 'a EDIÇÃO COMPLETA de um vídeo falado da pessoa (o motor do EDIT IA PRO): corte da melhor tomada sem respiro, voz limpa e no volume, legenda animada, gancho, câmera viva, peças, b-roll, sons, trilha com ducking e portão de qualidade',
|
|
156
|
+
args: 'acao: editar | cortar | montar | preparar | estado. video (a gravação; o original nunca é tocado). pasta (a da edição, devolvida por cortar). prompt (nomes difíceis da fala). '
|
|
157
|
+
+ 'cortar devolve a fala do corte com os tempos; com ela, montar recebe plano {gancho:"Texto *destaque*", pecas:[lettering|lista_check|passo_a_passo|contador|rotulo|carimbo...], broll:[{busca,de,ate,rotulo}], blocos?, estilo?, legenda?, correcoes?} e trilha (auto | arquivo | false). '
|
|
158
|
+
+ 'Sai em <pasta>/final.mp4, só com o portão PASSOU. Leia a skill editar-video.' },
|
|
155
159
|
{ tool: 'estudio_video', escreve: true, faz: 'o que se faz com a gravação do usuário: importar, medir, cortar silêncio, transcrever, recortar a pessoa',
|
|
156
160
|
args: 'id. acao: editar | camadas | importar | medir | silencio | cortar-silencio | quadro | legendas | recortar-pessoa. '
|
|
157
161
|
+ 'editar: FAZ A EDIÇÃO INTEIRA sozinho — corta o silêncio, transcreve palavra por palavra e monta o plano '
|
|
@@ -252,6 +256,8 @@ const GRUPOS = [
|
|
|
252
256
|
// ser para os outros casos). Esta tool é a única porta pra lá.
|
|
253
257
|
{ tool: 'remember_general', escreve: false, faz: 'grava algo sobre o usuário que vale para todo projeto (memory-geral.md)',
|
|
254
258
|
args: 'fato: o que lembrar, em uma frase.' },
|
|
259
|
+
{ tool: 'lembrar', escreve: false, faz: 'guarda ou apaga uma memória que dura entre sessões (~/.primocode/memoria/)',
|
|
260
|
+
args: 'acao: salvar ou apagar. nome: kebab-case curto (mesmo nome atualiza). descricao: uma linha. tipo: usuario|feedback|projeto|referencia. texto: o fato (nunca chave, token ou senha).' },
|
|
255
261
|
// A troca de pasta pede o SIM do usuário, e quem pergunta é o bin:
|
|
256
262
|
// por isso o loop (lib/act.js) atende, não o tools.execute.
|
|
257
263
|
{ tool: 'mudar_pasta', escreve: false, noLoop: true,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chamada-torta.js — o JSON da chamada de ferramenta veio quebrado.
|
|
3
|
+
*
|
|
4
|
+
* Visto no goose (AAIF, Apache-2.0; só a ideia, código nosso): em vez de
|
|
5
|
+
* jogar fora a chamada mal formada, 1) tenta recuperar o objeto por uma
|
|
6
|
+
* cascata (JSON → cerca de markdown → objeto achado por chaves balanceadas →
|
|
7
|
+
* vírgula sobrando) e 2) se não der, DEVOLVE O ERRO AO MODELO, com os
|
|
8
|
+
* argumentos crus, para ele se corrigir na volta seguinte.
|
|
9
|
+
*
|
|
10
|
+
* Antes, o motor local fazia `catch { /* o loop pede de novo */ }` e rodava a
|
|
11
|
+
* ferramenta com `{}`: o `write_file` sem path respondia "path é obrigatório",
|
|
12
|
+
* o modelo não sabia que o problema era a sintaxe, e repetia o mesmo JSON.
|
|
13
|
+
*
|
|
14
|
+
* Puro, sem I/O: o teste mede a cascata inteira.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const MARCA = '__json_torto';
|
|
20
|
+
const MAX_CRU = 600;
|
|
21
|
+
|
|
22
|
+
/** Primeiro objeto `{…}` com chaves balanceadas (respeita aspas e escapes). */
|
|
23
|
+
function objetoBalanceado(s) {
|
|
24
|
+
const ini = s.indexOf('{');
|
|
25
|
+
if (ini < 0) return null;
|
|
26
|
+
let fundo = 0, aspa = false, esc = false;
|
|
27
|
+
for (let i = ini; i < s.length; i++) {
|
|
28
|
+
const ch = s[i];
|
|
29
|
+
if (aspa) { if (esc) esc = false; else if (ch === '\\') esc = true; else if (ch === '"') aspa = false; continue; }
|
|
30
|
+
if (ch === '"') aspa = true;
|
|
31
|
+
else if (ch === '{') fundo++;
|
|
32
|
+
else if (ch === '}' && --fundo === 0) return s.slice(ini, i + 1);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const semVirgulaSobrando = (s) => s.replace(/,\s*([}\]])/g, '$1');
|
|
38
|
+
|
|
39
|
+
function comoObjeto(v) {
|
|
40
|
+
if (typeof v === 'string') { try { return comoObjeto(JSON.parse(v)); } catch { return null; } } // JSON dentro de JSON
|
|
41
|
+
if (Array.isArray(v)) return v.length === 1 ? comoObjeto(v[0]) : null;
|
|
42
|
+
return v && typeof v === 'object' ? v : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @returns {{ok: true, args: object, como: string} | {ok: false}} */
|
|
46
|
+
function recuperar(bruto) {
|
|
47
|
+
if (bruto && typeof bruto === 'object') return { ok: true, args: bruto, como: 'objeto' };
|
|
48
|
+
const s = String(bruto == null ? '' : bruto).trim();
|
|
49
|
+
if (!s) return { ok: true, args: {}, como: 'vazio' };
|
|
50
|
+
const tentativas = [['json', s]];
|
|
51
|
+
const cerca = s.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
52
|
+
if (cerca) tentativas.push(['cerca', cerca[1].trim()]);
|
|
53
|
+
const solto = objetoBalanceado(s);
|
|
54
|
+
if (solto && !s.startsWith('[')) tentativas.push(['chaves balanceadas', solto]);
|
|
55
|
+
for (const [como, t] of [...tentativas]) tentativas.push([como + ' sem vírgula sobrando', semVirgulaSobrando(t)]);
|
|
56
|
+
for (const [como, t] of tentativas) {
|
|
57
|
+
try {
|
|
58
|
+
const o = comoObjeto(JSON.parse(t));
|
|
59
|
+
if (o) return { ok: true, args: o, como };
|
|
60
|
+
} catch { /* próxima */ }
|
|
61
|
+
}
|
|
62
|
+
return { ok: false };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Os argumentos que o motor entrega quando nem a cascata salvou. */
|
|
66
|
+
function marcar(bruto) { return { [MARCA]: String(bruto == null ? '' : bruto) }; }
|
|
67
|
+
function ehTorta(args) { return !!args && typeof args === 'object' && typeof args[MARCA] === 'string'; }
|
|
68
|
+
|
|
69
|
+
/** O texto que volta ao modelo no lugar do resultado da ferramenta. */
|
|
70
|
+
function erroParaOModelo(nome, args) {
|
|
71
|
+
const cru = String(args[MARCA] || '');
|
|
72
|
+
const parte = cru.length > MAX_CRU ? cru.slice(0, MAX_CRU) + `…(+${cru.length - MAX_CRU} caracteres)` : cru;
|
|
73
|
+
return JSON.stringify({
|
|
74
|
+
ok: false,
|
|
75
|
+
error: `A chamada de "${nome}" NÃO rodou: os argumentos não são um JSON válido (aspas sem fechar, vírgula no lugar errado ou texto em volta).`,
|
|
76
|
+
argumentosRecebidos: parte,
|
|
77
|
+
comoCorrigir: 'Chame a ferramenta de novo com UM objeto JSON válido. Em texto longo, escape as aspas (\\") e as quebras de linha (\\n). Se o conteúdo é grande, divida em arquivos menores.',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { recuperar, marcar, ehTorta, erroParaOModelo, objetoBalanceado, MARCA };
|