primocode 8.42.2 → 9.1.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 +41 -37
- package/bin/primocode.js +598 -360
- package/lib/acervo.js +8 -1
- package/lib/act.js +205 -114
- package/lib/api.js +35 -4
- package/lib/boas-vindas.js +42 -195
- package/lib/catalogo.js +100 -61
- package/lib/config.js +1 -1
- package/lib/conta.js +3 -24
- package/lib/diff.js +161 -0
- package/lib/diretor.js +37 -47
- package/lib/entrada.js +576 -0
- package/lib/especialistas.js +2 -2
- package/lib/estudio/assets.js +343 -0
- package/lib/estudio/identidade.js +560 -0
- package/lib/estudio/index.js +565 -0
- package/lib/estudio/plano.js +327 -0
- package/lib/estudio/projeto.js +246 -0
- package/lib/estudio/remotion.js +644 -0
- package/lib/estudio/slides.js +190 -0
- package/lib/estudio/video.js +296 -0
- package/lib/fala.js +49 -6
- package/lib/marca.js +4 -1
- package/lib/markdown.js +376 -0
- package/lib/marketplace.js +189 -0
- package/lib/pasta.js +15 -4
- package/lib/permissao.js +137 -0
- package/lib/registro.js +229 -0
- package/lib/skills.js +329 -0
- package/lib/tools.js +106 -187
- package/lib/ui.js +7 -3
- package/lib/voz.js +239 -0
- package/package.json +12 -3
- package/skills/acessibilidade/SKILL.md +28 -0
- package/skills/api-que-nao-quebra/SKILL.md +27 -0
- package/skills/depurar-de-verdade/SKILL.md +29 -0
- package/skills/editar-meu-video/SKILL.md +59 -0
- package/skills/escrever-em-portugues/SKILL.md +28 -0
- package/skills/git-limpo/SKILL.md +28 -0
- package/skills/motion-de-primeira/SKILL.md +62 -0
- package/skills/pagina-que-converte/SKILL.md +28 -0
- package/skills/performance-web/SKILL.md +28 -0
- package/skills/revisao-antes-de-entregar/SKILL.md +27 -0
- package/skills/slides-que-prendem/SKILL.md +40 -0
- package/studio/edge.py +16 -66
- package/studio/narrar.py +274 -0
- package/studio/pessoa.py +199 -0
- package/voz/primo_voz/__init__.py +0 -0
- package/voz/primo_voz/__main__.py +383 -0
- package/voz/primo_voz/escuta.py +235 -0
- package/voz/primo_voz/orbe.py +216 -0
- package/lib/audio-livre.js +0 -177
- package/lib/chaves.js +0 -149
- package/lib/claude-engine.js +0 -631
- package/lib/codex-engine.js +0 -208
- package/lib/remotion.js +0 -663
- package/lib/studio.js +0 -960
- package/studio/PRIMOCODE.md +0 -58
- package/studio/README.md +0 -154
- package/studio/__pycache__/edge.cpython-311.pyc +0 -0
- package/studio/__pycache__/elevenlabs.cpython-311.pyc +0 -0
- package/studio/__pycache__/voz.cpython-311.pyc +0 -0
- package/studio/arquivos/corte-teste-de-camera-fb823.json +0 -57
- package/studio/ferramentas/00-protocolo.md +0 -125
- package/studio/ferramentas/01-modelos.md +0 -270
- package/studio/ferramentas/README.md +0 -32
- package/studio/ferramentas/corte.md +0 -919
- package/studio/ferramentas/grade.md +0 -92
- package/studio/ferramentas/prisma.md +0 -107
- package/studio/ferramentas/prosa.md +0 -86
- package/studio/ferramentas/tela.md +0 -93
- package/studio/ferramentas/traco.md +0 -79
- package/studio/server.py +0 -2254
- package/studio/studio.log +0 -57
- package/studio/voz.py +0 -388
- package/studio/web/analise.html +0 -52
- package/studio/web/core.css +0 -1167
- package/studio/web/fontes/bebas.woff2 +0 -0
- package/studio/web/fontes/grotesk-700.woff2 +0 -0
- package/studio/web/fontes/inter-400.woff2 +0 -0
- package/studio/web/fontes/inter-700.woff2 +0 -0
- package/studio/web/fontes/jetbrains-400.woff2 +0 -0
- package/studio/web/fontes/playfair-700.woff2 +0 -0
- package/studio/web/fontes.css +0 -23
- package/studio/web/home.html +0 -217
- package/studio/web/js/analise.js +0 -510
- package/studio/web/js/audio.js +0 -607
- package/studio/web/js/editor.js +0 -1802
- package/studio/web/js/exportar.js +0 -222
- package/studio/web/js/ferramentas.js +0 -108
- package/studio/web/js/icones.js +0 -120
- package/studio/web/js/midia.js +0 -157
- package/studio/web/js/narracao.js +0 -325
- package/studio/web/js/player.js +0 -903
- package/studio/web/js/render-grade.js +0 -243
- package/studio/web/js/render-prosa.js +0 -104
- package/studio/web/js/render.js +0 -1852
- package/studio/web/js/sfx.js +0 -233
- package/studio/web/js/video.js +0 -464
- package/studio/web/js/voz-neural.js +0 -152
- package/studio/web/render.css +0 -1352
- package/studio/web/tf.html +0 -15
- package/studio/web/tool.html +0 -78
- package/studio/web/view.html +0 -27
package/lib/skills.js
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills.js — o que o PrimoCode sabe fazer bem, escrito em texto.
|
|
3
|
+
*
|
|
4
|
+
* "O Primo Code já vem com várias skills instaladas por padrão. Comandos:
|
|
5
|
+
* /skills, /install-skill e /marketplace."
|
|
6
|
+
*
|
|
7
|
+
* ── O QUE É UMA SKILL, E O QUE ELA NÃO É ─────────────────────────────────
|
|
8
|
+
* Uma skill é uma pasta com um `SKILL.md` dentro: um cabeçalho que diz quem
|
|
9
|
+
* ela é e quando serve, e um corpo que é o prompt dela. Nada de código, nada
|
|
10
|
+
* de instalar dependência, nada que rode. É conhecimento, não programa.
|
|
11
|
+
*
|
|
12
|
+
* Isso é decisão, não limitação. Uma skill que executa código é uma porta
|
|
13
|
+
* para qualquer pessoa mandar um arquivo e rodar o que quiser na máquina de
|
|
14
|
+
* quem instalou — e um marketplace de programas executáveis exige assinatura,
|
|
15
|
+
* revisão e sandbox, que é outro produto. Skill é texto: o pior que um texto
|
|
16
|
+
* mal-intencionado faz é dar um conselho ruim ao modelo, e o usuário vê o
|
|
17
|
+
* agente agindo, passo a passo, na tela.
|
|
18
|
+
*
|
|
19
|
+
* ── POR QUE NÃO É A MESMA COISA QUE OS ESPECIALISTAS ─────────────────────
|
|
20
|
+
* `especialistas.js` são sete briefings FIXOS, escolhidos pelo modelo quando
|
|
21
|
+
* ele delega uma tarefa a um subagente. Eles são do produto: ninguém instala,
|
|
22
|
+
* ninguém desliga, e a lista não cresce.
|
|
23
|
+
*
|
|
24
|
+
* Skill é do USUÁRIO. Ele instala, desliga, escreve a dele. É a resposta para
|
|
25
|
+
* "o Primo sempre esquece de rodar o lint antes de terminar" — que não se
|
|
26
|
+
* resolve pedindo de novo a cada conversa, e sim escrevendo uma vez.
|
|
27
|
+
*
|
|
28
|
+
* ── COMO ELA CHEGA AO MODELO ─────────────────────────────────────────────
|
|
29
|
+
* O corpo NÃO vai em todo pedido. São oito skills; mandar todas custaria
|
|
30
|
+
* milhares de tokens por mensagem num plano limitado por token, para que
|
|
31
|
+
* sete delas não tenham nada a ver com o pedido.
|
|
32
|
+
*
|
|
33
|
+
* O que decide é o `quando:` do cabeçalho — as palavras que fazem aquela
|
|
34
|
+
* skill valer a pena. Bateu, o corpo dela entra na frente da tarefa; não
|
|
35
|
+
* bateu, ela não custa nada. É o mesmo desenho dos especialistas, e pelo
|
|
36
|
+
* mesmo motivo: o teto de tokens é o recurso escasso deste produto.
|
|
37
|
+
*
|
|
38
|
+
* ── ONDE ELAS MORAM ──────────────────────────────────────────────────────
|
|
39
|
+
* Em `~/.primocode/skills/`, fora do pacote — pelo mesmo motivo do Studio:
|
|
40
|
+
* instalado global, a pasta do pacote costuma ser de sistema, e um `npm
|
|
41
|
+
* update` apagaria o que o usuário escreveu. As que vêm com o produto são
|
|
42
|
+
* copiadas na primeira vez; as dele ficam lá, a salvo de atualização.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
'use strict';
|
|
46
|
+
|
|
47
|
+
const fs = require('fs');
|
|
48
|
+
const os = require('os');
|
|
49
|
+
const path = require('path');
|
|
50
|
+
|
|
51
|
+
const ORIGEM = path.join(__dirname, '..', 'skills');
|
|
52
|
+
const CASA = path.join(os.homedir(), '.primocode', 'skills');
|
|
53
|
+
const ESTADO = path.join(CASA, 'estado.json');
|
|
54
|
+
|
|
55
|
+
/* Teto do que entra num pedido. Duas skills longas já são mais texto que a
|
|
56
|
+
maioria das tarefas — e cada caractere aqui é um caractere a menos de
|
|
57
|
+
arquivo lido. Estourou, entram as que couberam, das que casaram melhor. */
|
|
58
|
+
const MAX_CARACTERES = 6000;
|
|
59
|
+
|
|
60
|
+
// ── leitura ──────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* O cabeçalho e o corpo de um SKILL.md.
|
|
64
|
+
*
|
|
65
|
+
* O cabeçalho fica entre duas linhas de `---`, uma chave por linha. Formato
|
|
66
|
+
* escolhido por ser o que qualquer pessoa já viu, e por caber num parser de
|
|
67
|
+
* dez linhas — YAML de verdade seria uma dependência para ler quatro campos.
|
|
68
|
+
*/
|
|
69
|
+
function separar(texto) {
|
|
70
|
+
const bruto = String(texto || '');
|
|
71
|
+
const m = bruto.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
72
|
+
if (!m) return { cabecalho: {}, corpo: bruto.trim() };
|
|
73
|
+
const cabecalho = {};
|
|
74
|
+
for (const linha of m[1].split(/\r?\n/)) {
|
|
75
|
+
const par = linha.match(/^\s*([a-zA-Z_][\w-]*)\s*:\s*(.*)$/);
|
|
76
|
+
if (par) cabecalho[par[1].toLowerCase()] = par[2].trim().replace(/^["']|["']$/g, '');
|
|
77
|
+
}
|
|
78
|
+
return { cabecalho, corpo: m[2].trim() };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function lerEstado() {
|
|
82
|
+
try { return JSON.parse(fs.readFileSync(ESTADO, 'utf8')); } catch { return { desligadas: [] }; }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function salvarEstado(estado) {
|
|
86
|
+
try {
|
|
87
|
+
fs.mkdirSync(CASA, { recursive: true });
|
|
88
|
+
fs.writeFileSync(ESTADO, JSON.stringify(estado, null, 2), 'utf8');
|
|
89
|
+
return true;
|
|
90
|
+
} catch { return false; }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* As que vêm com o produto são copiadas na primeira vez. Copiar SEMPRE por
|
|
94
|
+
cima seria desfazer a edição de quem melhorou uma delas; copiar só o que
|
|
95
|
+
falta deixa o usuário dono do que ele mexeu. */
|
|
96
|
+
function prepararCasa() {
|
|
97
|
+
fs.mkdirSync(CASA, { recursive: true });
|
|
98
|
+
let nascidas = 0;
|
|
99
|
+
let origens = [];
|
|
100
|
+
try { origens = fs.readdirSync(ORIGEM, { withFileTypes: true }); } catch { return 0; }
|
|
101
|
+
for (const item of origens) {
|
|
102
|
+
if (!item.isDirectory()) continue;
|
|
103
|
+
const destino = path.join(CASA, item.name);
|
|
104
|
+
if (fs.existsSync(destino)) continue;
|
|
105
|
+
try {
|
|
106
|
+
fs.mkdirSync(destino, { recursive: true });
|
|
107
|
+
fs.copyFileSync(path.join(ORIGEM, item.name, 'SKILL.md'), path.join(destino, 'SKILL.md'));
|
|
108
|
+
nascidas++;
|
|
109
|
+
} catch { /* uma que falha não impede as outras */ }
|
|
110
|
+
}
|
|
111
|
+
return nascidas;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Uma skill pelo nome da pasta, ou null. */
|
|
115
|
+
function ler(nome) {
|
|
116
|
+
const arquivo = path.join(CASA, nome, 'SKILL.md');
|
|
117
|
+
let texto;
|
|
118
|
+
try { texto = fs.readFileSync(arquivo, 'utf8'); } catch { return null; }
|
|
119
|
+
const { cabecalho, corpo } = separar(texto);
|
|
120
|
+
const estado = lerEstado();
|
|
121
|
+
return {
|
|
122
|
+
nome,
|
|
123
|
+
titulo: cabecalho.nome || cabecalho.titulo || nome,
|
|
124
|
+
descricao: cabecalho.descricao || '',
|
|
125
|
+
quando: String(cabecalho.quando || '').split(',').map((s) => s.trim().toLowerCase()).filter(Boolean),
|
|
126
|
+
autor: cabecalho.autor || '',
|
|
127
|
+
corpo,
|
|
128
|
+
ligada: !estado.desligadas.includes(nome),
|
|
129
|
+
doProduto: fs.existsSync(path.join(ORIGEM, nome, 'SKILL.md')),
|
|
130
|
+
caminho: arquivo,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Todas as instaladas, em ordem alfabética. */
|
|
135
|
+
function listar() {
|
|
136
|
+
prepararCasa();
|
|
137
|
+
let nomes = [];
|
|
138
|
+
try {
|
|
139
|
+
nomes = fs.readdirSync(CASA, { withFileTypes: true })
|
|
140
|
+
.filter((d) => d.isDirectory())
|
|
141
|
+
.map((d) => d.name)
|
|
142
|
+
.sort();
|
|
143
|
+
} catch { return []; }
|
|
144
|
+
return nomes.map(ler).filter(Boolean);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── escrita ──────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
/* O nome vira pasta, então precisa ser um nome de pasta: sem barra, sem
|
|
150
|
+
`..`, sem acento. Um nome com `../` escreveria fora de ~/.primocode. */
|
|
151
|
+
function apelidar(bruto) {
|
|
152
|
+
return String(bruto || '')
|
|
153
|
+
.normalize('NFD').replace(/[̀-ͯ]/g, '')
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
156
|
+
.replace(/^-+|-+$/g, '')
|
|
157
|
+
.slice(0, 48);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Cria (ou reescreve) uma skill.
|
|
162
|
+
*
|
|
163
|
+
* @param {string} nome como a pessoa a chamou
|
|
164
|
+
* @param {string} corpo o prompt inteiro
|
|
165
|
+
* @param {object} extra { descricao, quando, autor }
|
|
166
|
+
*/
|
|
167
|
+
function criar(nome, corpo, extra = {}) {
|
|
168
|
+
const apelido = apelidar(nome);
|
|
169
|
+
if (!apelido) return { ok: false, error: 'Dê um nome à skill.' };
|
|
170
|
+
if (!String(corpo || '').trim()) return { ok: false, error: 'A skill precisa de um conteúdo — é ele que vai para o modelo.' };
|
|
171
|
+
|
|
172
|
+
const cabecalho = [
|
|
173
|
+
'---',
|
|
174
|
+
`nome: ${String(nome).trim()}`,
|
|
175
|
+
`descricao: ${String(extra.descricao || '').replace(/\n/g, ' ').trim()}`,
|
|
176
|
+
`quando: ${String(extra.quando || '').replace(/\n/g, ' ').trim()}`,
|
|
177
|
+
`autor: ${String(extra.autor || 'você').trim()}`,
|
|
178
|
+
'---',
|
|
179
|
+
'',
|
|
180
|
+
].join('\n');
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const pasta = path.join(CASA, apelido);
|
|
184
|
+
fs.mkdirSync(pasta, { recursive: true });
|
|
185
|
+
fs.writeFileSync(path.join(pasta, 'SKILL.md'), cabecalho + String(corpo).trim() + '\n', 'utf8');
|
|
186
|
+
// Skill recém-criada nasce LIGADA, mesmo que uma de mesmo nome tenha
|
|
187
|
+
// sido desligada antes: quem acabou de escrevê-la quer usá-la.
|
|
188
|
+
const estado = lerEstado();
|
|
189
|
+
estado.desligadas = estado.desligadas.filter((x) => x !== apelido);
|
|
190
|
+
salvarEstado(estado);
|
|
191
|
+
return { ok: true, nome: apelido, caminho: path.join(pasta, 'SKILL.md') };
|
|
192
|
+
} catch (e) {
|
|
193
|
+
return { ok: false, error: `Não deu para gravar: ${e.message}` };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function ligar(nome, ligada) {
|
|
198
|
+
const estado = lerEstado();
|
|
199
|
+
estado.desligadas = estado.desligadas.filter((x) => x !== nome);
|
|
200
|
+
if (!ligada) estado.desligadas.push(nome);
|
|
201
|
+
salvarEstado(estado);
|
|
202
|
+
return { ok: true, nome, ligada: !!ligada };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Apaga uma skill.
|
|
207
|
+
*
|
|
208
|
+
* As do produto voltam no próximo `listar()` — a cópia nasce de novo a partir
|
|
209
|
+
* do pacote. Por isso apagar uma delas não é apagar, é DESLIGAR: prometer
|
|
210
|
+
* remoção e ver a skill reaparecer amanhã é pior que dizer a verdade agora.
|
|
211
|
+
*/
|
|
212
|
+
function apagar(nome) {
|
|
213
|
+
const s = ler(nome);
|
|
214
|
+
if (!s) return { ok: false, error: `Não achei a skill "${nome}".` };
|
|
215
|
+
if (s.doProduto) {
|
|
216
|
+
ligar(nome, false);
|
|
217
|
+
return { ok: true, desligada: true,
|
|
218
|
+
aviso: 'Esta skill vem com o PrimoCode, então ela foi DESLIGADA em vez de apagada — '
|
|
219
|
+
+ 'apagada, ela voltaria na próxima atualização.' };
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
fs.rmSync(path.join(CASA, nome), { recursive: true, force: true });
|
|
223
|
+
return { ok: true, apagada: true };
|
|
224
|
+
} catch (e) { return { ok: false, error: e.message }; }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Instala de um arquivo ou de uma pasta que já está no disco.
|
|
229
|
+
*
|
|
230
|
+
* Não busca na rede de propósito: baixar e executar texto que virou prompt do
|
|
231
|
+
* agente é uma decisão que merece o caminho explícito de quem salvou o
|
|
232
|
+
* arquivo antes. O `/marketplace` traz as do catálogo; `/install-skill
|
|
233
|
+
* <caminho>` traz as que a pessoa escreveu ou recebeu.
|
|
234
|
+
*/
|
|
235
|
+
function instalarDe(caminho) {
|
|
236
|
+
const alvo = String(caminho || '').replace(/^~(?=$|[/\\])/, os.homedir());
|
|
237
|
+
let arquivo = alvo;
|
|
238
|
+
try {
|
|
239
|
+
if (fs.statSync(alvo).isDirectory()) arquivo = path.join(alvo, 'SKILL.md');
|
|
240
|
+
} catch { return { ok: false, error: `Não achei ${caminho}.` }; }
|
|
241
|
+
|
|
242
|
+
let texto;
|
|
243
|
+
try { texto = fs.readFileSync(arquivo, 'utf8'); }
|
|
244
|
+
catch { return { ok: false, error: `Não consegui ler ${arquivo}. Uma skill é um arquivo SKILL.md.` }; }
|
|
245
|
+
|
|
246
|
+
const { cabecalho, corpo } = separar(texto);
|
|
247
|
+
const nome = cabecalho.nome || path.basename(path.dirname(arquivo));
|
|
248
|
+
if (!corpo.trim()) return { ok: false, error: 'O arquivo não tem conteúdo depois do cabeçalho.' };
|
|
249
|
+
return criar(nome, corpo, cabecalho);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── o que entra no pedido ────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
const semAcento = (v) => String(v || '').toLowerCase()
|
|
255
|
+
.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Quantas palavras do `quando:` aparecem no texto do pedido.
|
|
259
|
+
*
|
|
260
|
+
* ── PALAVRA INTEIRA, NUNCA PEDACO ────────────────────────────────────────
|
|
261
|
+
* O primeiro jeito era `texto.includes(palavra)`, e estava errado de um jeito
|
|
262
|
+
* que so apareceu no ensaio: "faz uma landing page PRO meu curso" puxou a
|
|
263
|
+
* skill de Git, porque o gatilho `pr` mora dentro de "pro". Gatilhos curtos
|
|
264
|
+
* — pr, api, seo, css — casam com metade da lingua portuguesa se a borda nao
|
|
265
|
+
* for exigida.
|
|
266
|
+
*
|
|
267
|
+
* Uma skill errada nao e ruido inofensivo: ela ocupa o teto de tokens do
|
|
268
|
+
* pedido e da ao modelo instrucoes sobre um assunto que ninguem pediu.
|
|
269
|
+
*/
|
|
270
|
+
function pontuar(skill, texto) {
|
|
271
|
+
const t = semAcento(texto);
|
|
272
|
+
let pontos = 0;
|
|
273
|
+
for (const palavra of skill.quando) {
|
|
274
|
+
const p = semAcento(palavra).trim();
|
|
275
|
+
if (!p) continue;
|
|
276
|
+
const escapado = p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
277
|
+
// A borda e escrita a mao em vez de `\b`: o gatilho pode comecar ou
|
|
278
|
+
// terminar em simbolo ("e-mail"), e ali o `\b` nao vale.
|
|
279
|
+
const re = new RegExp('(^|[^a-z0-9])' + escapado + '($|[^a-z0-9])');
|
|
280
|
+
if (re.test(t)) pontos++;
|
|
281
|
+
}
|
|
282
|
+
return pontos;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* As skills que valem a pena para ESTE pedido, das mais ao menos relevantes.
|
|
287
|
+
*
|
|
288
|
+
* Só as ligadas, só as que casaram com alguma palavra, e só até o teto de
|
|
289
|
+
* caracteres. Skill que não casou não entra: mandar as oito em todo pedido
|
|
290
|
+
* custaria milhares de tokens para sete delas não terem nada a ver com o que
|
|
291
|
+
* foi pedido.
|
|
292
|
+
*/
|
|
293
|
+
function paraOPedido(texto, { teto = MAX_CARACTERES, todas = null } = {}) {
|
|
294
|
+
const lista = (todas || listar()).filter((s) => s.ligada && s.corpo);
|
|
295
|
+
const casadas = lista
|
|
296
|
+
.map((s) => ({ skill: s, pontos: pontuar(s, texto) }))
|
|
297
|
+
.filter((x) => x.pontos > 0)
|
|
298
|
+
.sort((a, b) => b.pontos - a.pontos);
|
|
299
|
+
|
|
300
|
+
const escolhidas = [];
|
|
301
|
+
let tamanho = 0;
|
|
302
|
+
for (const { skill } of casadas) {
|
|
303
|
+
if (tamanho + skill.corpo.length > teto) continue;
|
|
304
|
+
escolhidas.push(skill);
|
|
305
|
+
tamanho += skill.corpo.length;
|
|
306
|
+
}
|
|
307
|
+
return escolhidas;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* O texto que vai na frente da tarefa.
|
|
312
|
+
*
|
|
313
|
+
* Vem antes do pedido, e não depois, pelo mesmo motivo do briefing dos
|
|
314
|
+
* especialistas: é contexto para ler o pedido, não um adendo a ele.
|
|
315
|
+
*/
|
|
316
|
+
function montar(texto, op = {}) {
|
|
317
|
+
const escolhidas = paraOPedido(texto, op);
|
|
318
|
+
if (!escolhidas.length) return { texto, skills: [] };
|
|
319
|
+
const blocos = escolhidas.map((s) => `### ${s.titulo}\n${s.corpo}`);
|
|
320
|
+
const cabeca = 'O QUE VOCÊ SABE SOBRE ESTE TIPO DE TAREFA (siga ao trabalhar):\n\n'
|
|
321
|
+
+ blocos.join('\n\n') + '\n\n---\n\nPEDIDO:\n';
|
|
322
|
+
return { texto: cabeca + texto, skills: escolhidas };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = {
|
|
326
|
+
CASA, ORIGEM, MAX_CARACTERES,
|
|
327
|
+
separar, listar, ler, criar, ligar, apagar, instalarDe, apelidar,
|
|
328
|
+
paraOPedido, montar, pontuar, prepararCasa,
|
|
329
|
+
};
|