primocode 10.13.0 → 10.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -12
- package/bin/primocode.js +148 -8
- package/lib/act.js +48 -1
- package/lib/catalogo.js +12 -1
- package/lib/config.js +1 -0
- package/lib/estudio/cota.js +150 -0
- package/lib/estudio/dados/projetos/clube-ad5.json +105 -0
- package/lib/estudio/dados/projetos/clube-ae-sem-memoria.json +107 -0
- package/lib/estudio/dados/projetos/clube-agent.json +120 -0
- package/lib/estudio/dados/projetos/clube-antes-e-agora.json +86 -0
- package/lib/estudio/dados/projetos/clube-apresentando-clube.json +110 -0
- package/lib/estudio/dados/projetos/clube-bluevisa.json +106 -0
- package/lib/estudio/dados/projetos/clube-caught-in-the-fire.json +127 -0
- package/lib/estudio/dados/projetos/clube-chat-cliente-satisfeito.json +98 -0
- package/lib/estudio/dados/projetos/clube-chatgpt-anuncios.json +120 -0
- package/lib/estudio/dados/projetos/clube-claude-dj.json +140 -0
- package/lib/estudio/dados/projetos/clube-claude-mcp.json +142 -0
- package/lib/estudio/dados/projetos/clube-cleanui.json +106 -0
- package/lib/estudio/dados/projetos/clube-clinica-take-unico.json +128 -0
- package/lib/estudio/dados/projetos/clube-clinica.json +104 -0
- package/lib/estudio/dados/projetos/clube-clube-opener.json +125 -0
- package/lib/estudio/dados/projetos/clube-clube.json +108 -0
- package/lib/estudio/dados/projetos/clube-codigo.json +101 -0
- package/lib/estudio/dados/projetos/clube-distilbook.json +104 -0
- package/lib/estudio/dados/projetos/clube-drive-upload.json +115 -0
- package/lib/estudio/dados/projetos/clube-dub.json +119 -0
- package/lib/estudio/dados/projetos/clube-enxovaly.json +105 -0
- package/lib/estudio/dados/projetos/clube-fecha7.json +118 -0
- package/lib/estudio/dados/projetos/clube-filatov.json +128 -0
- package/lib/estudio/dados/projetos/clube-frame.json +108 -0
- package/lib/estudio/dados/projetos/clube-glass.json +118 -0
- package/lib/estudio/dados/projetos/clube-letra-em-apps.json +84 -0
- package/lib/estudio/dados/projetos/clube-letra-globo-3d.json +111 -0
- package/lib/estudio/dados/projetos/clube-letra-nos-apps.json +146 -0
- package/lib/estudio/dados/projetos/clube-letra-seis-mundos.json +100 -0
- package/lib/estudio/dados/projetos/clube-lula-3.json +146 -0
- package/lib/estudio/dados/projetos/clube-morph.json +119 -0
- package/lib/estudio/dados/projetos/clube-motion-designer.json +115 -0
- package/lib/estudio/dados/projetos/clube-motion-pacote.json +112 -0
- package/lib/estudio/dados/projetos/clube-movimento.json +103 -0
- package/lib/estudio/dados/projetos/clube-not-like-us.json +117 -0
- package/lib/estudio/dados/projetos/clube-pinterest-inspiracao.json +86 -0
- package/lib/estudio/dados/projetos/clube-pixel-e-tinta.json +108 -0
- package/lib/estudio/dados/projetos/clube-ponte.json +118 -0
- package/lib/estudio/dados/projetos/clube-processo-edit-tonal.json +126 -0
- package/lib/estudio/dados/projetos/clube-processo-motion-100-ia.json +109 -0
- package/lib/estudio/dados/projetos/clube-promo.json +114 -0
- package/lib/estudio/dados/projetos/clube-proposito.json +115 -0
- package/lib/estudio/dados/projetos/clube-rankhog.json +128 -0
- package/lib/estudio/dados/projetos/clube-recursao.json +116 -0
- package/lib/estudio/dados/projetos/clube-revelar.json +118 -0
- package/lib/estudio/dados/projetos/clube-robo.json +125 -0
- package/lib/estudio/dados/projetos/clube-shelby-ep2.json +144 -0
- package/lib/estudio/dados/projetos/clube-shelby.json +97 -0
- package/lib/estudio/dados/projetos/clube-solar-eclipse.json +96 -0
- package/lib/estudio/dados/projetos/clube-soldier.json +128 -0
- package/lib/estudio/dados/projetos/clube-som-de-cena.json +108 -0
- package/lib/estudio/dados/projetos/clube-spotify-candidatura.json +112 -0
- package/lib/estudio/dados/projetos/clube-tda-app.json +118 -0
- package/lib/estudio/dados/projetos/clube-tda-ia.json +75 -0
- package/lib/estudio/dados/projetos/clube-telegram-chat.json +118 -0
- package/lib/estudio/dados/projetos/clube-telegram-ilha.json +106 -0
- package/lib/estudio/dados/projetos/clube-tennessee-nike.json +132 -0
- package/lib/estudio/dados/projetos/clube-tudo-se-move.json +112 -0
- package/lib/estudio/dados/projetos/clube-uiad.json +107 -0
- package/lib/estudio/dados/projetos/clube-viralgrow.json +108 -0
- package/lib/estudio/dados/projetos/clube-whose-side.json +140 -0
- package/lib/estudio/dados/projetos/clube-youtube-inscrever.json +108 -0
- package/lib/estudio/dados/projetos/elementos-do-site-em-movimento.json +49 -0
- package/lib/estudio/dados/projetos/explainer-narrado.json +51 -0
- package/lib/estudio/dados/projetos/lancamento-de-produto.json +51 -0
- package/lib/estudio/dados/projetos/motion-so-com-sons.json +50 -0
- package/lib/estudio/dados/projetos/titulo-em-after-effects.json +51 -0
- package/lib/estudio/dados/projetos/tour-de-saas.json +50 -0
- package/lib/estudio/dados/projetos-ae/antes-e-agora/ae/cenas.jsx +733 -0
- package/lib/estudio/dados/projetos-ae/antes-e-agora/assets/fazer_assets.py +88 -0
- package/lib/estudio/dados/projetos-ae/letra-em-apps/ae/cenas.jsx +402 -0
- package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/preparar_assets.py +17 -0
- package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/svg_para_ae.py +25 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/BRIEF-AGENTES.md +31 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/README.md +53 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ROTEIRO-CENAS.md +69 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/A-agenda.jsx +148 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/B-docs.jsx +174 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/C-alttab.jsx +113 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/D-spotify.jsx +118 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/E-notas.jsx +146 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/F-jornal1.jsx +144 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/G-google.jsx +153 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/H-mapa.jsx +176 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/I-imagens.jsx +132 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/J-jornal2.jsx +191 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/K-maps.jsx +184 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera-movimentos.jsx +48 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera.jsx +57 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/card-spotify.jsx +98 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/digitar.jsx +38 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/jornal-comum.jsx +196 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra.jsx +23 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra_automatica.py +83 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/montagem.jsx +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/render_html.py +13 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/util.jsx +162 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-midia-comum.jsx +114 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/wg-util.jsx +360 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/F-jornal1/render_html.py +13 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/render_w-midia.py +26 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/inclin.py +30 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_d.py +36 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_e.py +27 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ajustar_movimentos.py +61 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ancorar.py +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/assar.py +47 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/camera3d_jornal.py +172 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/comparar.py +32 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/dados_ae_jornal.py +46 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/folha_contato.py +28 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/homografia_jornal.py +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/integrar.py +44 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/medir_quadro_a_quadro.py +32 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/mesa_2d.py +64 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/plot_cenas.py +11 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/retificar_quadros.py +38 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/assar_ab.py +52 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/medir_ab.py +59 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ajustar_h.py +111 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_corrige_cam.py +17 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ecc_cadeia.py +80 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_finalizar_cam.py +65 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_mosaico.py +61 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/01-gramatica-e-roteiro.md +55 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/02-camera.md +66 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/03-montagem-no-ae.md +50 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/04-render-e-conferencia.md +43 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/05-abertura-celular-mergulho.md +43 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces.md +42 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/atraso_imagem.py +31 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/fundo.py +56 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/juntar.py +45 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/montar.py +177 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/rastreio.py +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/sincronia.py +18 -0
- package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/ae/cenas.jsx +530 -0
- package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/assets/fazer_assets.py +107 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/assets/pessoas/figurinha.py +46 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/BRIEF-A.md +83 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/audio.py +179 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/confere.py +20 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte1.js +1 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte2.js +1 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/gerar_emojis.py +11 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/img/filmes/_indice.js +122 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/base.js +380 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/main.js +86 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1a.js +169 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1b.js +455 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1c.js +389 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p2.js +379 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/video.py +62 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/README.md +65 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/oraculo_take.py +137 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/render_take.py +388 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/take.js +235 -0
- package/lib/estudio/editor.js +285 -0
- package/lib/estudio/effectcraft.js +152 -0
- package/lib/estudio/exportar.js +181 -0
- package/lib/estudio/index.js +90 -2
- package/lib/estudio/kit-ae/base.jsx +1146 -0
- package/lib/estudio/kit-ae/cenas.jsx +395 -0
- package/lib/estudio/kit-ae/primitivas.jsx +911 -0
- package/lib/estudio/maquina-diretor.js +393 -0
- package/lib/estudio/maquina-elementos.js +134 -0
- package/lib/estudio/maquina-layout.js +198 -0
- package/lib/estudio/maquina-som.js +478 -0
- package/lib/estudio/maquina-trilha.js +160 -0
- package/lib/estudio/maquina-voz.js +429 -0
- package/lib/estudio/maquina.js +516 -0
- package/lib/estudio/projetos.js +60 -0
- package/lib/headless.js +71 -0
- package/lib/hooks.js +72 -0
- package/lib/local/cerebro.json +398 -12
- package/lib/local/entrada.js +46 -2
- package/lib/marca-kit.js +309 -0
- package/lib/mcp.js +286 -6
- package/lib/perigo.js +31 -3
- package/lib/planos.js +47 -19
- package/lib/preparar.js +111 -0
- package/lib/retomar.js +55 -0
- package/lib/sandbox.js +197 -0
- package/lib/skills.js +3 -1
- package/lib/tools.js +13 -4
- package/lib/ui.js +1 -1
- package/package.json +4 -2
- package/skills/after-effects/SKILL.md +52 -0
- package/skills/maquina-de-motion/SKILL.md +84 -0
package/lib/local/entrada.js
CHANGED
|
@@ -88,6 +88,13 @@ const GRUPOS = [
|
|
|
88
88
|
tools: ['estudio_video'],
|
|
89
89
|
gatilho: /\b(grava[çc][ãa]o|gravei|filmagem|filmei|meu v[íi]deo|edit\w* (o |a |meu |minha |esse |essa |este |esta )?(v[íi]deo|grava[çc][ãa]o)|\.mp4|\.mov|legendas?|cort(a|ar|e) (o )?sil[êe]ncio|recort(a|ar|e) (a )?pessoa|transcrev\w*|importa\w* (o )?v[íi]deo)\b/i,
|
|
90
90
|
},
|
|
91
|
+
{
|
|
92
|
+
/* O motor After Effects só entra quando o pedido o chama: o esquema
|
|
93
|
+
dele pesa no teto do minuto e "faz um vídeo" não precisa dele. */
|
|
94
|
+
nome: 'after-effects',
|
|
95
|
+
tools: ['estudio_ae', 'estudio_motion', 'estudio_editar'],
|
|
96
|
+
gatilho: /\b(after ?effects?|aftereffects|\bae\b|effectcraft|extendscript|\.jsx|keyframes?|express[ãa]o do ae|motion( design| graphics?)?|an[uú]ncio animado|vinheta|reels?|estudio_motion|estudio_editar|editor de v[íi]deo|pr[ée]via|deixa mais (r[áa]pido|lento)|tira a m[úu]sica|troca (o|a) (t[íi]tulo|cor|fonte|m[úu]sica))\b/i,
|
|
97
|
+
},
|
|
91
98
|
{
|
|
92
99
|
nome: 'navegador',
|
|
93
100
|
tools: ['browser_open', 'browser_click', 'browser_type', 'browser_key',
|
|
@@ -154,7 +161,7 @@ const GRUPOS = [
|
|
|
154
161
|
ASSUNTO (o que costuma ter partes) e pelo TAMANHO (ver `ehGrande`). */
|
|
155
162
|
nome: 'delegar',
|
|
156
163
|
tools: ['spawn_agent'],
|
|
157
|
-
gatilho: /\b(v[áa]rios|v[áa]rias|todos os|todas as|em paralelo|projeto inteiro|refator\w*|migra[çc][ãa]o|auditor\w*|e tamb[ée]m|e depois|al[ée]m disso|sistema|plataforma|complet[oa]|backend|frontend|front e back|full.?stack|testes|telas|m[óo]dulos|camadas|arquivos|crud|api e
|
|
164
|
+
gatilho: /\b(v[áa]rios|v[áa]rias|todos os|todas as|em paralelo|projeto inteiro|refator\w*|migra[çc][ãa]o|auditor\w*|e tamb[ée]m|e depois|al[ée]m disso|sistema|plataforma|complet[oa]|backend|frontend|front e back|full.?stack|testes|telas|m[óo]dulos|camadas|arquivos|crud|api e)\b/i, // sem "site", "app", "aplicativo": casavam com quase todo pedido (13/10)
|
|
158
165
|
},
|
|
159
166
|
{
|
|
160
167
|
nome: 'terminal',
|
|
@@ -189,7 +196,14 @@ function gruposDe(texto, forcar = []) {
|
|
|
189
196
|
// Pedido grande delega mesmo sem a palavra certa. E quem pode construir
|
|
190
197
|
// um app inteiro também pode dividir: as duas saídas vão juntas, e a
|
|
191
198
|
// seção de delegar diz quando é cada uma.
|
|
192
|
-
|
|
199
|
+
/* O TIME SÓ EM PEDIDO GRANDE (13/10: "como fazer a cota não acabar?").
|
|
200
|
+
"Faz um site de portfólio" ganhava o construir_app e o spawn_agent, e o
|
|
201
|
+
modelo os usava: 22 chamadas, 6 de subagente, para um site de uma página.
|
|
202
|
+
Cada subagente é mais pedidos na cota grátis. Pedido pequeno: um agente. */
|
|
203
|
+
const grande = ehGrande(t);
|
|
204
|
+
if (!grande) escolhidos.delete('pipeline');
|
|
205
|
+
// O spawn_agent segue o gatilho dele (backend e frontend, várias telas…); o pedido grande também o ganha.
|
|
206
|
+
if (grande) escolhidos.add('delegar');
|
|
193
207
|
// Antes de clicar, o MCP: quem vai mexer no navegador ou no desktop leva os conectores.
|
|
194
208
|
if (escolhidos.has('navegador') || escolhidos.has('computador')) escolhidos.add('mcp');
|
|
195
209
|
return escolhidos;
|
|
@@ -348,11 +362,27 @@ function referenciasProtegidas(messages) {
|
|
|
348
362
|
return new Set(ultima.values());
|
|
349
363
|
}
|
|
350
364
|
|
|
365
|
+
/* O QUE FOI FEITO NÃO SOME (13/10: "continua lendo e listando coisas
|
|
366
|
+
desnecessárias"). Com ~1.900 tokens de conversa, as leituras velhas saíam
|
|
367
|
+
inteiras e o modelo, sem saber que já tinha lido, lia de novo. Agora o
|
|
368
|
+
resultado encurtado diz o que era, e a rodada descartada vira uma linha no
|
|
369
|
+
pedido: "você já fez isto, não repita". */
|
|
370
|
+
const LEITURAS = new Set(['read_file', 'list_dir', 'glob', 'grep']);
|
|
371
|
+
function chamadaCurta(tc) {
|
|
372
|
+
const f = (tc && tc.function) || {};
|
|
373
|
+
let a = {};
|
|
374
|
+
try { a = JSON.parse(f.arguments || '{}'); } catch { /* argumento ilegível */ }
|
|
375
|
+
const alvo = a.path || a.pattern || a.query || a.command || a.id || '';
|
|
376
|
+
return `${f.name || '?'}${alvo ? ' ' + String(alvo).slice(0, 50) : ''}`;
|
|
377
|
+
}
|
|
378
|
+
|
|
351
379
|
function caberNoTeto(messages, sobra) {
|
|
352
380
|
const conta = (m) => tokensDe(m);
|
|
353
381
|
if (conta(messages) <= sobra) return { messages, cortou: false };
|
|
354
382
|
|
|
355
383
|
let atual = messages.map((m) => ({ ...m }));
|
|
384
|
+
const chamadaDe = new Map();
|
|
385
|
+
for (const m of atual) if (m.role === 'assistant' && Array.isArray(m.tool_calls)) for (const tc of m.tool_calls) chamadaDe.set(tc.id, tc);
|
|
356
386
|
|
|
357
387
|
// 1 e 2: encurtar resultados antigos, em dois apertos.
|
|
358
388
|
const protegidas = referenciasProtegidas(atual);
|
|
@@ -369,6 +399,11 @@ function caberNoTeto(messages, sobra) {
|
|
|
369
399
|
}
|
|
370
400
|
if (m.role !== 'tool' || manterInteiros.has(i)) return m;
|
|
371
401
|
if (texto.length <= limite) return m;
|
|
402
|
+
const tc = chamadaDe.get(m.tool_call_id);
|
|
403
|
+
if (tc && LEITURAS.has((tc.function || {}).name)) {
|
|
404
|
+
const linhas = texto.split('\n').length;
|
|
405
|
+
return { ...m, content: `[já lido: ${chamadaCurta(tc)}, ${linhas} linhas — saiu daqui para caber no plano. Não leia de novo; releia só se for editar ESTE arquivo agora.]` };
|
|
406
|
+
}
|
|
372
407
|
return { ...m, content: texto.slice(0, limite) + '…(encurtado para caber no limite do plano)' };
|
|
373
408
|
});
|
|
374
409
|
if (conta(atual) <= sobra) return { messages: atual, cortou: true, como: 'encurtou resultados antigos' };
|
|
@@ -390,6 +425,15 @@ function caberNoTeto(messages, sobra) {
|
|
|
390
425
|
while (fim < atual.length && atual[fim].role === 'tool') fim++;
|
|
391
426
|
// Nunca engolir tudo: para se sobrar só o pedido do usuário.
|
|
392
427
|
if (fim >= atual.length - 1) break;
|
|
428
|
+
// A rodada sai, o rastro fica: uma linha no último pedido antes dela.
|
|
429
|
+
const feitas = (atual[primeiroAssistente].tool_calls || []).map(chamadaCurta);
|
|
430
|
+
let u = primeiroAssistente - 1;
|
|
431
|
+
while (u >= 0 && atual[u].role !== 'user') u--;
|
|
432
|
+
if (u >= 0 && feitas.length && typeof atual[u].content === 'string') {
|
|
433
|
+
const marca = '\n\n[você já fez, e saiu da conversa para caber no plano: ';
|
|
434
|
+
const c = atual[u].content;
|
|
435
|
+
atual[u] = { ...atual[u], content: c.includes(marca) ? c.replace(/\. Não repita\.\]\s*$/, '; ' + feitas.join('; ') + '. Não repita.]') : c + marca + feitas.join('; ') + '. Não repita.]' };
|
|
436
|
+
}
|
|
393
437
|
atual = [...atual.slice(0, primeiroAssistente), ...atual.slice(fim)];
|
|
394
438
|
}
|
|
395
439
|
|
package/lib/marca-kit.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* marca-kit.js — o KIT DE MARCA: a pessoa cria uma vez, todo vídeo e site usa.
|
|
3
|
+
*
|
|
4
|
+
* ~/.primocode/marcas/<nome>/marca.json nome, cores {fundo,destaque,tinta,apoio},
|
|
5
|
+
* fontes {titulo,texto} (Google Fonts), tom, slogan,
|
|
6
|
+
* site, redes, assinatura (motion preferida, opcional)
|
|
7
|
+
* ~/.primocode/marcas/<nome>/logo.png|svg
|
|
8
|
+
* ~/.primocode/marcas/.ativa nome do kit ativo (global)
|
|
9
|
+
* <projeto>/.primocode/marca fixa o kit naquela pasta (ganha do global)
|
|
10
|
+
*
|
|
11
|
+
* Comandos: `primocode marca criar|listar|usar <nome> [aqui]|mostrar|apagar <nome>` e `/marca`.
|
|
12
|
+
* O Estúdio chama `preencher(marca, {pastas})`: o kit entra por baixo, o que o
|
|
13
|
+
* roteiro já trouxe ganha, campo a campo.
|
|
14
|
+
*/
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
const PAPEIS = ['fundo', 'destaque', 'tinta', 'apoio'];
|
|
20
|
+
|
|
21
|
+
function raiz() { return process.env.PRIMOCODE_MARCAS || path.join(os.homedir(), '.primocode', 'marcas'); }
|
|
22
|
+
|
|
23
|
+
function slug(nome) {
|
|
24
|
+
return String(nome || '').normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()
|
|
25
|
+
.replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40);
|
|
26
|
+
}
|
|
27
|
+
const hexOk = (h) => /^#[0-9a-f]{6}$/i.test(String(h || ''));
|
|
28
|
+
function normHex(h) {
|
|
29
|
+
let s = String(h || '').trim();
|
|
30
|
+
if (/^[0-9a-f]{3}$/i.test(s)) s = '#' + s;
|
|
31
|
+
if (/^#[0-9a-f]{3}$/i.test(s)) s = '#' + s.slice(1).split('').map((x) => x + x).join('');
|
|
32
|
+
if (/^[0-9a-f]{6}$/i.test(s)) s = '#' + s;
|
|
33
|
+
return hexOk(s) ? s.toLowerCase() : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── contraste (WCAG) ─────────────────────────────────────────────────────
|
|
37
|
+
function rgb(h) { const n = parseInt(h.slice(1), 16); return [n >> 16, (n >> 8) & 255, n & 255]; }
|
|
38
|
+
function luz(h) {
|
|
39
|
+
const [r, g, b] = rgb(h).map((v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); });
|
|
40
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
41
|
+
}
|
|
42
|
+
function contraste(a, b) {
|
|
43
|
+
const [x, y] = [luz(a), luz(b)].sort((p, q) => q - p);
|
|
44
|
+
return (x + 0.05) / (y + 0.05);
|
|
45
|
+
}
|
|
46
|
+
function misturar(a, b, t) {
|
|
47
|
+
const A = rgb(a), B = rgb(b);
|
|
48
|
+
return '#' + A.map((v, i) => Math.round(v + (B[i] - v) * t).toString(16).padStart(2, '0')).join('');
|
|
49
|
+
}
|
|
50
|
+
/** A cor mais perto de `cor` que passa de `min` sobre `fundo` (clareia ou escurece em direção ao extremo que contrasta). */
|
|
51
|
+
function ajustar(cor, fundo, min = 4.5) {
|
|
52
|
+
const alvo = luz(fundo) > 0.18 ? '#000000' : '#ffffff';
|
|
53
|
+
for (let t = 0.05; t <= 1.001; t += 0.05) {
|
|
54
|
+
const c = misturar(cor, alvo, t);
|
|
55
|
+
if (contraste(c, fundo) >= min) return c;
|
|
56
|
+
}
|
|
57
|
+
return alvo;
|
|
58
|
+
}
|
|
59
|
+
/** Confere tinta×fundo e destaque×fundo. Devolve [{par, razao, ok, sugestao}]. */
|
|
60
|
+
function validarContraste(cores = {}, min = 4.5) {
|
|
61
|
+
const out = [];
|
|
62
|
+
for (const [par, k] of [['tinta×fundo', 'tinta'], ['destaque×fundo', 'destaque']]) {
|
|
63
|
+
if (!hexOk(cores[k]) || !hexOk(cores.fundo)) continue;
|
|
64
|
+
const razao = Math.round(contraste(cores[k], cores.fundo) * 100) / 100;
|
|
65
|
+
const ok = razao >= min;
|
|
66
|
+
out.push({ par, razao, ok, ...(ok ? {} : { sugestao: { [k]: ajustar(cores[k], cores.fundo, min) } }) });
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── disco ────────────────────────────────────────────────────────────────
|
|
72
|
+
function pastaDe(nome) { const s = slug(nome); return s ? path.join(raiz(), s) : null; }
|
|
73
|
+
function ler(nome) {
|
|
74
|
+
const p = pastaDe(nome);
|
|
75
|
+
if (!p) return null;
|
|
76
|
+
try {
|
|
77
|
+
const m = JSON.parse(fs.readFileSync(path.join(p, 'marca.json'), 'utf8'));
|
|
78
|
+
const logo = ['logo.svg', 'logo.png'].map((f) => path.join(p, f)).find((f) => fs.existsSync(f));
|
|
79
|
+
return { ...m, ...(logo ? { logo } : {}), _pasta: p };
|
|
80
|
+
} catch { return null; }
|
|
81
|
+
}
|
|
82
|
+
function listar() {
|
|
83
|
+
try {
|
|
84
|
+
return fs.readdirSync(raiz(), { withFileTypes: true }).filter((d) => d.isDirectory())
|
|
85
|
+
.map((d) => ler(d.name)).filter(Boolean);
|
|
86
|
+
} catch { return []; }
|
|
87
|
+
}
|
|
88
|
+
function validar(m) {
|
|
89
|
+
const erros = [];
|
|
90
|
+
if (!m || !String(m.nome || '').trim()) erros.push('falta o nome.');
|
|
91
|
+
for (const k of PAPEIS) if (m.cores && m.cores[k] && !hexOk(m.cores[k])) erros.push(`cor ${k} inválida: ${m.cores[k]}`);
|
|
92
|
+
return erros;
|
|
93
|
+
}
|
|
94
|
+
/** Grava o kit. `logoArquivo` (opcional) é copiado para logo.png|svg. */
|
|
95
|
+
function salvar(m, { logoArquivo } = {}) {
|
|
96
|
+
const erros = validar(m);
|
|
97
|
+
if (erros.length) return { ok: false, error: erros.join(' ') };
|
|
98
|
+
const p = pastaDe(m.nome);
|
|
99
|
+
if (!p) return { ok: false, error: 'o nome não serve de pasta.' };
|
|
100
|
+
fs.mkdirSync(p, { recursive: true });
|
|
101
|
+
const { logo, _pasta, ...dados } = m;
|
|
102
|
+
fs.writeFileSync(path.join(p, 'marca.json'), JSON.stringify({ ...dados, criada: dados.criada || new Date().toISOString() }, null, 2) + '\n');
|
|
103
|
+
if (logoArquivo && fs.existsSync(logoArquivo)) {
|
|
104
|
+
const ext = /\.svg$/i.test(logoArquivo) ? '.svg' : '.png';
|
|
105
|
+
for (const e of ['.svg', '.png']) try { fs.unlinkSync(path.join(p, 'logo' + e)); } catch { /* não havia */ }
|
|
106
|
+
fs.copyFileSync(logoArquivo, path.join(p, 'logo' + ext));
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, pasta: p, kit: ler(m.nome) };
|
|
109
|
+
}
|
|
110
|
+
function apagar(nome) {
|
|
111
|
+
const p = pastaDe(nome);
|
|
112
|
+
if (!p || !fs.existsSync(path.join(p, 'marca.json'))) return { ok: false, error: `não há kit "${nome}".` };
|
|
113
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
114
|
+
if (ativaGlobal() === slug(nome)) try { fs.unlinkSync(path.join(raiz(), '.ativa')); } catch { /* ok */ }
|
|
115
|
+
return { ok: true };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── ativa: pino do projeto ganha da global ────────────────────────────────
|
|
119
|
+
function ativaGlobal() { try { return fs.readFileSync(path.join(raiz(), '.ativa'), 'utf8').trim() || null; } catch { return null; } }
|
|
120
|
+
function usar(nome, { aqui = null } = {}) {
|
|
121
|
+
const k = ler(nome);
|
|
122
|
+
if (!k) return { ok: false, error: `não há kit "${nome}". Veja: primocode marca listar` };
|
|
123
|
+
if (aqui) {
|
|
124
|
+
fs.mkdirSync(path.join(aqui, '.primocode'), { recursive: true });
|
|
125
|
+
fs.writeFileSync(path.join(aqui, '.primocode', 'marca'), slug(nome) + '\n');
|
|
126
|
+
return { ok: true, kit: k, onde: 'projeto' };
|
|
127
|
+
}
|
|
128
|
+
fs.mkdirSync(raiz(), { recursive: true });
|
|
129
|
+
fs.writeFileSync(path.join(raiz(), '.ativa'), slug(nome) + '\n');
|
|
130
|
+
return { ok: true, kit: k, onde: 'global' };
|
|
131
|
+
}
|
|
132
|
+
function fixadaEm(pasta) {
|
|
133
|
+
let d = pasta ? path.resolve(pasta) : null;
|
|
134
|
+
for (let i = 0; d && i < 6; i++) {
|
|
135
|
+
try { const n = fs.readFileSync(path.join(d, '.primocode', 'marca'), 'utf8').trim(); if (n) return n; } catch { /* sobe */ }
|
|
136
|
+
const pai = path.dirname(d);
|
|
137
|
+
if (pai === d) break;
|
|
138
|
+
d = pai;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
/** O kit em vigor: pino do primeiro `pastas` que tiver, senão a global. */
|
|
143
|
+
function ativa(pastas = []) {
|
|
144
|
+
for (const p of [].concat(pastas).filter(Boolean)) {
|
|
145
|
+
const n = fixadaEm(p);
|
|
146
|
+
if (n) { const k = ler(n); if (k) return k; }
|
|
147
|
+
}
|
|
148
|
+
const g = ativaGlobal();
|
|
149
|
+
return g ? ler(g) : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* O gancho do Estúdio: preenche a `marca` do JSON com o kit, campo a campo
|
|
154
|
+
* (o que a pessoa/roteiro já pôs ganha). Sem kit ativo, devolve a marca intacta.
|
|
155
|
+
*/
|
|
156
|
+
function preencher(marca, { pastas = [] } = {}) {
|
|
157
|
+
const k = ativa(pastas);
|
|
158
|
+
if (!k) return marca;
|
|
159
|
+
const m = marca && typeof marca === 'object' ? marca : {};
|
|
160
|
+
const temCores = m.cores && (Array.isArray(m.cores) ? m.cores.length : Object.keys(m.cores).length);
|
|
161
|
+
const temFontes = m.fontes && (Array.isArray(m.fontes) ? m.fontes.length : Object.keys(m.fontes).length);
|
|
162
|
+
return {
|
|
163
|
+
...m,
|
|
164
|
+
nome: m.nome || k.nome,
|
|
165
|
+
cores: temCores ? (Array.isArray(m.cores) ? m.cores : { ...(k.cores || {}), ...m.cores }) : { ...(k.cores || {}) },
|
|
166
|
+
fontes: temFontes ? (Array.isArray(m.fontes) ? m.fontes : { ...(k.fontes || {}), ...m.fontes }) : { ...(k.fontes || {}) },
|
|
167
|
+
...(m.logo || k.logo ? { logo: m.logo || k.logo } : {}),
|
|
168
|
+
...(k.slogan && !m.slogan ? { slogan: k.slogan } : {}),
|
|
169
|
+
...(k.tom && !m.tom ? { tom: k.tom } : {}),
|
|
170
|
+
...(k.assinatura && !m.assinatura ? { assinatura: k.assinatura } : {}),
|
|
171
|
+
kit: k.nome,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── do site: logo e cores com o que já existe ────────────────────────────
|
|
176
|
+
async function doSite(entrada, { identidade = require('./estudio/identidade.js') } = {}) {
|
|
177
|
+
let url = String(entrada || '').trim();
|
|
178
|
+
if (!url) return { ok: false, error: 'sem site.' };
|
|
179
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
180
|
+
if (!/\./.test(url)) { const a = await identidade.acharSite(url); if (!a || !a.ok) return { ok: false, error: (a && a.error) || 'não achei o site.' }; url = a.url || a.site; }
|
|
181
|
+
else url = 'https://' + url;
|
|
182
|
+
}
|
|
183
|
+
const r = await identidade.analisar(url);
|
|
184
|
+
if (!r.ok) return r;
|
|
185
|
+
const lista = ((r.cores && r.cores.marca) || []).map((x) => x.cor || x).filter(hexOk);
|
|
186
|
+
const todas = lista.length ? lista : ((r.cores && r.cores.todas) || []).map((x) => x.cor || x).filter(hexOk);
|
|
187
|
+
const cores = require('./estudio/marca.js').marcaEmObjeto({ cores: todas }).cores || {};
|
|
188
|
+
const f = r.fontes || {};
|
|
189
|
+
const nomes = [...(f.doGoogleFonts || []), ...(f.declaradas || []).map((x) => x.fonte)];
|
|
190
|
+
const unicas = [...new Set(nomes)];
|
|
191
|
+
const fontes = { ...(unicas[0] ? { titulo: unicas[0] } : {}), ...(unicas[0] ? { texto: unicas[1] || unicas[0] } : {}) };
|
|
192
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'marca-'));
|
|
193
|
+
let logoArquivo = null;
|
|
194
|
+
try {
|
|
195
|
+
const l = await identidade.pegarLogo(r.html || '', r.url, { pasta: tmp, nome: 'kit' });
|
|
196
|
+
if (l && l.ok && l.src) logoArquivo = path.join(tmp, l.src);
|
|
197
|
+
} catch { /* sem logo: a pessoa manda depois */ }
|
|
198
|
+
return { ok: true, url: r.url, nome: r.marca || '', slogan: r.descricao || '', cores, fontes, logoArquivo };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ── criar (interativo curto) ─────────────────────────────────────────────
|
|
202
|
+
/**
|
|
203
|
+
* `perguntar(texto, padrao) → Promise<string>` e `dizer(texto)` são injetáveis (teste).
|
|
204
|
+
* Fluxo: nome → site (opcional) → confirma cores/fonte/logo → contraste → grava.
|
|
205
|
+
*/
|
|
206
|
+
async function criar({ perguntar, dizer = console.log, deps = {} } = {}) {
|
|
207
|
+
const nome = String(await perguntar('Nome da marca', '')).trim();
|
|
208
|
+
if (!nome) return { ok: false, error: 'sem nome, sem kit.' };
|
|
209
|
+
const site = String(await perguntar('Site (opcional, Enter pula)', '')).trim();
|
|
210
|
+
let achado = { cores: {}, fontes: {}, logoArquivo: null, slogan: '' };
|
|
211
|
+
if (site) {
|
|
212
|
+
dizer('Lendo o site...');
|
|
213
|
+
const r = await doSite(site, deps);
|
|
214
|
+
if (r.ok) { achado = r; dizer(`Achei: ${Object.entries(r.cores).map(([k, v]) => `${k} ${v}`).join(' · ') || 'sem cores'}${r.fontes.titulo ? ` · fonte ${r.fontes.titulo}` : ''}${r.logoArquivo ? ' · logo' : ' · sem logo'}`); }
|
|
215
|
+
else dizer(`Não deu pra ler o site (${r.error}). Sigo com o que você disser.`);
|
|
216
|
+
}
|
|
217
|
+
const cores = {};
|
|
218
|
+
for (const k of PAPEIS) {
|
|
219
|
+
const padrao = achado.cores[k] || (k === 'fundo' ? '#0b0b0f' : k === 'tinta' ? '#f5f5f5' : k === 'destaque' ? '#ff5a1f' : '');
|
|
220
|
+
const v = normHex(await perguntar(`Cor ${k}${k === 'apoio' ? ' (opcional)' : ''}`, padrao));
|
|
221
|
+
if (v) cores[k] = v;
|
|
222
|
+
}
|
|
223
|
+
let sugeriu = [];
|
|
224
|
+
for (let volta = 0; volta < 2; volta++) {
|
|
225
|
+
const ruins = validarContraste(cores).filter((x) => !x.ok);
|
|
226
|
+
if (!ruins.length) break;
|
|
227
|
+
for (const x of ruins) {
|
|
228
|
+
const [k, v] = Object.entries(x.sugestao)[0];
|
|
229
|
+
dizer(`Contraste baixo em ${x.par}: ${x.razao} (o mínimo é 4,5). Sugiro ${k} ${v}.`);
|
|
230
|
+
const ok = /^(s|sim|y|yes|)$/i.test(String(await perguntar(`Trocar ${k} por ${v}? (s/n)`, 's')).trim());
|
|
231
|
+
if (ok) { cores[k] = v; sugeriu.push(k); }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const titulo = String(await perguntar('Fonte do título (Google Fonts)', achado.fontes.titulo || '')).trim();
|
|
235
|
+
const texto = String(await perguntar('Fonte do texto (Google Fonts)', achado.fontes.texto || titulo)).trim();
|
|
236
|
+
const slogan = String(await perguntar('Slogan (opcional)', '')).trim();
|
|
237
|
+
const tom = String(await perguntar('Tom de voz (opcional, ex.: direto e simpático)', '')).trim();
|
|
238
|
+
const redes = String(await perguntar('@redes (opcional)', '')).trim();
|
|
239
|
+
let logoArquivo = achado.logoArquivo;
|
|
240
|
+
if (!logoArquivo) {
|
|
241
|
+
const l = String(await perguntar('Arquivo do logo (png/svg, opcional)', '')).trim().replace(/^~(?=\/)/, os.homedir());
|
|
242
|
+
if (l && fs.existsSync(l)) logoArquivo = l; else if (l) dizer('Não achei esse arquivo; fica sem logo.');
|
|
243
|
+
}
|
|
244
|
+
const r = salvar({
|
|
245
|
+
nome, cores,
|
|
246
|
+
...((titulo || texto) ? { fontes: { ...(titulo ? { titulo } : {}), ...(texto ? { texto } : {}) } } : {}),
|
|
247
|
+
...(slogan ? { slogan } : {}), ...(tom ? { tom } : {}), ...(site ? { site } : {}), ...(redes ? { redes: redes.split(/[\s,]+/).filter(Boolean) } : {}),
|
|
248
|
+
}, { logoArquivo });
|
|
249
|
+
if (r.ok) r.contraste = validarContraste(cores);
|
|
250
|
+
return r;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ── texto e CLI ──────────────────────────────────────────────────────────
|
|
254
|
+
function mostrar(k) {
|
|
255
|
+
if (!k) return 'Nenhum kit ativo. Crie um: primocode marca criar';
|
|
256
|
+
const c = k.cores || {}, f = k.fontes || {};
|
|
257
|
+
const lin = [`Marca: ${k.nome}`,
|
|
258
|
+
` cores ${PAPEIS.filter((p) => c[p]).map((p) => `${p} ${c[p]}`).join(' · ') || '-'}`,
|
|
259
|
+
` fontes ${f.titulo || '-'} / ${f.texto || '-'}`,
|
|
260
|
+
` logo ${k.logo || '-'}`];
|
|
261
|
+
for (const [r, v] of [['slogan', k.slogan], ['tom', k.tom], ['site', k.site], ['redes', (k.redes || []).join(' ')], ['motion', k.assinatura]]) if (v) lin.push(` ${r.padEnd(7)} ${v}`);
|
|
262
|
+
const ruins = validarContraste(c).filter((x) => !x.ok);
|
|
263
|
+
for (const x of ruins) lin.push(` ! contraste ${x.par} ${x.razao} (< 4,5)`);
|
|
264
|
+
return lin.join('\n');
|
|
265
|
+
}
|
|
266
|
+
function tela(lista, ativaNome) {
|
|
267
|
+
return lista.length ? lista.map((k) => `${slug(k.nome) === ativaNome ? '●' : ' '} ${k.nome}${k.site ? ' ' + k.site : ''}`).join('\n') : 'Nenhum kit ainda. Crie um: primocode marca criar';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function perguntarNoTerminal() {
|
|
271
|
+
const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
272
|
+
const ask = (t, p) => new Promise((res) => rl.question(`${t}${p ? ` [${p}]` : ''}: `, (a) => res(a.trim() || p || '')));
|
|
273
|
+
return { ask, fechar: () => rl.close() };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** `primocode marca <op>`; o /marca da sessão chama o mesmo, com `perguntar` próprio. */
|
|
277
|
+
async function main(argv, { perguntar, dizer = console.log, pasta = process.cwd(), deps } = {}) {
|
|
278
|
+
const [op = 'mostrar', ...resto] = argv;
|
|
279
|
+
const nome = resto.filter((x) => x !== 'aqui').join(' ').trim();
|
|
280
|
+
const aqui = resto.includes('aqui');
|
|
281
|
+
switch (op) {
|
|
282
|
+
case 'criar': {
|
|
283
|
+
const t = perguntar ? null : perguntarNoTerminal();
|
|
284
|
+
try {
|
|
285
|
+
const r = await criar({ perguntar: perguntar || t.ask, dizer, deps });
|
|
286
|
+
if (!r.ok) { dizer(r.error); return 1; }
|
|
287
|
+
dizer(`Kit "${r.kit.nome}" salvo em ${r.pasta}. Ativar: primocode marca usar ${r.kit.nome}`);
|
|
288
|
+
return 0;
|
|
289
|
+
} finally { if (t) t.fechar(); }
|
|
290
|
+
}
|
|
291
|
+
case 'listar': case 'ls': dizer(tela(listar(), ativaGlobal())); return 0;
|
|
292
|
+
case 'usar': {
|
|
293
|
+
if (!nome) { dizer('Qual? primocode marca usar <nome> [aqui]'); return 1; }
|
|
294
|
+
const r = usar(nome, { aqui: aqui ? pasta : null });
|
|
295
|
+
dizer(r.ok ? `Marca "${r.kit.nome}" ${r.onde === 'projeto' ? 'fixada nesta pasta' : 'ativa'}.` : r.error);
|
|
296
|
+
return r.ok ? 0 : 1;
|
|
297
|
+
}
|
|
298
|
+
case 'mostrar': case 'ver': dizer(mostrar(nome ? ler(nome) : ativa([pasta]))); return 0;
|
|
299
|
+
case 'apagar': case 'rm': {
|
|
300
|
+
if (!nome) { dizer('Qual? primocode marca apagar <nome>'); return 1; }
|
|
301
|
+
const r = apagar(nome);
|
|
302
|
+
dizer(r.ok ? `Kit "${nome}" apagado.` : r.error);
|
|
303
|
+
return r.ok ? 0 : 1;
|
|
304
|
+
}
|
|
305
|
+
default: dizer('Uso: primocode marca criar | listar | usar <nome> [aqui] | mostrar [nome] | apagar <nome>'); return 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = { raiz, slug, normHex, contraste, ajustar, validarContraste, ler, listar, salvar, apagar, usar, ativa, fixadaEm, preencher, doSite, criar, mostrar, main };
|