primocode 11.3.1 → 11.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # PrimoCode v11.3.1
1
+ # PrimoCode v11.3.2
2
2
 
3
3
  Agente de engenharia brasileiro para o terminal, no estilo Claude Code.
4
4
  Cria arquivos de verdade, roda comandos, **controla o navegador e o desktop** —
@@ -9,7 +9,7 @@ OpenRouter, e cada apelido (`top`/`main`/`fast`) é uma **corrente**: se um mode
9
9
  está no limite de pedidos, o próximo atende. Você não vê o erro, e não paga nada.
10
10
 
11
11
  ```
12
- ▐▛███▜▌ Primo Code v11.3.1
12
+ ▐▛███▜▌ Primo Code v11.3.2
13
13
  ▝▜█████▛▘ Bem-vindo de volta, Joel
14
14
  ▘▘ ▝▝ ~/primocode · /dir <pasta> muda
15
15
  ```
@@ -7,13 +7,13 @@
7
7
 
8
8
  const path = require('path');
9
9
 
10
- /* tipo → receita. `via`: 'roteiro' (plano pelo roteirista + render) ou 'motion' (a máquina de motion). */
10
+ /* tipo → receita. `via`: 'roteiro' (plano pelo roteirista + render) ou 'kit' (motion pelos estilos: os 50 kits ou um estilo próprio, lib/plugin/kits.js). */
11
11
  const TIPOS = Object.freeze({
12
12
  slides: { rotulo: 'Slides', via: 'roteiro', formato: '16:9', projeto: 'slides' },
13
13
  video: { rotulo: 'Vídeo curto', via: 'roteiro', formato: '9:16', projeto: 'video', duracao: [15, 60, 30] },
14
- motion: { rotulo: 'Motion', via: 'motion', formato: '9:16', duracao: [6, 60, 15] },
15
- anuncio: { rotulo: 'Anúncio', via: 'motion', formato: '9:16', duracao: [10, 30, 15], prefixo: 'Anúncio para chamar pra comprar: ' },
16
- abertura: { rotulo: 'Abertura de canal', via: 'motion', formato: '16:9', duracao: [5, 5, 5], prefixo: 'Abertura de canal com a marca: ' },
14
+ motion: { rotulo: 'Motion', via: 'kit', formato: '9:16', duracao: [6, 60, 15] },
15
+ anuncio: { rotulo: 'Anúncio', via: 'kit', formato: '9:16', duracao: [10, 30, 15], prefixo: 'Anúncio para chamar pra comprar: ' },
16
+ abertura: { rotulo: 'Abertura de canal', via: 'kit', formato: '16:9', duracao: [5, 5, 5], prefixo: 'Abertura de canal com a marca: ' },
17
17
  });
18
18
  const FORMATOS = ['9:16', '16:9', '1:1'];
19
19
 
@@ -28,7 +28,10 @@ function normalizar(args = {}) {
28
28
  if (briefing.length < 3) return { ok: false, error: 'falta o briefing: diga em uma frase o que criar.' };
29
29
  const formato = FORMATOS.includes(String(args.formato)) ? String(args.formato) : receita.formato;
30
30
  const duracao = receita.duracao ? limitar(args.duracao, receita.duracao) : undefined;
31
- return { ok: true, tipo, receita, briefing: briefing.slice(0, 600), formato, duracao };
31
+ const estilo = String(args.estilo || '').trim().slice(0, 60) || undefined;
32
+ const en = args.estilo_novo && typeof args.estilo_novo === 'object' ? args.estilo_novo : null;
33
+ const estiloNovo = en && String(en.nome || '').trim() ? { nome: String(en.nome).trim().slice(0, 40), descricao: String(en.descricao || '').slice(0, 300), base: en.base ? String(en.base).slice(0, 60) : undefined } : undefined;
34
+ return { ok: true, tipo, receita, briefing: briefing.slice(0, 600), formato, duracao, estilo, estiloNovo };
32
35
  }
33
36
 
34
37
  /** Os arquivos entregues, com o tipo que a tela sabe abrir. */
@@ -52,12 +55,12 @@ function tipoDoArquivo(c) {
52
55
 
53
56
  /**
54
57
  * @param {object} args { tipo, briefing, duracao?, formato? }
55
- * @param {object} deps { projeto, plano(args), motion(args), render(args), progresso(pct, etapa) }
58
+ * @param {object} deps { projeto, kit(args), plano(args), render(args), progresso(pct, etapa) }
56
59
  */
57
60
  async function criar(args, deps) {
58
61
  const n = normalizar(args);
59
62
  if (!n.ok) return n;
60
- const { tipo, receita, briefing, formato, duracao } = n;
63
+ const { tipo, receita, briefing, formato, duracao, estilo, estiloNovo } = n;
61
64
  const progresso = deps.progresso || (() => {});
62
65
  const falha = (error, extra) => ({ ok: false, error, ...extra });
63
66
 
@@ -69,12 +72,16 @@ async function criar(args, deps) {
69
72
  const id = proj.id;
70
73
  const base = { tipo, id, pasta: proj.pasta, formato, duracao, titulo };
71
74
 
72
- if (receita.via === 'motion') {
73
- progresso(15, 'Montando as cenas de motion');
74
- const m = await deps.motion({ id, assunto: pedido, formato, duracao });
75
- if (!m || m.ok === false) return falha((m && m.error) || 'o motion não saiu.', base);
76
- progresso(100, 'Pronto');
77
- return { ok: true, ...base, duracao: m.duracao || duracao, arquivo: m.video, arquivos: arquivosDe(m), resumo: `${receita.rotulo} de ${m.duracao || duracao} s.` };
75
+ if (receita.via === 'kit') {
76
+ /* Motion SEMPRE pelos kits (11.1): escolhe o estilo (um dos 50, um criado, ou o da tela de estilos) e monta a pasta
77
+ do vídeo com as peças desmontadas dele. A cena a IA compõe com as peças e renderiza com `primocode kit pecas`.
78
+ Nunca cai no gerador antigo (estudio_motion): quem quiser esse caminho chama a ferramenta dele. */
79
+ progresso(15, 'Escolhendo o estilo e montando as pastas');
80
+ const k = await (deps.kit || ((a) => require('./kit-projeto.js').montar(a)))({ pasta: proj.pasta, pedido, formato, duracao, estilo, estiloNovo });
81
+ if (!k || k.ok === false) return falha((k && k.error) || 'não consegui montar o projeto do estilo.', { ...base, ...(k && k.precisa ? { precisa: k.precisa } : {}), ...(k && k.menu ? { menu: k.menu } : {}) });
82
+ progresso(40, 'Pastas do estilo prontas');
83
+ return { ok: true, ...base, kit: k.slug, estilo: k.nome, pastaKit: k.pasta, pecas: k.pecas, categorias: k.categorias, etapa: 'cena', proximo: k.proximo,
84
+ resumo: `${receita.rotulo} no estilo ${k.nome}: pastas montadas, falta compor a cena e renderizar.` };
78
85
  }
79
86
 
80
87
  progresso(15, 'Escrevendo o roteiro');
@@ -99,9 +106,9 @@ module.exports = { criar, normalizar, TIPOS, FORMATOS, tipoDoArquivo, arquivosDe
99
106
  const QUADRADOS = Object.freeze({
100
107
  slides: 'Tipo escolhido na tela: Apresentação de slides. Use estudio_criar com tipo "slides" e o briefing do pedido (a skill slides-que-prendem ajuda no conteúdo).',
101
108
  video: 'Tipo escolhido na tela: Vídeo curto vertical. Use estudio_criar com tipo "video" e o briefing do pedido.',
102
- motion: 'Tipo escolhido na tela: Motion. Use estudio_criar com tipo "motion" e o briefing do pedido.',
103
- anuncio: 'Tipo escolhido na tela: Anúncio de 15 s. Use estudio_criar com tipo "anuncio" e o briefing do pedido.',
104
- abertura: 'Tipo escolhido na tela: Abertura de canal de 5 s. Use estudio_criar com tipo "abertura" e o briefing do pedido.',
109
+ motion: 'Tipo escolhido na tela: Motion (pelos estilos/kits). Use estudio_criar com tipo "motion", o briefing e o estilo (um dos kits do menu, ou estilo_novo; sem estilo vale o da tela de estilos). Depois siga o caminho "primocode kit pecas <estilo>" que a ferramenta devolve.',
110
+ anuncio: 'Tipo escolhido na tela: Anúncio de 15 s em motion (pelos estilos/kits). Use estudio_criar com tipo "anuncio", o briefing e o estilo (um dos kits do menu, ou estilo_novo). Depois siga o caminho "primocode kit pecas <estilo>" que a ferramenta devolve.',
111
+ abertura: 'Tipo escolhido na tela: Abertura de canal de 5 s em motion (pelos estilos/kits). Use estudio_criar com tipo "abertura", o briefing e o estilo (um dos kits do menu, ou estilo_novo). Depois siga o caminho "primocode kit pecas <estilo>" que a ferramenta devolve.',
105
112
  pagina: 'Tipo escolhido na tela: Página ou app. Construa com write_file na pasta do projeto, confira abrindo no navegador e entregue o arquivo principal. Não use o Estúdio.',
106
113
  bug: 'Tipo escolhido na tela: Corrigir um bug. Ache a causa lendo o código do projeto (read_file, grep), corrija com edit_file e crie um teste que pegue o caso; rode os testes e conte o que mudou. Não use o Estúdio.',
107
114
  pr: 'Tipo escolhido na tela: Revisar um PR. Leia o diff do PR (o número ou o link que a pessoa der; gh ou git diff), aponte os riscos do mais grave ao menos grave e sugira a correção. Não use o Estúdio.',
@@ -7,7 +7,7 @@ const path = require('path');
7
7
  const { tipoDoArquivo } = require('./criar');
8
8
  const Miniatura = require('./miniatura');
9
9
 
10
- const PULAR = new Set(['node_modules', 'remotion', 'midia', 'entrada', 'versoes', '.git']);
10
+ const PULAR = new Set(['node_modules', 'remotion', 'midia', 'entrada', 'versoes', '.git', 'pecas']);
11
11
  const SAIDAS = new Set(['video', 'pdf', 'imagem']);
12
12
 
13
13
  /* Procura as saídas em até 2 níveis; imagem solta (quadro de conferência) serve de miniatura, não de entrega. */
@@ -750,12 +750,12 @@ async function renderMotion(args, p, receita) {
750
750
 
751
751
  // ── estudio_criar ────────────────────────────────────────────────────────
752
752
 
753
- /** A porta simples (criar.js): escolhe a receita e chama as ferramentas de cima. `ctx.progresso(pct, etapa)` alimenta o item da tela. */
753
+ /** A porta simples (criar.js): escolhe a receita e chama as ferramentas de cima (motion vai pelos kits: kit-projeto.js). `ctx.progresso(pct, etapa)` alimenta o item da tela. */
754
754
  async function ferramentaCriar(args, ctx = {}) {
755
755
  return require('./criar.js').criar(args, {
756
756
  projeto,
757
757
  plano: (a) => ferramentaPlano({ ...a, __pasta: ctx.projectDir }),
758
- motion: (a) => ferramentaMotion(a),
758
+ kit: (a) => require('./kit-projeto.js').montar({ ...a, projectDir: ctx.projectDir }, { aoFalar: ctx.aoFalar }),
759
759
  render: (a) => ferramentaRender({ ...a, __pasta: ctx.projectDir }, ctx),
760
760
  progresso: ctx.progresso,
761
761
  });
@@ -0,0 +1,69 @@
1
+ // O motion pelo sistema de estilos (kits): o `estudio_criar` dos tipos motion, anuncio e abertura passa por aqui.
2
+ // Resolve o estilo (um dos 50, um que a pessoa criou, ou a escolha dela na tela de estilos) e monta a pasta do vídeo
3
+ // com as peças desmontadas do estilo (formas, texturas, transições, tipografia, câmera, sons, trilha, cores, fontes,
4
+ // regras). A cena em si a IA compõe do zero com essas peças e renderiza com `primocode kit pecas` (lib/plugin/kits.js).
5
+ // Nada de gerador antigo aqui: é a 11.1 de kits, só que com a porta simples na frente. Node puro.
6
+ 'use strict';
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const semReferencia = (src) => !/[\\/]referencia([\\/]|$)/.test(src);
12
+
13
+ /**
14
+ * @param {object} a { pasta (do projeto), pedido, formato, duracao, estilo?, estiloNovo?: {nome, descricao, base, ref?}, home?, projectDir? }
15
+ * @param {object} d { K (lib/plugin/kits.js), MAT (materiais.js), T (kits-tela.js), aoFalar }
16
+ */
17
+ async function montar(a, d = {}) {
18
+ const K = d.K || require('../kits.js');
19
+ const MAT = d.MAT || require('./materiais.js');
20
+ const op = { home: a.home, projectDir: a.projectDir || a.pasta, env: d.env, aoFalar: d.aoFalar };
21
+ let slug = null, nome = null, origem = null;
22
+
23
+ // 1) estilo novo, pedido pela IA: copia as peças de um estilo base e devolve o slug
24
+ if (a.estiloNovo && a.estiloNovo.nome) {
25
+ const e = a.estiloNovo;
26
+ const argv = ['criar', '--nome', String(e.nome), '--descricao', String(e.descricao || e.nome)];
27
+ if (e.base) argv.push('--base', String(e.base));
28
+ if (e.ref) argv.push('--ref', String(e.ref));
29
+ const r = await K.main(argv, op);
30
+ if (!r.ok || !r.slug) return { ok: false, error: r.texto || 'não consegui criar o estilo.' };
31
+ slug = r.slug; origem = 'criado';
32
+ }
33
+ // 2) estilo nomeado (slug ou nome) entre os 50 e os da pessoa
34
+ if (!slug && a.estilo) {
35
+ const k = K.achar(a.estilo, op);
36
+ if (!k) return { ok: false, error: `o estilo "${a.estilo}" não existe. Escolha um do menu ou crie um novo (estilo_novo).`, menu: K.menuParaIA(op) };
37
+ slug = k.slug; nome = k.nome; origem = 'escolhido';
38
+ }
39
+ // 3) a escolha dela na tela de estilos (no primeiro motion a tela abre e a escolha volta)
40
+ if (!slug) {
41
+ const m = await K.main(['menu'], op);
42
+ if (m && m.escolha) { slug = m.escolha; origem = 'tela'; }
43
+ else return { ok: false, precisa: 'estilo', error: 'falta escolher o estilo: chame de novo com estilo=<slug> (um do menu) ou estilo_novo={nome, descricao, base}.', menu: m && m.texto ? m.texto : K.menuParaIA(op) };
44
+ }
45
+ if (!MAT.migrado(slug)) return { ok: false, error: `o estilo ${slug} não tem as peças soltas (materiais/${slug}).` };
46
+ if (!nome) { const k = K.achar(slug, op); nome = k ? k.nome : slug; }
47
+
48
+ // a pasta do vídeo: motion/pecas/<categorias> + kit.json + LEIA.md
49
+ const raiz = path.join(a.pasta, 'motion');
50
+ const pecas = path.join(raiz, 'pecas');
51
+ fs.rmSync(pecas, { recursive: true, force: true });
52
+ fs.mkdirSync(raiz, { recursive: true });
53
+ fs.cpSync(MAT.pastaDe(slug), pecas, { recursive: true, filter: semReferencia });
54
+ const pastas = fs.readdirSync(pecas, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
55
+ const arquivos = fs.readdirSync(pecas, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort();
56
+ const kit = { slug, nome, origem, formato: a.formato, duracao: a.duracao || null, pedido: a.pedido, pecas: path.relative(a.pasta, pecas), criado: new Date().toISOString() };
57
+ fs.writeFileSync(path.join(raiz, 'kit.json'), JSON.stringify(kit, null, 2) + '\n');
58
+ const proximo = [
59
+ `Estilo ${nome} (${slug}): as peças estão em ${pecas} (${pastas.join(', ')}).`,
60
+ `1) primocode kit pecas ${slug} regras (identidade e densidade mínima)`,
61
+ `2) primocode kit pecas ${slug} ver <peça> (código e params das peças que vai usar)`,
62
+ `3) escreva a cena nova em TSX com o conteúdo do pedido e grave: primocode kit pecas ${slug} escrever <nome> --arquivo cena.tsx --pasta ${a.pasta}`,
63
+ `4) renderize: primocode kit pecas ${slug} renderizar <nome> --pasta ${a.pasta} (o vídeo sai em ${path.join(a.pasta, 'saida')})`,
64
+ ].join('\n');
65
+ fs.writeFileSync(path.join(raiz, 'LEIA.md'), `# ${nome}\n\nPedido: ${a.pedido}\n\n${proximo}\n`);
66
+ return { ok: true, slug, nome, origem, pasta: raiz, pecas, categorias: pastas, arquivos, proximo };
67
+ }
68
+
69
+ module.exports = { montar };
@@ -460,8 +460,9 @@ async function cmdPecas(a, op) {
460
460
  const [estilo, sub, alvo] = a._;
461
461
  if (!estilo) return { ok: true, texto: `Estilos com peças soltas: ${MATERIAIS.estilos().join(', ') || 'nenhum'}. Uso: primocode kit pecas <estilo> [ver <peça> | regras | escrever <nome> --arquivo <cena.tsx> | conferir <cena.tsx> | renderizar <nome>]` };
462
462
  if (!MATERIAIS.migrado(estilo)) return { ok: false, texto: `O estilo "${estilo}" ainda não tem peças soltas (migrados: ${MATERIAIS.estilos().join(', ') || 'nenhum'}).` };
463
- const pasta = op.projectDir || process.cwd();
464
- const lerArq = (f) => fs.readFileSync(path.resolve(pasta, String(f)), 'utf8');
463
+ // --pasta <projeto>: a pasta do vídeo que o estudio_criar montou (motion/ e saida/ ficam nela)
464
+ const pasta = a.pasta ? path.resolve(String(a.pasta)) : (op.projectDir || process.cwd());
465
+ const lerArq = (f) => { const aqui = path.resolve(op.projectDir || process.cwd(), String(f)); return fs.readFileSync(fs.existsSync(aqui) ? aqui : path.resolve(pasta, String(f)), 'utf8'); };
465
466
  try {
466
467
  switch (sub) {
467
468
  case undefined: case 'listar': return MATERIAIS.listar(estilo);
@@ -477,7 +478,7 @@ async function cmdPecas(a, op) {
477
478
  const dir = path.join(pasta, 'motion', `.render-${alvo}`);
478
479
  MATERIAIS.prepararRender(estilo, dir, arq, { duracao: a.quadros ? Number(a.quadros) : undefined });
479
480
  ligarModulos(dir, est.modulos);
480
- const saida = path.resolve(pasta, String(a.saida || path.join('motion', `${alvo}.mp4`)));
481
+ const saida = path.resolve(pasta, String(a.saida || (a.pasta ? path.join('saida', `${alvo}.mp4`) : path.join('motion', `${alvo}.mp4`))));
481
482
  const CQ = require('./estudio/conferir-quadros.js');
482
483
  const cenas = CQ.cenasDe(fs.readFileSync(arq, 'utf8'));
483
484
  const r = await rodarRender(dir, saida, { escala: Number(a.escala) || 1, quadros: cenas.map((c) => c.meio), aoFalar: op.aoFalar || (() => {}) });
@@ -506,7 +507,7 @@ function opcoes(args) {
506
507
  const AJUDA = `primocode kit — os estilos de motion (peças soltas)
507
508
  menu o menu dos estilos (no primeiro motion abre a tela de estilos)
508
509
  ver <slug> as peças e as regras do estilo (igual a pecas <slug>)
509
- pecas <estilo> [ver <peça>|regras|escrever <nome> --arquivo <tsx>|conferir <tsx>|renderizar <nome>]
510
+ pecas <estilo> [ver <peça>|regras|escrever <nome> --arquivo <tsx>|conferir <tsx>|renderizar <nome>] [--pasta <projeto>]
510
511
  a IA compõe a cena do zero com as peças; o conferir cobra a densidade
511
512
  referencia abre a tela de referências e devolve o que mediu
512
513
  criar --nome "<nome>" --descricao "<como é>" [--base <slug>] [--ref <vídeo>] [--quando "a; b; c"] [--familia "<família>"]
@@ -1200,7 +1200,8 @@ async function runActInterno(userPrompt, opts) {
1200
1200
  try {
1201
1201
  const kits = require('../../plugin/kits.js');
1202
1202
  kits.novoTurno();
1203
- const cabeca = kits.cabecaDoPedido(userPrompt, { projectDir });
1203
+ // a instrução do quadrado (Motion, Anúncio, Abertura) também conta: o pedido sozinho ("bolo de pote") não parece motion
1204
+ const cabeca = kits.cabecaDoPedido(instrucao ? `${userPrompt}\n${instrucao}` : userPrompt, { projectDir });
1204
1205
  // o vídeo esperando o sim vai colado na resposta da pessoa (é dela que a IA decide); o menu, na frente
1205
1206
  if (cabeca && /^\[VÍDEO (ESPERANDO|SENDO MONTADO)/.test(cabeca) && pedido.endsWith(userPrompt)) pedido = `${pedido.slice(0, pedido.length - userPrompt.length)}${cabeca}\n\nRESPOSTA DA PESSOA: ${userPrompt}`;
1206
1207
  else if (cabeca) pedido = `${cabeca}\n\n${pedido}`;
@@ -82,8 +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
+ { tool: 'estudio_criar', escreve: true, faz: 'a porta simples: slides e vídeo vão do pedido ao arquivo; motion, anúncio e abertura escolhem um dos 50 estilos (kits) e montam as pastas do vídeo',
86
+ args: 'tipo (slides|video|motion|anuncio|abertura). briefing. duracao (s). formato (9:16|16:9|1:1). Motion: estilo (slug do kit) ou estilo_novo {nome, descricao, base}; depois, primocode kit pecas.' },
87
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',
88
88
  args: 'acao: criar | abrir | procurar | listar | anotar | editar | assinatura. '
89
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. '
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "11.3.1",
3
+ "version": "11.3.2",
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": {