primocode 10.13.0 → 10.14.1

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.
Files changed (196) hide show
  1. package/README.md +66 -12
  2. package/bin/primocode.js +148 -8
  3. package/lib/act.js +56 -3
  4. package/lib/catalogo.js +12 -1
  5. package/lib/config.js +1 -0
  6. package/lib/estudio/cota.js +157 -0
  7. package/lib/estudio/dados/projetos/clube-ad5.json +105 -0
  8. package/lib/estudio/dados/projetos/clube-ae-sem-memoria.json +107 -0
  9. package/lib/estudio/dados/projetos/clube-agent.json +120 -0
  10. package/lib/estudio/dados/projetos/clube-antes-e-agora.json +86 -0
  11. package/lib/estudio/dados/projetos/clube-apresentando-clube.json +110 -0
  12. package/lib/estudio/dados/projetos/clube-bluevisa.json +106 -0
  13. package/lib/estudio/dados/projetos/clube-caught-in-the-fire.json +127 -0
  14. package/lib/estudio/dados/projetos/clube-chat-cliente-satisfeito.json +98 -0
  15. package/lib/estudio/dados/projetos/clube-chatgpt-anuncios.json +120 -0
  16. package/lib/estudio/dados/projetos/clube-claude-dj.json +140 -0
  17. package/lib/estudio/dados/projetos/clube-claude-mcp.json +142 -0
  18. package/lib/estudio/dados/projetos/clube-cleanui.json +106 -0
  19. package/lib/estudio/dados/projetos/clube-clinica-take-unico.json +128 -0
  20. package/lib/estudio/dados/projetos/clube-clinica.json +104 -0
  21. package/lib/estudio/dados/projetos/clube-clube-opener.json +125 -0
  22. package/lib/estudio/dados/projetos/clube-clube.json +108 -0
  23. package/lib/estudio/dados/projetos/clube-codigo.json +101 -0
  24. package/lib/estudio/dados/projetos/clube-distilbook.json +104 -0
  25. package/lib/estudio/dados/projetos/clube-drive-upload.json +115 -0
  26. package/lib/estudio/dados/projetos/clube-dub.json +119 -0
  27. package/lib/estudio/dados/projetos/clube-enxovaly.json +105 -0
  28. package/lib/estudio/dados/projetos/clube-fecha7.json +118 -0
  29. package/lib/estudio/dados/projetos/clube-filatov.json +128 -0
  30. package/lib/estudio/dados/projetos/clube-frame.json +108 -0
  31. package/lib/estudio/dados/projetos/clube-glass.json +118 -0
  32. package/lib/estudio/dados/projetos/clube-letra-em-apps.json +84 -0
  33. package/lib/estudio/dados/projetos/clube-letra-globo-3d.json +111 -0
  34. package/lib/estudio/dados/projetos/clube-letra-nos-apps.json +146 -0
  35. package/lib/estudio/dados/projetos/clube-letra-seis-mundos.json +100 -0
  36. package/lib/estudio/dados/projetos/clube-lula-3.json +146 -0
  37. package/lib/estudio/dados/projetos/clube-morph.json +119 -0
  38. package/lib/estudio/dados/projetos/clube-motion-designer.json +115 -0
  39. package/lib/estudio/dados/projetos/clube-motion-pacote.json +112 -0
  40. package/lib/estudio/dados/projetos/clube-movimento.json +103 -0
  41. package/lib/estudio/dados/projetos/clube-not-like-us.json +117 -0
  42. package/lib/estudio/dados/projetos/clube-pinterest-inspiracao.json +86 -0
  43. package/lib/estudio/dados/projetos/clube-pixel-e-tinta.json +108 -0
  44. package/lib/estudio/dados/projetos/clube-ponte.json +118 -0
  45. package/lib/estudio/dados/projetos/clube-processo-edit-tonal.json +126 -0
  46. package/lib/estudio/dados/projetos/clube-processo-motion-100-ia.json +109 -0
  47. package/lib/estudio/dados/projetos/clube-promo.json +114 -0
  48. package/lib/estudio/dados/projetos/clube-proposito.json +115 -0
  49. package/lib/estudio/dados/projetos/clube-rankhog.json +128 -0
  50. package/lib/estudio/dados/projetos/clube-recursao.json +116 -0
  51. package/lib/estudio/dados/projetos/clube-revelar.json +118 -0
  52. package/lib/estudio/dados/projetos/clube-robo.json +125 -0
  53. package/lib/estudio/dados/projetos/clube-shelby-ep2.json +144 -0
  54. package/lib/estudio/dados/projetos/clube-shelby.json +97 -0
  55. package/lib/estudio/dados/projetos/clube-solar-eclipse.json +96 -0
  56. package/lib/estudio/dados/projetos/clube-soldier.json +128 -0
  57. package/lib/estudio/dados/projetos/clube-som-de-cena.json +108 -0
  58. package/lib/estudio/dados/projetos/clube-spotify-candidatura.json +112 -0
  59. package/lib/estudio/dados/projetos/clube-tda-app.json +118 -0
  60. package/lib/estudio/dados/projetos/clube-tda-ia.json +75 -0
  61. package/lib/estudio/dados/projetos/clube-telegram-chat.json +118 -0
  62. package/lib/estudio/dados/projetos/clube-telegram-ilha.json +106 -0
  63. package/lib/estudio/dados/projetos/clube-tennessee-nike.json +132 -0
  64. package/lib/estudio/dados/projetos/clube-tudo-se-move.json +112 -0
  65. package/lib/estudio/dados/projetos/clube-uiad.json +107 -0
  66. package/lib/estudio/dados/projetos/clube-viralgrow.json +108 -0
  67. package/lib/estudio/dados/projetos/clube-whose-side.json +140 -0
  68. package/lib/estudio/dados/projetos/clube-youtube-inscrever.json +108 -0
  69. package/lib/estudio/dados/projetos/elementos-do-site-em-movimento.json +49 -0
  70. package/lib/estudio/dados/projetos/explainer-narrado.json +51 -0
  71. package/lib/estudio/dados/projetos/lancamento-de-produto.json +51 -0
  72. package/lib/estudio/dados/projetos/motion-so-com-sons.json +50 -0
  73. package/lib/estudio/dados/projetos/titulo-em-after-effects.json +51 -0
  74. package/lib/estudio/dados/projetos/tour-de-saas.json +50 -0
  75. package/lib/estudio/dados/projetos-ae/antes-e-agora/ae/cenas.jsx +733 -0
  76. package/lib/estudio/dados/projetos-ae/antes-e-agora/assets/fazer_assets.py +88 -0
  77. package/lib/estudio/dados/projetos-ae/letra-em-apps/ae/cenas.jsx +402 -0
  78. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/preparar_assets.py +17 -0
  79. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/svg_para_ae.py +25 -0
  80. package/lib/estudio/dados/projetos-ae/letra-nos-apps/BRIEF-AGENTES.md +31 -0
  81. package/lib/estudio/dados/projetos-ae/letra-nos-apps/README.md +53 -0
  82. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ROTEIRO-CENAS.md +69 -0
  83. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/A-agenda.jsx +148 -0
  84. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/B-docs.jsx +174 -0
  85. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/C-alttab.jsx +113 -0
  86. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/D-spotify.jsx +118 -0
  87. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/E-notas.jsx +146 -0
  88. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/F-jornal1.jsx +144 -0
  89. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/G-google.jsx +153 -0
  90. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/H-mapa.jsx +176 -0
  91. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/I-imagens.jsx +132 -0
  92. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/J-jornal2.jsx +191 -0
  93. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/K-maps.jsx +184 -0
  94. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera-movimentos.jsx +48 -0
  95. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera.jsx +57 -0
  96. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/card-spotify.jsx +98 -0
  97. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/digitar.jsx +38 -0
  98. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/jornal-comum.jsx +196 -0
  99. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra.jsx +23 -0
  100. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra_automatica.py +83 -0
  101. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/montagem.jsx +74 -0
  102. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/render_html.py +13 -0
  103. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/util.jsx +162 -0
  104. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-midia-comum.jsx +114 -0
  105. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/wg-util.jsx +360 -0
  106. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/F-jornal1/render_html.py +13 -0
  107. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/render_w-midia.py +26 -0
  108. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/inclin.py +30 -0
  109. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_d.py +36 -0
  110. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_e.py +27 -0
  111. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ajustar_movimentos.py +61 -0
  112. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ancorar.py +74 -0
  113. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/assar.py +47 -0
  114. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/camera3d_jornal.py +172 -0
  115. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/comparar.py +32 -0
  116. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/dados_ae_jornal.py +46 -0
  117. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/folha_contato.py +28 -0
  118. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/homografia_jornal.py +74 -0
  119. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/integrar.py +44 -0
  120. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/medir_quadro_a_quadro.py +32 -0
  121. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/mesa_2d.py +64 -0
  122. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/plot_cenas.py +11 -0
  123. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/retificar_quadros.py +38 -0
  124. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/assar_ab.py +52 -0
  125. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/medir_ab.py +59 -0
  126. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ajustar_h.py +111 -0
  127. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_corrige_cam.py +17 -0
  128. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ecc_cadeia.py +80 -0
  129. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_finalizar_cam.py +65 -0
  130. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_mosaico.py +61 -0
  131. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/01-gramatica-e-roteiro.md +55 -0
  132. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/02-camera.md +66 -0
  133. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/03-montagem-no-ae.md +50 -0
  134. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/04-render-e-conferencia.md +43 -0
  135. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/05-abertura-celular-mergulho.md +43 -0
  136. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces.md +42 -0
  137. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/atraso_imagem.py +31 -0
  138. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/fundo.py +56 -0
  139. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/juntar.py +45 -0
  140. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/montar.py +177 -0
  141. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/rastreio.py +74 -0
  142. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/sincronia.py +18 -0
  143. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/ae/cenas.jsx +530 -0
  144. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/assets/fazer_assets.py +107 -0
  145. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/assets/pessoas/figurinha.py +46 -0
  146. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/BRIEF-A.md +83 -0
  147. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/audio.py +179 -0
  148. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/confere.py +20 -0
  149. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte1.js +1 -0
  150. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte2.js +1 -0
  151. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/gerar_emojis.py +11 -0
  152. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/img/filmes/_indice.js +122 -0
  153. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/base.js +380 -0
  154. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/main.js +86 -0
  155. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1a.js +169 -0
  156. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1b.js +455 -0
  157. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1c.js +389 -0
  158. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p2.js +379 -0
  159. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/video.py +62 -0
  160. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/README.md +65 -0
  161. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/oraculo_take.py +137 -0
  162. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/render_take.py +388 -0
  163. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/take.js +235 -0
  164. package/lib/estudio/editor.js +290 -0
  165. package/lib/estudio/effectcraft.js +152 -0
  166. package/lib/estudio/exportar.js +181 -0
  167. package/lib/estudio/index.js +97 -2
  168. package/lib/estudio/kit-ae/base.jsx +1146 -0
  169. package/lib/estudio/kit-ae/cenas.jsx +395 -0
  170. package/lib/estudio/kit-ae/primitivas.jsx +911 -0
  171. package/lib/estudio/maquina-diretor.js +393 -0
  172. package/lib/estudio/maquina-elementos.js +134 -0
  173. package/lib/estudio/maquina-layout.js +198 -0
  174. package/lib/estudio/maquina-som.js +478 -0
  175. package/lib/estudio/maquina-trilha.js +160 -0
  176. package/lib/estudio/maquina-voz.js +429 -0
  177. package/lib/estudio/maquina.js +516 -0
  178. package/lib/estudio/projetos.js +72 -0
  179. package/lib/headless.js +71 -0
  180. package/lib/hooks.js +72 -0
  181. package/lib/limites.js +100 -0
  182. package/lib/local/cerebro.json +398 -12
  183. package/lib/local/entrada.js +46 -2
  184. package/lib/marca-kit.js +312 -0
  185. package/lib/mcp.js +286 -6
  186. package/lib/perigo.js +31 -3
  187. package/lib/planos.js +49 -19
  188. package/lib/preparar.js +111 -0
  189. package/lib/retomar.js +55 -0
  190. package/lib/sandbox.js +197 -0
  191. package/lib/skills.js +3 -1
  192. package/lib/tools.js +13 -4
  193. package/lib/ui.js +1 -1
  194. package/package.json +4 -2
  195. package/skills/after-effects/SKILL.md +52 -0
  196. package/skills/maquina-de-motion/SKILL.md +84 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * headless.js — `primocode -p "pedido"`: sem tela, para script e CI.
3
+ *
4
+ * primocode -p "arruma o teste" texto no stdout
5
+ * primocode -p "..." --output-format json um JSON no fim: {type, is_error, result, duration_ms}
6
+ * git diff | primocode -p "revisa isso" o stdin entra no pedido
7
+ * primocode -p "..." --permission-mode plan só lê (ver lib/perigo.js)
8
+ *
9
+ * Mesma ideia do `claude -p` e do `codex exec` (flags documentadas deles; o
10
+ * código é nosso). O pedido passa pelo MESMO caminho do digitado (sendPrompt).
11
+ * Código de saída: 0 ok, 1 erro.
12
+ */
13
+ 'use strict';
14
+
15
+ const { stripAnsi } = require('./ui');
16
+
17
+ const FORMATOS = ['text', 'json'];
18
+
19
+ /** Tira -p/--print, --output-format e --permission-mode dos argumentos. */
20
+ function analisar(argv) {
21
+ const r = { ativo: false, formato: 'text', permissao: null, resto: [], erro: null };
22
+ for (let i = 0; i < argv.length; i++) {
23
+ const a = argv[i];
24
+ const [chave, valor] = a.startsWith('--') && a.includes('=') ? [a.slice(0, a.indexOf('=')), a.slice(a.indexOf('=') + 1)] : [a, null];
25
+ if (a === '-p' || a === '--print') r.ativo = true;
26
+ else if (chave === '--output-format') {
27
+ const v = valor !== null ? valor : argv[++i];
28
+ if (!FORMATOS.includes(v)) r.erro = `--output-format aceita ${FORMATOS.join(' ou ')}`;
29
+ else r.formato = v;
30
+ } else if (chave === '--permission-mode') r.permissao = valor !== null ? valor : argv[++i];
31
+ else r.resto.push(a);
32
+ }
33
+ return r;
34
+ }
35
+
36
+ /** O pedido: os argumentos soltos, mais o stdin (que vem depois, como contexto). */
37
+ function montarPedido(palavras, stdin) {
38
+ const pedido = palavras.join(' ').trim();
39
+ const extra = String(stdin || '').trim();
40
+ if (pedido && extra) return `${pedido}\n\n${extra}`;
41
+ return pedido || extra;
42
+ }
43
+
44
+ function lerStdin() {
45
+ if (process.stdin.isTTY) return Promise.resolve('');
46
+ return new Promise((resolve) => {
47
+ let s = '';
48
+ process.stdin.setEncoding('utf8');
49
+ process.stdin.on('data', (d) => { s += d; });
50
+ process.stdin.on('end', () => resolve(s));
51
+ process.stdin.on('error', () => resolve(s));
52
+ });
53
+ }
54
+
55
+ /** Roda `fn` guardando tudo o que ela escreve. `calar` esconde da tela. */
56
+ async function capturar(fn, { calar = false } = {}) {
57
+ const log = console.log;
58
+ const escrever = process.stdout.write.bind(process.stdout);
59
+ let texto = '';
60
+ const pega = (s) => { texto += stripAnsi(s); };
61
+ console.log = (...a) => { pega(a.join(' ') + '\n'); if (!calar) log(...a); };
62
+ process.stdout.write = (s, ...resto) => { pega(typeof s === 'string' ? s : Buffer.from(s).toString()); return calar ? true : escrever(s, ...resto); };
63
+ try { await fn(); } finally { console.log = log; process.stdout.write = escrever; }
64
+ return texto.replace(/\n{3,}/g, '\n\n').trim();
65
+ }
66
+
67
+ function resultadoJson({ texto, erro, inicio, permissao }) {
68
+ return JSON.stringify({ type: 'result', is_error: !!erro, result: erro ? String(erro) : texto, duration_ms: Date.now() - inicio, permission_mode: permissao || 'auto' });
69
+ }
70
+
71
+ module.exports = { analisar, montarPedido, lerStdin, capturar, resultadoJson, FORMATOS };
package/lib/hooks.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * hooks.js — comandos seus que rodam ANTES e DEPOIS de cada ferramenta.
3
+ *
4
+ * Arquivo: ~/.primocode/hooks.json (vale em tudo) e <projeto>/.primocode/hooks.json:
5
+ *
6
+ * { "PreToolUse": [{ "matcher": "run_shell|write_file", "command": "./checar.sh" }],
7
+ * "PostToolUse": [{ "matcher": "write_file|edit_file", "command": "npx prettier --write ." }] }
8
+ *
9
+ * O comando recebe o evento em JSON no stdin ({evento, ferramenta, argumentos,
10
+ * resultado, pasta}). No PreToolUse, sair com código 2 BLOQUEIA a ferramenta e o
11
+ * stderr vira o motivo que o agente lê. Qualquer outro código só avisa. Sem
12
+ * `matcher`, vale para toda ferramenta. O contrato é o mesmo dos hooks
13
+ * documentados do Claude Code (code.claude.com/docs/en/hooks); o código é nosso.
14
+ */
15
+ 'use strict';
16
+
17
+ const fs = require('fs');
18
+ const os = require('os');
19
+ const path = require('path');
20
+ const { spawnSync } = require('child_process');
21
+
22
+ const EVENTOS = ['PreToolUse', 'PostToolUse'];
23
+ const PRAZO_MS = 10000;
24
+
25
+ function ler(arquivo) {
26
+ try {
27
+ const j = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
28
+ return j && typeof j === 'object' ? j : {};
29
+ } catch { return {}; }
30
+ }
31
+
32
+ /** Os hooks de um evento: os da casa primeiro, depois os do projeto. */
33
+ function carregar(evento, projectDir) {
34
+ const arquivos = [path.join(os.homedir(), '.primocode', 'hooks.json')];
35
+ if (projectDir) arquivos.push(path.join(projectDir, '.primocode', 'hooks.json'));
36
+ const out = [];
37
+ for (const a of arquivos) {
38
+ const lista = ler(a)[evento];
39
+ if (!Array.isArray(lista)) continue;
40
+ for (const h of lista) if (h && typeof h.command === 'string' && h.command.trim()) out.push(h);
41
+ }
42
+ return out;
43
+ }
44
+
45
+ function casa(matcher, nome) {
46
+ if (!matcher || matcher === '*') return true;
47
+ try { return new RegExp(`^(?:${matcher})$`).test(nome); } catch { return false; }
48
+ }
49
+
50
+ /**
51
+ * Roda os hooks do evento. Devolve { bloqueado, motivo, avisos[] }.
52
+ * Só o PreToolUse bloqueia.
53
+ */
54
+ function rodar(evento, { nome, args, resultado, projectDir }) {
55
+ const saida = { bloqueado: false, motivo: '', avisos: [] };
56
+ if (!EVENTOS.includes(evento)) return saida;
57
+ const entrada = JSON.stringify({ evento, ferramenta: nome, argumentos: args || {}, resultado, pasta: projectDir });
58
+ for (const h of carregar(evento, projectDir)) {
59
+ if (!casa(h.matcher, nome)) continue;
60
+ const r = spawnSync(h.command, { shell: true, input: entrada, cwd: projectDir || process.cwd(), encoding: 'utf8', timeout: h.timeout ? h.timeout * 1000 : PRAZO_MS });
61
+ const err = String(r.stderr || '').trim();
62
+ if (evento === 'PreToolUse' && r.status === 2) {
63
+ saida.bloqueado = true;
64
+ saida.motivo = err || `bloqueado pelo hook: ${h.command}`;
65
+ return saida;
66
+ }
67
+ if (r.error || r.status !== 0) saida.avisos.push(`hook "${h.command.slice(0, 40)}" ${r.error ? 'falhou: ' + r.error.message : 'saiu com ' + r.status}${err ? ' — ' + err.slice(0, 120) : ''}`);
68
+ }
69
+ return saida;
70
+ }
71
+
72
+ module.exports = { EVENTOS, carregar, casa, rodar };
package/lib/limites.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * limites.js — o que o plano libera, aplicado de verdade na CLI.
3
+ *
4
+ * A tabela mora em server/lib/planos.js; o servidor responde o plano da pessoa
5
+ * em POST /api/video/cota (recursos + o catálogo enxuto). Aqui ficam:
6
+ * - situacao(): o plano agora, guardado 5 min na memória do processo;
7
+ * - o MODO LOCAL generoso (sem rede, servidor mudo ou campo que o servidor
8
+ * ainda não manda): nada é barrado. Errar para o lado de quem trabalha;
9
+ * - bloqueio(): a mensagem curta e honesta — o quê, em que plano você está,
10
+ * qual plano libera e o comando para assinar. Nunca barra sem dizer o porquê.
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const GENEROSO = { videosMes: 0, resolucao: 2160, marcaDagua: false, narracaoPremium: true, clubeCompleto: true,
16
+ scriptsAE: true, prioridade: 0, subagentes: 7, kitsMarca: 0, versoesEditor: 0 };
17
+ const MAX_PARALELO = 8;
18
+ const VALIDADE = 5 * 60000;
19
+
20
+ let cache = null;
21
+ const avisos = [];
22
+
23
+ /** Completa o que o servidor não mandou com o generoso (campo novo, servidor velho). */
24
+ function completar(c) {
25
+ const r = { ...GENEROSO };
26
+ for (const [k, v] of Object.entries((c && c.recursos) || {})) if (v !== undefined && v !== null) r[k] = v;
27
+ return { ...(c || {}), recursos: r };
28
+ }
29
+
30
+ function guardar(c) { cache = { em: Date.now(), c: completar(c) }; }
31
+ function esquecer() { cache = null; }
32
+
33
+ /** Nos testes (node test/x.test.js) ninguém bate no servidor de verdade: vale o modo local. */
34
+ const emTeste = () => /\.test\.m?js$/.test(String(process.argv[1] || '')) || process.env.PRIMOCODE_LIMITES === 'local';
35
+
36
+ /** O plano agora: {plano, recursos, planos, offline}. Nunca lança. */
37
+ async function situacao(deps = {}) {
38
+ if (cache && Date.now() - cache.em < VALIDADE) return cache.c;
39
+ if (emTeste() && !deps.consultar) return completar(require('./estudio/cota.js').LOCAL);
40
+ let c;
41
+ try { c = await (deps.consultar || require('./estudio/cota.js').consultar)('checar'); } catch { c = require('./estudio/cota.js').LOCAL; }
42
+ guardar(c);
43
+ return cache.c;
44
+ }
45
+ const recursos = async (deps) => (await situacao(deps)).recursos;
46
+
47
+ const reais = (n) => `R$ ${Number(n).toFixed(2).replace('.', ',')}`;
48
+
49
+ /* Cada recurso: o que o plano ganha ao passar, e o texto de quando bate. */
50
+ const RECURSOS = {
51
+ narracaoPremium: { de: (r) => !!r.narracaoPremium, texto: 'A narração premium (voz do Gemini)', libera: 'a narração premium' },
52
+ clubeCompleto: { de: (r) => !!r.clubeCompleto, texto: 'A receita completa do Clube (etapas e prompt)', libera: 'as receitas completas do Clube' },
53
+ scriptsAE: { de: (r) => !!r.scriptsAE, texto: 'Os scripts de After Effects modelo', libera: 'os scripts de After Effects modelo' },
54
+ kitsMarca: { n: (r) => r.kitsMarca, texto: (r) => `Você já tem ${r.kitsMarca} kit${r.kitsMarca > 1 ? 's' : ''} de marca, o limite do plano`, libera: (r) => (r.kitsMarca ? `${r.kitsMarca} kits de marca` : 'kits de marca sem teto') },
55
+ versoesEditor: { n: (r) => r.versoesEditor, texto: (r) => `Este vídeo já tem ${r.versoesEditor} edições, o limite do plano`, libera: (r) => (r.versoesEditor ? `${r.versoesEditor} edições por vídeo` : 'edições sem teto') },
56
+ subagentes: { n: (r) => r.subagentes + 1, texto: (r) => `Seu plano roda ${r.subagentes + 1} subagente${r.subagentes ? 's juntos' : ' por vez'}`, libera: (r) => (r.subagentes ? `${r.subagentes + 1} subagentes juntos` : 'subagentes em paralelo') },
57
+ };
58
+
59
+ /** O primeiro plano acima do atual que libera `chave` (ou null). */
60
+ function quemLibera(chave, c) {
61
+ const def = RECURSOS[chave], lista = c.planos || [];
62
+ const i = lista.findIndex((p) => p.id === c.plano);
63
+ return lista.slice(i + 1).find((p) => (def.de ? def.de(p.recursos) : (def.n(p.recursos) === 0 || def.n(p.recursos) > def.n(c.recursos)))) || null;
64
+ }
65
+
66
+ /** A mensagem de limite: curta, com o motivo, o degrau que libera e o comando. */
67
+ function bloqueio(chave, c) {
68
+ const def = RECURSOS[chave], r = c.recursos;
69
+ const nome = ((c.planos || []).find((p) => p.id === c.plano) || {}).nome || c.plano;
70
+ const p = quemLibera(chave, c);
71
+ const texto = typeof def.texto === 'function' ? def.texto(r) : `${def.texto} não vêm no plano ${nome}`;
72
+ const libera = typeof def.libera === 'function' ? def.libera(p ? p.recursos : r) : def.libera;
73
+ return `${texto} (plano ${nome}).`
74
+ + (p ? ` O ${p.nome} (${reais(p.preco)}/mês) libera ${libera}.` : '')
75
+ + ' Veja e assine: primocode planos (ou primocode.web.app/planos).';
76
+ }
77
+
78
+ /** Libera o que é só ligado/desligado. {ok:true} ou {ok:false, error}. */
79
+ async function exigir(chave, deps) {
80
+ const c = await situacao(deps);
81
+ return RECURSOS[chave].de(c.recursos) ? { ok: true } : { ok: false, error: bloqueio(chave, c) };
82
+ }
83
+
84
+ /** Limite numérico: `usados` já é o que existe; passa se o teto é 0 (sem teto) ou se ainda cabe mais um. */
85
+ async function cabeMais(chave, usados, deps) {
86
+ const c = await situacao(deps), teto = RECURSOS[chave].n(c.recursos);
87
+ return !teto || usados < teto ? { ok: true } : { ok: false, error: bloqueio(chave, c) };
88
+ }
89
+
90
+ /** Quantos subagentes rodam juntos na mesma rodada (o principal fica de fora). */
91
+ async function paralelo(deps) {
92
+ const c = await situacao(deps);
93
+ return { max: Math.min(MAX_PARALELO, c.recursos.subagentes + 1), aviso: (n) => (n > c.recursos.subagentes + 1 ? bloqueio('subagentes', c) : null) };
94
+ }
95
+
96
+ /** Avisos que o resultado da ferramenta leva ao fim da vez (ex.: narração premium trocada pela local). */
97
+ function avisar(msg) { if (msg && !avisos.includes(msg)) avisos.push(msg); }
98
+ function pegarAvisos() { return avisos.splice(0); }
99
+
100
+ module.exports = { GENEROSO, MAX_PARALELO, situacao, recursos, guardar, esquecer, completar, bloqueio, quemLibera, exigir, cabeMais, paralelo, avisar, pegarAvisos, RECURSOS };