primocode 7.18.0 → 8.0.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/bin/primocode.js CHANGED
File without changes
package/lib/catalogo.js CHANGED
@@ -53,6 +53,18 @@ const GRUPOS = [
53
53
  { tool: 'open_in_browser', escreve: false, faz: 'abre no seu navegador normal, sem controlar' },
54
54
  ],
55
55
  },
56
+ {
57
+ nome: 'Estúdio',
58
+ resumo: 'a suíte de criação do PrimoCode — sobe sozinha, não precisa de app externo',
59
+ itens: [
60
+ { tool: 'studio_manual', escreve: false, faz: 'lê o manual do estúdio e as seis ferramentas' },
61
+ { tool: 'studio_spec', escreve: false, faz: 'lê a spec de uma ferramenta (prisma, tela, prosa…)' },
62
+ { tool: 'studio_criar', escreve: false, faz: 'cria slides, arte, documento, planilha, vídeo ou diagrama' },
63
+ { tool: 'studio_ler', escreve: false, faz: 'lê algo que já foi criado' },
64
+ { tool: 'studio_atualizar', escreve: false, faz: 'ajusta o que já existe, sem refazer do zero' },
65
+ { tool: 'studio_listar', escreve: false, faz: 'lista o que já existe no estúdio' },
66
+ ],
67
+ },
56
68
  {
57
69
  nome: 'Computador',
58
70
  resumo: 'janelas, botões, mouse e teclado — age em qualquer programa e na área de trabalho',
package/lib/studio.js ADDED
@@ -0,0 +1,198 @@
1
+ /**
2
+ * studio.js — o PrimoCode Studio, ligado ao agente.
3
+ *
4
+ * O Studio é a suíte de criação que vem junto com o PrimoCode (pasta studio/):
5
+ * Prisma (apresentações), Tela (design), Prosa (documentos), Grade (planilhas),
6
+ * Corte (vídeo) e Traço (diagramas). É um servidor Python de biblioteca padrão,
7
+ * sem nada para instalar.
8
+ *
9
+ * ── POR QUE ESTE MÓDULO EXISTE ───────────────────────────────────────────
10
+ * Medido em uso real: o agente sabia do Studio, tentou falar com ele em
11
+ * localhost:7777, tomou "conexão recusada" e DESISTIU — respondeu ao usuário
12
+ * que "o estúdio não está rodando na sua máquina". O usuário não tem que ligar
13
+ * ferramenta nenhuma: ele pediu um vídeo, quer o vídeo.
14
+ *
15
+ * Então aqui o Studio sobe SOZINHO, na primeira vez que o agente precisa dele.
16
+ * `garantirNoAr()` é chamado antes de toda tool studio_*, e o modelo nem sabe
17
+ * que houve um passo a mais.
18
+ *
19
+ * ── POR QUE ELE RODA DE ~/.primocode/studio, E NÃO DE DENTRO DO PACOTE ────
20
+ * O Studio grava o que cria (arquivos/ e midia/) ao lado do server.py. Com o
21
+ * pacote instalado global (`npm i -g`), essa pasta costuma ser de sistema e a
22
+ * escrita falharia — e, pior, um `npm update` apagaria o trabalho do usuário.
23
+ * Então o código é copiado para ~/.primocode/studio na primeira vez, e as
24
+ * criações ficam lá, a salvo de atualização do pacote.
25
+ */
26
+
27
+ 'use strict';
28
+
29
+ const http = require('http');
30
+ const fs = require('fs');
31
+ const path = require('path');
32
+ const os = require('os');
33
+ const { spawn, execSync } = require('child_process');
34
+
35
+ const ORIGEM = path.join(__dirname, '..', 'studio');
36
+ const CASA = path.join(os.homedir(), '.primocode', 'studio');
37
+ const PORTA = Number(process.env.PRIMOCODE_STUDIO_PORT) || 7777;
38
+ const BASE = `http://127.0.0.1:${PORTA}`;
39
+
40
+ // Só o código do Studio é atualizado; `arquivos/` e `midia/` (o que o usuário
41
+ // criou) nunca são tocados.
42
+ const COPIAVEIS = new Set(['server.py', 'web', 'ferramentas', 'README.md', 'PRIMOCODE.md']);
43
+
44
+ function pythonCmd() {
45
+ const candidatos = os.platform() === 'win32' ? ['python', 'py', 'python3'] : ['python3', 'python'];
46
+ for (const c of candidatos) {
47
+ try {
48
+ const v = execSync(`${c} --version`, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 }).toString();
49
+ if (/Python 3/.test(v)) return c;
50
+ } catch {}
51
+ }
52
+ return null;
53
+ }
54
+
55
+ // Cópia recursiva que só reescreve o que mudou — assim `npm update` traz o
56
+ // Studio novo sem custo, e o que o usuário criou continua onde está.
57
+ function copiar(de, para) {
58
+ const st = fs.statSync(de);
59
+ if (st.isDirectory()) {
60
+ fs.mkdirSync(para, { recursive: true });
61
+ for (const item of fs.readdirSync(de)) copiar(path.join(de, item), path.join(para, item));
62
+ return;
63
+ }
64
+ let precisa = true;
65
+ try {
66
+ const alvo = fs.statSync(para);
67
+ precisa = alvo.size !== st.size || alvo.mtimeMs < st.mtimeMs;
68
+ } catch {}
69
+ if (precisa) fs.copyFileSync(de, para);
70
+ }
71
+
72
+ function prepararCasa() {
73
+ if (!fs.existsSync(ORIGEM)) return { ok: false, error: 'A pasta studio/ não veio no pacote.' };
74
+ fs.mkdirSync(CASA, { recursive: true });
75
+ for (const item of COPIAVEIS) {
76
+ const de = path.join(ORIGEM, item);
77
+ if (fs.existsSync(de)) copiar(de, path.join(CASA, item));
78
+ }
79
+ return { ok: true, casa: CASA };
80
+ }
81
+
82
+ // ── HTTP ─────────────────────────────────────────────────────────────────
83
+ function pedir(metodo, rota, corpo, timeout = 30000) {
84
+ return new Promise((resolve, reject) => {
85
+ const dados = corpo == null ? null : Buffer.from(JSON.stringify(corpo), 'utf8');
86
+ const req = http.request({
87
+ hostname: '127.0.0.1',
88
+ port: PORTA,
89
+ path: rota,
90
+ method: metodo,
91
+ timeout,
92
+ headers: dados ? { 'Content-Type': 'application/json', 'Content-Length': dados.length } : {},
93
+ }, (res) => {
94
+ let texto = '';
95
+ res.setEncoding('utf8');
96
+ res.on('data', (c) => { texto += c; });
97
+ res.on('end', () => {
98
+ if (res.statusCode >= 400) return reject(new Error(`HTTP ${res.statusCode}: ${texto.slice(0, 200)}`));
99
+ try { resolve(JSON.parse(texto)); }
100
+ catch { resolve({ texto }); }
101
+ });
102
+ });
103
+ req.on('timeout', () => { req.destroy(new Error('tempo esgotado falando com o Studio')); });
104
+ req.on('error', reject);
105
+ if (dados) req.write(dados);
106
+ req.end();
107
+ });
108
+ }
109
+
110
+ async function estaNoAr() {
111
+ try { await pedir('GET', '/api/ferramentas', null, 1500); return true; }
112
+ catch { return false; }
113
+ }
114
+
115
+ // ── Subir ────────────────────────────────────────────────────────────────
116
+ // Uma subida por vez: sem esta trava, duas tools chamadas no mesmo turno
117
+ // disparariam dois servidores na mesma porta e o segundo morreria com
118
+ // "address in use" — barulho por nada.
119
+ let subindo = null;
120
+
121
+ async function garantirNoAr() {
122
+ if (await estaNoAr()) return { ok: true, url: BASE, jaEstava: true };
123
+ if (subindo) return subindo;
124
+
125
+ subindo = (async () => {
126
+ const casa = prepararCasa();
127
+ if (!casa.ok) return casa;
128
+
129
+ const py = pythonCmd();
130
+ if (!py) {
131
+ return {
132
+ ok: false,
133
+ error: 'O Studio precisa do Python 3, que não está instalado.',
134
+ comoResolver: os.platform() === 'darwin'
135
+ ? 'Instale com: brew install python3 (ou baixe em python.org).'
136
+ : 'Instale o Python 3 (python.org) e tente de novo.',
137
+ };
138
+ }
139
+
140
+ // Solto do processo do CLI: o Studio continua de pé quando a sessão
141
+ // fecha, que é o que o usuário espera de um app aberto.
142
+ const filho = spawn(py, ['server.py', String(PORTA)], {
143
+ cwd: CASA,
144
+ detached: true,
145
+ stdio: 'ignore',
146
+ });
147
+ filho.unref();
148
+
149
+ // Espera ficar de pé (~8s no pior caso). Máquina lenta continua sendo
150
+ // atendida; máquina rápida sai na primeira volta.
151
+ for (let i = 0; i < 40; i++) {
152
+ await new Promise((r) => setTimeout(r, 200));
153
+ if (await estaNoAr()) return { ok: true, url: BASE, subiuAgora: true };
154
+ }
155
+ return { ok: false, error: `O Studio não respondeu em ${BASE} depois de subir. Veja se a porta ${PORTA} está livre.` };
156
+ })();
157
+
158
+ try { return await subindo; }
159
+ finally { subindo = null; }
160
+ }
161
+
162
+ // ── O que o agente usa ───────────────────────────────────────────────────
163
+ // Toda função garante o Studio no ar ANTES de falar com ele. É isto que faz o
164
+ // "abre o Canva e faz um post" virar um post pronto, sem o usuário ligar nada.
165
+
166
+ async function comStudio(fn) {
167
+ const no = await garantirNoAr();
168
+ if (!no.ok) return no;
169
+ return fn();
170
+ }
171
+
172
+ const manual = () => comStudio(async () => ({ ok: true, ...(await pedir('GET', '/api/manual')) }));
173
+ const ferramentas = () => comStudio(async () => ({ ok: true, ferramentas: await pedir('GET', '/api/ferramentas') }));
174
+ const spec = (slug) => comStudio(async () => ({ ok: true, spec: await pedir('GET', `/api/ferramentas/${encodeURIComponent(slug)}`) }));
175
+ const listar = () => comStudio(async () => ({ ok: true, arquivos: await pedir('GET', '/api/arquivos') }));
176
+ const ler = (id) => comStudio(async () => ({ ok: true, arquivo: await pedir('GET', `/api/arquivos/${encodeURIComponent(id)}`) }));
177
+
178
+ const criar = ({ ferramenta, titulo, doc }) => comStudio(async () => {
179
+ if (!ferramenta) return { ok: false, error: 'Diga a ferramenta: prisma, tela, prosa, grade, corte ou traco.' };
180
+ if (!doc) return { ok: false, error: 'Falta o doc. Leia a spec com studio_spec antes de criar.' };
181
+ const r = await pedir('POST', '/api/arquivos', { ferramenta, titulo: titulo || 'Sem título', doc });
182
+ return { ok: true, ...r };
183
+ });
184
+
185
+ const atualizar = ({ id, titulo, doc }) => comStudio(async () => {
186
+ if (!id) return { ok: false, error: 'Falta o id do arquivo.' };
187
+ const corpo = {};
188
+ if (titulo != null) corpo.titulo = titulo;
189
+ if (doc != null) corpo.doc = doc;
190
+ const r = await pedir('PUT', `/api/arquivos/${encodeURIComponent(id)}`, corpo);
191
+ return { ok: true, ...r };
192
+ });
193
+
194
+ module.exports = {
195
+ BASE, PORTA, CASA,
196
+ garantirNoAr, estaNoAr, prepararCasa, pythonCmd,
197
+ manual, ferramentas, spec, listar, ler, criar, atualizar,
198
+ };
package/lib/tools.js CHANGED
@@ -14,6 +14,7 @@ const { exec } = require('child_process');
14
14
  const browser = require('./browser');
15
15
  const desktop = require('./desktop');
16
16
  const referencia = require('./referencia');
17
+ const studio = require('./studio');
17
18
 
18
19
  const IGNORAR = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.cache', 'vendor']);
19
20
  const MAX_ARQUIVOS_VARREDURA = 5000;
@@ -446,6 +447,20 @@ async function despachar(name, args, ctx) {
446
447
  return desktop.scroll(args.dy != null ? args.dy : 300);
447
448
  case 'desktop_size':
448
449
  return desktop.getScreenSize();
450
+ // ── PrimoCode Studio — a suíte de criação (sobe sozinha) ───────
451
+ case 'studio_manual':
452
+ return studio.manual();
453
+ case 'studio_spec':
454
+ return studio.spec(args.ferramenta || args.slug || '');
455
+ case 'studio_criar':
456
+ return studio.criar({ ferramenta: args.ferramenta, titulo: args.titulo, doc: args.doc });
457
+ case 'studio_ler':
458
+ return studio.ler(args.id);
459
+ case 'studio_atualizar':
460
+ return studio.atualizar({ id: args.id, titulo: args.titulo, doc: args.doc });
461
+ case 'studio_listar':
462
+ return studio.listar();
463
+
449
464
  case 'desktop_double_click':
450
465
  return desktop.doubleClick(args.x, args.y);
451
466
  case 'desktop_open_app':
package/lib/ui.js CHANGED
@@ -310,11 +310,13 @@ function actionHeader(name, args) {
310
310
  desktop_drag:'Desktop·drag', desktop_type:'Desktop·type', desktop_key:'Desktop·key',
311
311
  desktop_scroll:'Desktop·scroll', desktop_size:'Desktop·size',
312
312
  desktop_double_click:'Desktop·2click', desktop_open_app:'Desktop·app',
313
+ studio_manual:'Estúdio·manual', studio_spec:'Estúdio·spec', studio_criar:'Estúdio·criar',
314
+ studio_ler:'Estúdio·ler', studio_atualizar:'Estúdio·ajustar', studio_listar:'Estúdio·lista',
313
315
  desktop_windows:'Desktop·janelas', desktop_focus:'Desktop·foco',
314
316
  desktop_elements:'Desktop·elementos', desktop_click_element:'Desktop·click',
315
317
  };
316
318
  const label = nice[name] || name;
317
- const argStr = args ? (args.consulta || args.path || args.pattern || args.command || args.target || args.url || args.selector || args.text || args.app || args.window || args.name || args.description || (args.todos ? `${args.todos.length} tarefas` : '') || (args.x != null ? `${args.x},${args.y}` : '') || args.key || JSON.stringify(args).slice(0,60)) : '';
319
+ const argStr = args ? (args.consulta || args.path || args.pattern || args.command || args.target || args.url || args.selector || args.text || args.app || args.window || args.name || args.ferramenta || args.titulo || args.description || (args.todos ? `${args.todos.length} tarefas` : '') || (args.x != null ? `${args.x},${args.y}` : '') || args.key || JSON.stringify(args).slice(0,60)) : '';
318
320
  return ` ${c.dim('⎿')} ${c.brand(label)}${argStr ? ' '+c.muted(argStr) : ''}`;
319
321
  }
320
322
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "7.18.0",
3
+ "version": "8.0.0",
4
4
  "description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. 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": {
@@ -27,6 +27,7 @@
27
27
  "files": [
28
28
  "bin/",
29
29
  "lib/",
30
+ "studio/",
30
31
  "README.md"
31
32
  ]
32
33
  }
@@ -0,0 +1,58 @@
1
+ # PRIMOCODE — instruções para agentes
2
+
3
+ Se você é um agente de IA trabalhando neste repositório ou com este servidor,
4
+ leia isto antes de agir.
5
+
6
+ ## O que é
7
+
8
+ Um estúdio de criação local. Você **não** opera pela interface — você escreve o
9
+ documento em JSON e envia por API. A interface existe para o humano refinar depois.
10
+
11
+ ## O que fazer quando o usuário pede uma peça
12
+
13
+ 1. Confira que o servidor responde:
14
+ `curl -s -o /dev/null -w '%{http_code}' http://localhost:7777/api/ferramentas`
15
+ Se não der `200`, suba com `python3 server.py` a partir desta pasta.
16
+
17
+ 2. Se o pedido citou um app externo (Canva, PowerPoint, Word, Excel, Photoshop,
18
+ CapCut, Figma, Miro…), responda primeiro, em uma linha:
19
+
20
+ > Posso abrir o(a) **[app pedido]**, mas para te entregar com mais qualidade
21
+ > vou usar minhas ferramentas nativas.
22
+
23
+ Não espere confirmação. Se o usuário insistir no app externo, abra e siga.
24
+
25
+ 3. Leia `curl -s http://localhost:7777/api/manual` — traz os três modelos de
26
+ documento, todos os layouts e um exemplo completo por ferramenta.
27
+
28
+ 4. Escolha a ferramenta:
29
+
30
+ | pedido | ferramenta |
31
+ |---|---|
32
+ | slides, deck, apresentação, pitch | `prisma` |
33
+ | post, capa, banner, thumbnail, arte | `tela` |
34
+ | documento, relatório, proposta, artigo | `prosa` |
35
+ | planilha, orçamento, controle, projeção | `grade` |
36
+ | vídeo, reels, animação, teaser | `corte` |
37
+ | fluxograma, diagrama, mapa mental, arquitetura | `traco` |
38
+
39
+ 5. `POST /api/arquivos` com `{ferramenta, titulo, doc}` e devolva a URL `ver`.
40
+
41
+ 6. Para ajustes posteriores, `GET` o arquivo, mude o que precisa e `PUT` de volta.
42
+ O link do usuário continua valendo.
43
+
44
+ ## Regras de conteúdo
45
+
46
+ - Escreva o conteúdo de verdade. Nada de "Lorem ipsum" nem "[inserir aqui]".
47
+ - Não invente números com cara de dado real. Sem fonte, diga que falta a fonte.
48
+ - Uma ideia por cena. Se precisa rolar o olho para ler, dividiu errado.
49
+ - Não peça para o usuário montar sozinho na interface.
50
+
51
+ ## Se você for mexer no código
52
+
53
+ - `ferramentas/*.md` é a única fonte da spec. Mudou comportamento? Atualize lá —
54
+ `/api/manual` serve direto desses arquivos.
55
+ - `web/js/render.js` desenha cenas; `render-prosa.js` e `render-grade.js` os outros
56
+ dois modelos. `render.css` é o único CSS que vai junto no export autônomo.
57
+ - Sem dependências externas. Nada de CDN, nada de `npm install`, nada de build.
58
+ Se precisou de uma biblioteca, escreva a função.
@@ -0,0 +1,154 @@
1
+ <div align="center">
2
+
3
+ # PrimoCode Studio
4
+
5
+ **O estúdio de criação que a IA opera.**
6
+
7
+ Seis ferramentas nativas, um modelo de documento em comum, zero dependências.
8
+ A IA escreve o documento, o estúdio desenha, você refina à mão.
9
+
10
+ </div>
11
+
12
+ ---
13
+
14
+ ## Rodar
15
+
16
+ ```bash
17
+ python3 server.py
18
+ ```
19
+
20
+ Abra <http://localhost:7777>. Só precisa de Python 3 — nada para instalar,
21
+ nada para compilar, funciona offline.
22
+
23
+ Outra porta: `python3 server.py 8080`.
24
+
25
+ ## As ferramentas
26
+
27
+ | Ferramenta | Papel | No lugar de |
28
+ |---|---|---|
29
+ | **Prisma** | Apresentações | PowerPoint, Keynote, Canva Apresentações |
30
+ | **Tela** | Design gráfico | Photoshop, Figma, Canva |
31
+ | **Prosa** | Documentos | Word, Google Docs, Notion |
32
+ | **Grade** | Planilhas | Excel, Google Sheets |
33
+ | **Corte** | Vídeo | CapCut, Premiere, After Effects |
34
+ | **Traço** | Diagramas | Miro, Whimsical, Lucidchart |
35
+
36
+ Cada uma tem editor completo — trilho de inserção, camadas, inspetor de
37
+ propriedades, arrastar e redimensionar, desfazer, edição no lugar — e todas
38
+ compartilham o mesmo modelo de documento, o que faz a IA aprender uma vez e
39
+ operar as seis.
40
+
41
+ ## Como a IA usa
42
+
43
+ Ela não clica em nada. Lê o manual, escreve o documento e devolve o link:
44
+
45
+ ```bash
46
+ curl -s http://localhost:7777/api/manual
47
+
48
+ curl -s -X POST http://localhost:7777/api/arquivos \
49
+ -H 'Content-Type: application/json' \
50
+ -d '{"ferramenta":"prisma","titulo":"Energia solar","doc":{
51
+ "tema":"meia-noite",
52
+ "cenas":[{"layout":"capa","fundo":"malha","elementos":[
53
+ {"tipo":"texto","papel":"chapeu","texto":"Panorama 2026"},
54
+ {"tipo":"texto","papel":"titulo","texto":"Energia solar no Brasil"}]}]}}'
55
+ ```
56
+
57
+ A resposta traz `ver` (tela cheia) e `editar` (estúdio). O usuário recebe um link.
58
+
59
+ ### A regra de abertura
60
+
61
+ Quando alguém pede "abre o Canva", "abre o PowerPoint", o agente responde:
62
+
63
+ > Posso abrir o(a) **[app pedido]**, mas para te entregar com mais qualidade vou
64
+ > usar minhas ferramentas nativas.
65
+
66
+ E entrega o arquivo pronto em vez de um app vazio.
67
+
68
+ ## A pasta `ferramentas/`
69
+
70
+ É a fonte da verdade. O servidor lê os `.md` de lá e serve tudo junto em
71
+ `/api/manual` — não existe spec duplicada. Editar um arquivo muda o que a IA
72
+ sabe na hora, sem reiniciar nada.
73
+
74
+ ```
75
+ ferramentas/
76
+ 00-protocolo.md comportamento do agente, frase de abertura, tabela de equivalência
77
+ 01-modelos.md os três modelos: cena, documento, planilha
78
+ prisma.md tela.md prosa.md grade.md corte.md traco.md
79
+ ```
80
+
81
+ Para Claude Code há também `.claude/skills/primocode/` — a skill dispara sozinha
82
+ quando alguém fala em slides, arte, documento, planilha, vídeo ou diagrama.
83
+
84
+ ## Os três modelos
85
+
86
+ Aprenda `cena` e você opera quatro ferramentas.
87
+
88
+ ```json
89
+ { "tema": "meia-noite", "largura": 1920, "altura": 1080,
90
+ "cenas": [{ "layout": "capa", "fundo": "malha", "elementos": [
91
+ { "tipo": "texto", "papel": "titulo", "texto": "Título" }
92
+ ]}]}
93
+ ```
94
+
95
+ Com `layout` nomeado, o estúdio posiciona: `capa` `titulo` `secao` `duas-colunas`
96
+ `tres-colunas` `numeros` `citacao` `imagem-direita` `imagem-esquerda` `centro`.
97
+ Com `layout: "livre"`, você define `x`, `y`, `l`, `a` — é o canvas do Tela e do Traço.
98
+
99
+ Elementos: `texto` `lista` `cartoes` `numeros` `tabela` `citacao` `codigo`
100
+ `grafico` `imagem` `icone` `forma` `no` `conector`.
101
+
102
+ `documento` (Prosa) é uma lista de blocos. `planilha` (Grade) tem abas com
103
+ fórmulas de verdade — `=SOMA(D1:D4)`, `=ARRED(D1/B1*100)` — resolvidas no
104
+ navegador e também no export CSV.
105
+
106
+ Detalhe completo em [`ferramentas/01-modelos.md`](ferramentas/01-modelos.md).
107
+
108
+ ## API
109
+
110
+ | Método | Rota | O que faz |
111
+ |---|---|---|
112
+ | `GET` | `/api/manual` | manual completo, em um fetch |
113
+ | `GET` | `/api/ferramentas` | as seis, em JSON |
114
+ | `GET` | `/api/ferramentas/<slug>` | spec de uma |
115
+ | `GET` | `/api/arquivos` | lista |
116
+ | `POST` | `/api/arquivos` | cria — `{ferramenta, titulo, doc}` |
117
+ | `GET` `PUT` `DELETE` | `/api/arquivos/<id>` | lê, atualiza, apaga |
118
+ | `GET` | `/api/arquivos/<id>/export` | `.html` autônomo · `.csv` (Grade) · `.md` (Prosa) |
119
+
120
+ O export HTML é um arquivo único com CSS, renderizadores e documento embutidos —
121
+ zero requisição externa, abre offline, dá para mandar por e-mail.
122
+
123
+ ## Atalhos no modo Abrir
124
+
125
+ `→` `espaço` avança · `←` volta · `K` toca/pausa (Corte) · `F` tela cheia ·
126
+ `P` imprime ou salva em PDF, uma cena por página.
127
+
128
+ ## Estrutura
129
+
130
+ ```
131
+ server.py servidor e API — biblioteca padrão do Python
132
+ web/
133
+ core.css interface do estúdio
134
+ render.css aparência do que é criado (vai junto no export)
135
+ home.html biblioteca e lançador
136
+ tool.html editor
137
+ view.html apresentação e reprodução
138
+ js/
139
+ icones.js ~60 ícones em SVG
140
+ render.js desenha cenas: layouts, elementos, gráficos, conectores
141
+ render-prosa.js desenha documentos
142
+ render-grade.js desenha planilhas e resolve fórmulas
143
+ editor.js o estúdio manual
144
+ player.js apresentar, reproduzir, imprimir
145
+ ferramentas.js o que cada ferramenta oferece na interface
146
+ ferramentas/ a spec que a IA lê
147
+ arquivos/ o que foi criado, um JSON por peça
148
+ ```
149
+
150
+ Os arquivos ficam em `arquivos/*.json` — texto puro, versionável, diffável.
151
+
152
+ ## Licença
153
+
154
+ MIT.
@@ -0,0 +1,111 @@
1
+ # PROTOCOLO — como o agente age
2
+
3
+ O PrimoCode Studio roda em `http://localhost:7777`. O agente cria o arquivo por API,
4
+ sem abrir a interface, e devolve o link pronto. O usuário nunca precisa saber que
5
+ existe uma API.
6
+
7
+ ## Regra de abertura
8
+
9
+ Quando o usuário pedir para **abrir um app externo** — Canva, PowerPoint, Keynote,
10
+ Word, Google Docs, Excel, Sheets, Photoshop, Figma, CapCut, Premiere, Miro — responda
11
+ com esta frase, trocando o que estiver entre colchetes:
12
+
13
+ > Posso abrir o(a) **[ferramenta pedida]**, mas para te entregar com mais qualidade
14
+ > vou usar minhas ferramentas nativas.
15
+
16
+ Depois siga direto para o fluxo abaixo. Não espere confirmação — a frase avisa, não pergunta.
17
+ Se o usuário insistir no app externo, abra e pare de insistir.
18
+
19
+ Quando o pedido já vier sem citar app ("faz uns slides sobre X", "monta uma planilha
20
+ de custos"), pule a frase e vá direto ao fluxo.
21
+
22
+ ## Fluxo
23
+
24
+ 1. **Escolha a ferramenta** pela tabela de equivalência abaixo.
25
+ 2. **Leia a spec** da ferramenta: `GET /api/ferramentas/<slug>` (ou o arquivo `.md` desta pasta).
26
+ 3. **Escreva o conteúdo de verdade.** Pesquise se precisar. Nada de "Lorem ipsum",
27
+ nada de "[inserir dado aqui]". Números redondos inventados são pior que nenhum número.
28
+ 4. **Crie o arquivo:**
29
+
30
+ ```bash
31
+ curl -s -X POST http://localhost:7777/api/arquivos \
32
+ -H 'Content-Type: application/json' \
33
+ -d '{"ferramenta":"prisma","titulo":"...","doc":{...}}'
34
+ ```
35
+
36
+ 5. **Devolva ao usuário** a URL `ver` da resposta, em uma linha. Ofereça a `editar`
37
+ se ele quiser mexer à mão.
38
+
39
+ ## Qual ferramenta usar
40
+
41
+ | O usuário pediu | Ferramenta | slug |
42
+ |---|---|---|
43
+ | slides, deck, apresentação, PowerPoint, Keynote, Canva apresentação, pitch | **Prisma** | `prisma` |
44
+ | post, capa, banner, thumbnail, arte, Photoshop, Figma, Canva, story, feed | **Tela** | `tela` |
45
+ | documento, relatório, artigo, proposta, contrato, Word, Docs, one-pager | **Prosa** | `prosa` |
46
+ | planilha, tabela, orçamento, Excel, Sheets, controle, projeção | **Grade** | `grade` |
47
+ | vídeo, reels, animação, CapCut, Premiere, teaser, motion | **Corte** | `corte` |
48
+ | fluxograma, diagrama, mapa mental, arquitetura, organograma, Miro | **Traço** | `traco` |
49
+
50
+ Em dúvida entre duas, escolha a que o usuário vai **mostrar para outra pessoa**.
51
+
52
+ ## Qualidade — o que separa um arquivo bom de um genérico
53
+
54
+ - **Uma ideia por cena.** Se precisa rolar o olho para ler, dividiu errado.
55
+ - **Conteúdo antes de enfeite.** Só use `forma`, `icone` e imagem quando somam.
56
+ - **Hierarquia visível.** Toda cena tem um elemento dominante — normalmente o título.
57
+ - **Ritmo.** Em decks longos, um `layout: "secao"` a cada 3–5 cenas.
58
+ - **Escolha o tema pelo assunto:** `meia-noite` produto e tecnologia · `claro`
59
+ corporativo e projetor · `papel` editorial e longo · `neon` técnico e dev ·
60
+ `brasa` energia e urgência · `floresta` sustentabilidade e saúde.
61
+ - **Notas do apresentador** (`notas` na cena) em quem vai falar por cima.
62
+ - **Nunca invente dado com cara de real.** Sem fonte, escreva a ordem de grandeza
63
+ ou deixe o campo como pergunta explícita ao usuário.
64
+
65
+ ## API completa
66
+
67
+ | Método | Rota | O que faz |
68
+ |---|---|---|
69
+ | `GET` | `/api/manual` | este manual inteiro, em um fetch |
70
+ | `GET` | `/api/ferramentas` | as seis ferramentas em JSON |
71
+ | `GET` | `/api/ferramentas/<slug>` | a spec detalhada de uma |
72
+ | `GET` | `/api/arquivos` | lista o que já existe |
73
+ | `POST` | `/api/arquivos` | cria — `{ferramenta, titulo, doc}` |
74
+ | `GET` | `/api/arquivos/<id>` | lê |
75
+ | `PUT` | `/api/arquivos/<id>` | atualiza — `{titulo?, doc?}` |
76
+ | `DELETE` | `/api/arquivos/<id>` | apaga |
77
+ | `GET` | `/api/arquivos/<id>/export` | baixa: `.html` autônomo, `.csv` (Grade), `.md` (Prosa) |
78
+ | `GET` | `/api/midia` | lista imagens, áudios e vídeos já enviados |
79
+ | `POST` | `/api/midia` | envia — `{nome, dados}` com `dados` em data URI base64 |
80
+ | `DELETE` | `/api/midia/<nome>` | apaga um arquivo de mídia |
81
+
82
+ ## Música e imagem
83
+
84
+ O usuário pode ter arquivos no computador. Suba com `POST /api/midia` e use a `url`
85
+ devolvida em `src`, `fundo.imagem` ou `trilha.src`. Antes de pedir arquivo novo,
86
+ consulte `GET /api/midia` — pode já estar lá.
87
+
88
+ **Nunca baixe áudio ou vídeo de YouTube, Spotify, Deezer ou similares.** Isso quebra
89
+ os termos dessas plataformas e o direito autoral da faixa, e não é algo que eu faça
90
+ mesmo se pedirem. As três saídas legítimas, nesta ordem:
91
+
92
+ 1. **Trilha gerada** — `{"tipo":"gerada","estilo":"pulso"}`. O estúdio sintetiza a
93
+ música na hora. Sem arquivo, sem licença de ninguém. Estilos em `corte.md`.
94
+ 2. **Arquivo do usuário** — o que ele já tem no computador, via `POST /api/midia`.
95
+ 3. **URL direta** de um áudio que ele tenha direito de usar.
96
+
97
+ Se pedirem para baixar do YouTube ou do Spotify, diga em uma frase que não faço isso
98
+ e ofereça as três opções acima — sem sermão, e já entregando o vídeo com trilha gerada.
99
+
100
+ A resposta do POST traz:
101
+
102
+ ```json
103
+ { "id": "...", "editar": "http://localhost:7777/t/prisma/...",
104
+ "ver": "http://localhost:7777/v/..." }
105
+ ```
106
+
107
+ ## Iterar
108
+
109
+ Para ajustar algo que o usuário pediu depois, faça `GET` do arquivo, altere só o que
110
+ mudou e `PUT` de volta. Não recrie do zero — o link já está com o usuário e deve
111
+ continuar valendo.