@semacode/cli 1.5.17 → 1.5.19
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/AGENTS.md +268 -260
- package/LICENSE +22 -22
- package/README.md +93 -61
- package/SEMA_BRIEF.curto.txt +8 -6
- package/SEMA_BRIEF.md +71 -22
- package/SEMA_BRIEF.micro.txt +7 -5
- package/SEMA_INDEX.json +931 -193
- package/dist/controleComercialSupabase.d.ts +326 -0
- package/dist/controleComercialSupabase.js +310 -0
- package/dist/controleComercialSupabase.js.map +1 -0
- package/dist/docs.js +48 -20
- package/dist/docs.js.map +1 -1
- package/dist/drift.d.ts +5 -3
- package/dist/drift.js +119 -116
- package/dist/drift.js.map +1 -1
- package/dist/importador.d.ts +1 -1
- package/dist/importador.js +1 -200
- package/dist/importador.js.map +1 -1
- package/dist/index.js +1924 -493
- package/dist/index.js.map +1 -1
- package/dist/mcpRemoto.d.ts +32 -0
- package/dist/mcpRemoto.js +61 -0
- package/dist/mcpRemoto.js.map +1 -0
- package/dist/projeto.js +3 -21
- package/dist/projeto.js.map +1 -1
- package/dist/tipos.d.ts +1 -1
- package/docs/AGENT_STARTER.md +109 -102
- package/docs/cli.md +89 -33
- package/docs/como-ensinar-a-sema-para-ia.md +42 -36
- package/docs/deploy.md +231 -43
- package/docs/documentacao.md +61 -36
- package/docs/env.md +63 -14
- package/docs/extensao-vscode.md +12 -4
- package/docs/fluxo-pratico-ia-sema.md +45 -35
- package/docs/instalacao-e-primeiro-uso.md +52 -30
- package/docs/integracao-com-ia.md +44 -35
- package/docs/mcp.md +270 -31
- package/docs/pagamento-ponta-a-ponta.md +40 -24
- package/docs/persistencia-vendor-first.md +11 -5
- package/docs/prompt-base-ia-sema.md +18 -11
- package/docs/rollback.md +17 -15
- package/docs/sintaxe.md +37 -229
- package/exemplos/agendamento.sema +105 -106
- package/exemplos/assinatura.sema +133 -136
- package/exemplos/auditoria.sema +89 -88
- package/exemplos/autenticacao.sema +125 -125
- package/exemplos/author_obra_comum.sema +294 -0
- package/exemplos/author_tema_sensivel.sema +264 -0
- package/exemplos/automacao.sema +107 -107
- package/exemplos/cadastro_usuario.sema +54 -54
- package/exemplos/calculadora.sema +78 -78
- package/exemplos/crud_simples.sema +89 -89
- package/exemplos/estoque.sema +127 -126
- package/exemplos/exportacao.sema +94 -94
- package/exemplos/fila.sema +130 -131
- package/exemplos/integracao_externa.sema +94 -94
- package/exemplos/multi_tenant.sema +140 -140
- package/exemplos/notificacao.sema +149 -98
- package/exemplos/operacao_estrategia.sema +633 -402
- package/exemplos/pagamento.sema +434 -222
- package/exemplos/pagamento_dominio.sema +35 -35
- package/exemplos/pedido.sema +255 -119
- package/exemplos/permissao.sema +121 -121
- package/exemplos/persistencia_vendor_first.sema +86 -86
- package/exemplos/profile_game.sema +114 -0
- package/exemplos/profile_legal.sema +105 -0
- package/exemplos/profile_ops.sema +110 -0
- package/exemplos/profile_research.sema +104 -0
- package/exemplos/profile_software.sema +123 -0
- package/exemplos/profile_workflow_n8n.sema +99 -0
- package/exemplos/relatorio.sema +93 -93
- package/exemplos/replica_analitica_erp.sema +160 -0
- package/exemplos/testes_embutidos.sema +45 -45
- package/exemplos/tratamento_erro.sema +157 -157
- package/exemplos/upload_arquivo.sema +93 -93
- package/exemplos/webhook.sema +94 -96
- package/llms-full.txt +34 -34
- package/llms.txt +17 -17
- package/node_modules/@sema/gerador-css/dist/index.js +563 -563
- package/node_modules/@sema/gerador-css/package.json +1 -1
- package/node_modules/@sema/gerador-dart/package.json +1 -1
- package/node_modules/@sema/gerador-html/dist/index.js +90 -90
- package/node_modules/@sema/gerador-html/package.json +1 -1
- package/node_modules/@sema/gerador-javascript/dist/index.js +92 -92
- package/node_modules/@sema/gerador-javascript/package.json +1 -1
- package/node_modules/@sema/gerador-lua/dist/index.js +53 -53
- package/node_modules/@sema/gerador-lua/package.json +1 -1
- package/node_modules/@sema/gerador-python/dist/index.js +122 -96
- package/node_modules/@sema/gerador-python/dist/index.js.map +1 -1
- package/node_modules/@sema/gerador-python/package.json +1 -1
- package/node_modules/@sema/gerador-typescript/dist/index.js +153 -153
- package/node_modules/@sema/gerador-typescript/package.json +1 -1
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +2 -4
- package/node_modules/@sema/nucleo/dist/formatador/index.js +26 -46
- package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +9 -204
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +3 -35
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js +0 -21
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/parser/parser.js +0 -40
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +6 -5
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +76 -307
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +2 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +32 -2
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +1 -1
- package/node_modules/@sema/nucleo/package.json +1 -1
- package/node_modules/@sema/padroes/package.json +1 -1
- package/package.json +15 -15
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
2
|
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import ts from "typescript";
|
|
8
7
|
import pacoteCli from "../package.json" with { type: "json" };
|
|
9
8
|
import { compilarCodigo, formatarCodigo, formatarDiagnosticos, lerArquivoTexto, temErros, } from "@sema/nucleo";
|
|
10
9
|
import { descreverEstruturaModulo } from "@sema/padroes";
|
|
@@ -19,15 +18,16 @@ import { carregarConfiguracaoProjeto, carregarProjeto, resolverAlvoPadrao, resol
|
|
|
19
18
|
import { importarProjetoLegado, resumoImportacao } from "./importador.js";
|
|
20
19
|
import { analisarDriftLegado, assistirRenomeacaoSemantica, gerarMapaImpactoSemantico, } from "./drift.js";
|
|
21
20
|
import { resolverDocumentacaoObrigatoria, verificarDocumentacaoMudanca, } from "./docs.js";
|
|
22
|
-
const STARTER_IA = `Voce esta trabalhando com Sema, um
|
|
21
|
+
const STARTER_IA = `Voce esta trabalhando com Sema, um contrato semantico IA-first para agentes operarem software vivo em backend e front consumer.
|
|
23
22
|
|
|
24
23
|
Importante:
|
|
25
24
|
- a Sema se apresenta publicamente como protocolo e funciona tecnicamente como linguagem de intencao
|
|
26
25
|
- a Sema e protocolo de governanca semantica desenhado para IA, nao para ergonomia humana
|
|
26
|
+
- humanos sao autores/aprovadores; agentes sao os consumidores primarios do contrato
|
|
27
27
|
- leitura humana e bonus toleravel, nao objetivo de produto
|
|
28
28
|
- a Sema nao e gerador magico que deveria fazer tudo
|
|
29
29
|
- a Sema modela contratos, estados, fluxos, erros, efeitos, garantias, vinculos e execucao
|
|
30
|
-
- a Sema gera codigo e scaffolding real para TypeScript, Python
|
|
30
|
+
- a Sema gera codigo e scaffolding real para TypeScript, Python e Dart
|
|
31
31
|
- a Sema usa \`importar\` para bootstrap revisavel, nao para contrato final automatico
|
|
32
32
|
- a Sema usa \`impl\` para ligar task a simbolo real do runtime
|
|
33
33
|
- a Sema usa \`vinculos\` para ligar contrato a arquivo, simbolo, recurso e superficie real
|
|
@@ -41,10 +41,11 @@ Importante:
|
|
|
41
41
|
- se a tarefa envolver UI, prefira pedir Sema + React + TypeScript ou Sema + arquitetura de front-end
|
|
42
42
|
- evite pedir HTML unico solto quando a intencao for testar a Sema de verdade
|
|
43
43
|
|
|
44
|
-
Regras:
|
|
45
|
-
- nao invente sintaxe fora da gramatica e dos exemplos oficiais
|
|
46
|
-
-
|
|
47
|
-
-
|
|
44
|
+
Regras:
|
|
45
|
+
- nao invente sintaxe fora da gramatica e dos exemplos oficiais
|
|
46
|
+
- antes de qualquer acao, crie, edite ou remova o contrato .sema aplicavel; isso vale para Software, Author, Workflow, Ops, Game, Legal e Research
|
|
47
|
+
- se a IA for pequena, nao tente abrir tudo de uma vez
|
|
48
|
+
- use \`sema resumo\` e \`briefing.min.json\` antes de subir para o pacote completo
|
|
48
49
|
- trate \`ir --json\` como fonte de verdade semantica
|
|
49
50
|
- trate \`briefing.json\` como plano de intervencao antes de editar projeto vivo
|
|
50
51
|
- trate \`diagnosticos --json\` como fonte de correcao
|
|
@@ -55,19 +56,19 @@ Regras:
|
|
|
55
56
|
Comandos essenciais:
|
|
56
57
|
- resumo compacto por capacidade: \`sema resumo <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>]\`
|
|
57
58
|
- prompt curto para IA pequena: \`sema prompt-curto <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>]\`
|
|
58
|
-
- descoberta do projeto: \`sema inspecionar [arquivo-ou-pasta] --json\`
|
|
59
|
-
- auditoria do contrato vivo: \`sema drift <arquivo-ou-pasta> [--escopo <arquivo|modulo|projeto>] [--json]\`
|
|
60
|
-
- mapa de impacto: \`sema impacto <arquivo-ou-pasta> --alvo <token> [--mudanca <descricao>] [--json]\`
|
|
61
|
-
- renomeacao assistida: \`sema renomear-semantico <arquivo-ou-pasta> --de <nome-atual> --para <nome-novo> [--json]\`
|
|
59
|
+
- descoberta do projeto: \`sema inspecionar [arquivo-ou-pasta] --json\`
|
|
60
|
+
- auditoria do contrato vivo: \`sema drift <arquivo-ou-pasta> [--escopo <arquivo|modulo|projeto>] [--json]\`
|
|
61
|
+
- mapa de impacto: \`sema impacto <arquivo-ou-pasta> --alvo <token> [--mudanca <descricao>] [--json]\`
|
|
62
|
+
- renomeacao assistida: \`sema renomear-semantico <arquivo-ou-pasta> --de <nome-atual> --para <nome-novo> [--json]\`
|
|
62
63
|
- contexto completo do modulo: \`sema contexto-ia <arquivo.sema>\`
|
|
63
64
|
- estrutura sintatica: \`sema ast <arquivo.sema> --json\`
|
|
64
65
|
- estrutura semantica: \`sema ir <arquivo.sema> --json\`
|
|
65
66
|
- validacao: \`sema validar <arquivo.sema> --json\`
|
|
66
67
|
- diagnosticos: \`sema diagnosticos <arquivo.sema> --json\`
|
|
67
68
|
- formatacao: \`sema formatar <arquivo.sema>\`
|
|
68
|
-
- importacao assistida de legado: \`sema importar <nestjs|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|firebase|
|
|
69
|
-
- exemplos oficiais no projeto: \`sema instalar-exemplos\`
|
|
70
|
-
- geracao de codigo: \`sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart|lua> --saida <diretorio>\`
|
|
69
|
+
- importacao assistida de legado: \`sema importar <nestjs|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|firebase|dotnet|java|go|rust|cpp|typescript|python|dart> <diretorio> --saida <diretorio>\`
|
|
70
|
+
- exemplos oficiais no projeto: \`sema instalar-exemplos\`
|
|
71
|
+
- geracao de codigo: \`sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart|lua> --saida <diretorio>\`
|
|
71
72
|
- verificacao final: \`sema verificar <arquivo-ou-pasta> [--json]\`
|
|
72
73
|
|
|
73
74
|
Antes de editar:
|
|
@@ -106,9 +107,9 @@ Superficies que a IA deve enxergar como first-class:
|
|
|
106
107
|
|
|
107
108
|
Nao improvise quando faltar contexto.
|
|
108
109
|
`;
|
|
109
|
-
const PROMPT_BASE_IA = `Voce esta trabalhando com Sema, um
|
|
110
|
+
const PROMPT_BASE_IA = `Voce esta trabalhando com Sema, um contrato semantico IA-first para agentes operarem software vivo.
|
|
110
111
|
|
|
111
|
-
Trate a Sema como
|
|
112
|
+
Trate a Sema como protocolo de governanca semantica e linguagem de intencao feita para IA, nao para leitura humana confortavel. Humanos escrevem e aprovam; agentes consomem. Nao invente sintaxe, palavras-chave ou blocos fora da gramatica e dos exemplos oficiais.
|
|
112
113
|
|
|
113
114
|
Fontes de verdade, em ordem:
|
|
114
115
|
1. se o projeto expuser \`SEMA_CONTEXT.md\`, comece por ele
|
|
@@ -121,11 +122,13 @@ Fontes de verdade, em ordem:
|
|
|
121
122
|
8. \`sema resumo\` e \`briefing.min.json\` quando a IA for pequena
|
|
122
123
|
9. AST, IR e diagnosticos exportados pela CLI em JSON quando a capacidade aguentar
|
|
123
124
|
|
|
124
|
-
Regras de operacao:
|
|
125
|
-
-
|
|
126
|
-
-
|
|
125
|
+
Regras de operacao:
|
|
126
|
+
- contrato vem antes da acao: crie, edite ou remova o .sema aplicavel antes de codigo, docs operacionais, texto Author, workflow, jogo, pesquisa, legal ou ops
|
|
127
|
+
- preserve o significado semantico
|
|
128
|
+
- use o formatador oficial da Sema como fonte unica de estilo
|
|
127
129
|
- use diagnosticos estruturados como contrato de correcao
|
|
128
130
|
- use a IR como fonte de verdade semantica quando houver duvida
|
|
131
|
+
- use predicados canonicos como normalizacao opcional, preservando a forma original
|
|
129
132
|
- nao conclua uma alteracao sem validar e verificar o modulo
|
|
130
133
|
- comece pelo menor artefato semantico que resolva a tarefa
|
|
131
134
|
|
|
@@ -240,9 +243,10 @@ Se houver duvida, siga os exemplos oficiais e mantenha a separacao:
|
|
|
240
243
|
`;
|
|
241
244
|
const PROMPT_IA_SEMA_PRIMEIRO = `Quero que voce trabalhe no modo "Sema primeiro".
|
|
242
245
|
|
|
243
|
-
Regra principal:
|
|
244
|
-
- modele primeiro o dominio em arquivos \`.sema\`
|
|
245
|
-
-
|
|
246
|
+
Regra principal:
|
|
247
|
+
- modele primeiro o dominio em arquivos \`.sema\`
|
|
248
|
+
- se a intencao mudar, crie, edite ou remova o contrato antes de qualquer acao operacional
|
|
249
|
+
- so depois proponha ou gere codigo de aplicacao derivado disso
|
|
246
250
|
|
|
247
251
|
Fluxo obrigatorio:
|
|
248
252
|
1. entender o dominio pedido
|
|
@@ -325,6 +329,7 @@ Comandos uteis da CLI para esse fluxo:
|
|
|
325
329
|
`;
|
|
326
330
|
const DIRETORIO_CLI_ATUAL = path.dirname(fileURLToPath(import.meta.url));
|
|
327
331
|
const VERSAO_CLI = pacoteCli.version;
|
|
332
|
+
const requireRuntimeCli = createRequire(import.meta.url);
|
|
328
333
|
const ARQUIVOS_CANONICOS_IA_RAIZ = [
|
|
329
334
|
"llms.txt",
|
|
330
335
|
"SEMA_BRIEF.md",
|
|
@@ -342,12 +347,25 @@ const DOCUMENTOS_SUPORTE_IA = [
|
|
|
342
347
|
"docs/sintaxe.md",
|
|
343
348
|
"docs/cli.md",
|
|
344
349
|
"docs/mcp.md",
|
|
350
|
+
"docs/profiles.md",
|
|
351
|
+
"docs/profiles-author-agents-flow.md",
|
|
352
|
+
"docs/testes.md",
|
|
345
353
|
"docs/documentacao.md",
|
|
346
354
|
"docs/deploy.md",
|
|
347
355
|
"docs/env.md",
|
|
348
356
|
"docs/rollback.md",
|
|
349
357
|
"docs/extensao-vscode.md",
|
|
350
358
|
];
|
|
359
|
+
function resolverImportadorNodeOpcional(especificador) {
|
|
360
|
+
try {
|
|
361
|
+
return requireRuntimeCli.resolve(especificador);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const TSX_IMPORTADOR_CLI = resolverImportadorNodeOpcional("tsx");
|
|
368
|
+
const TSX_EXECUTOR_CLI = resolverImportadorNodeOpcional("tsx/cli");
|
|
351
369
|
async function caminhoEhDiretorio(caminhoAlvo) {
|
|
352
370
|
try {
|
|
353
371
|
return (await stat(caminhoAlvo)).isDirectory();
|
|
@@ -449,7 +467,20 @@ function ajuda() {
|
|
|
449
467
|
"sema docs-impacto --intencao \"fazer deploy\" --criar-ausentes --json",
|
|
450
468
|
"sema contexto-ia <arquivo.sema> --saida ./.tmp/contexto --json",
|
|
451
469
|
"",
|
|
452
|
-
"[3]
|
|
470
|
+
"[3] Governar escrita autoral com Sema Author",
|
|
471
|
+
"sema author iniciar [--tema-sensivel] [--saida contratos/author.sema]",
|
|
472
|
+
"sema author validar contratos/author.sema --json",
|
|
473
|
+
"sema author briefing contratos/author.sema --json",
|
|
474
|
+
"sema author revisar-cliches contratos/author.sema --texto <texto> --json # reprova se houver bloqueio",
|
|
475
|
+
"sema author validar-narrativa contratos/author.sema --texto <texto> --texto-anterior <texto> --json",
|
|
476
|
+
"sema author validar-proibicoes contratos/author.sema --texto <texto> --json",
|
|
477
|
+
"",
|
|
478
|
+
"[4] Validar profiles semanticos",
|
|
479
|
+
"sema profile validar workflow contratos/sema/workflow_ops.sema --maturidade production --preset webhook --artefato-arquivo workflow.md --json",
|
|
480
|
+
"sema profile validar ops contratos/sema/workflow_ops.sema --maturidade critical --preset deploy --artefato-arquivo runbook.md --json",
|
|
481
|
+
"sema profile validar game contratos/sema/game.sema --maturidade prototype --preset playtest --artefato-arquivo playtest.md --json",
|
|
482
|
+
"",
|
|
483
|
+
"[5] Adotar Sema em projeto que ainda nao usa",
|
|
453
484
|
"sema importar <fonte> <diretorio> --saida <diretorio> --json",
|
|
454
485
|
"sema formatar <arquivo-ou-pasta>",
|
|
455
486
|
"sema validar <arquivo-ou-pasta> --json",
|
|
@@ -467,13 +498,15 @@ function ajuda() {
|
|
|
467
498
|
"auditoria: sema drift <arquivo-ou-pasta> [--escopo <arquivo|modulo|projeto>] [--incluir-worktrees] [--incluir-consumidores-laterais] [--json]",
|
|
468
499
|
"impacto: sema impacto <arquivo-ou-pasta> --alvo <token> [--mudanca <descricao>] [--escopo <arquivo|modulo|projeto>] [--json]",
|
|
469
500
|
"renomeacao: sema renomear-semantico <arquivo-ou-pasta> --de <nome-atual> --para <nome-novo> [--escopo <arquivo|modulo|projeto>] [--json]",
|
|
470
|
-
"importacao: sema importar <nestjs|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|firebase|
|
|
501
|
+
"importacao: sema importar <nestjs|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|firebase|dotnet|java|go|rust|cpp|typescript|python|dart> <diretorio> [--saida <diretorio>] [--namespace <base>] [--json]",
|
|
471
502
|
"validacao: sema validar <arquivo-ou-pasta> [--json]",
|
|
472
503
|
"diagnostico: sema diagnosticos <arquivo.sema> [--json]",
|
|
473
504
|
"geracao: sema compilar <arquivo-ou-pasta> --alvo <python|typescript|dart|lua> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
|
|
474
505
|
"teste local: sema testar <arquivo.sema> --alvo <python|typescript|dart|lua> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
|
|
475
506
|
"verificacao final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--json]",
|
|
476
507
|
"formatacao: sema formatar <arquivo-ou-pasta> [--check] [--json]",
|
|
508
|
+
"author: sema author <iniciar|validar|briefing|revisar-cliches|validar-narrativa|validar-proibicoes> [arquivo] [--tema-sensivel] [--preset conto|romance|roteiro|lore|campanha] [--saida <arquivo>] [--texto <texto>] [--json]",
|
|
509
|
+
"profile: sema profile validar <software|workflow|ops|game|legal|research> <arquivo-ou-pasta> [--maturidade draft|prototype|production|critical] [--preset <preset>] [--artefato <texto>|--artefato-arquivo <arquivo>] [--json]",
|
|
477
510
|
]),
|
|
478
511
|
"",
|
|
479
512
|
renderizarSecaoAscii("Ajuda IA-first", [
|
|
@@ -486,13 +519,17 @@ function ajuda() {
|
|
|
486
519
|
"sema prompt-ia-react",
|
|
487
520
|
"sema prompt-ia-sema-primeiro",
|
|
488
521
|
"sema exemplos-prompt-ia",
|
|
489
|
-
"sema author [--help]",
|
|
490
|
-
"sema author exemplo [--sensivel] [--json]",
|
|
491
522
|
"sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--json]",
|
|
492
523
|
"sema sync-ai-entrypoints [--json]",
|
|
493
524
|
"sema instalar-exemplos [--json]",
|
|
525
|
+
"sema mcp-instalar-chave --stdin [--endpoint <url>] [--dry-run] [--json]",
|
|
494
526
|
"sema docs-impacto --intencao <acao> [--arquivo <caminho>] [--criar-ausentes] [--json]",
|
|
495
527
|
"sema finalizar-mudanca --intencao <acao> [--arquivo <caminho>] [--doc-lida <caminho>] [--json]",
|
|
528
|
+
"sema author briefing <arquivo.sema> [--json]",
|
|
529
|
+
"sema author revisar-cliches <arquivo.sema> [--texto <texto>|--texto-arquivo <arquivo>] [--texto-anterior <texto>|--texto-anterior-arquivo <arquivo>] [--json]",
|
|
530
|
+
"sema author validar-narrativa <arquivo.sema> [--texto <texto>|--texto-arquivo <arquivo>] [--json]",
|
|
531
|
+
"sema author validar-proibicoes <arquivo.sema> [--texto <texto>|--texto-arquivo <arquivo>] [--json]",
|
|
532
|
+
"sema profile validar <profile> <arquivo.sema> [--maturidade draft|prototype|production|critical] [--preset <preset>] [--artefato <texto>|--artefato-arquivo <arquivo>] [--json]",
|
|
496
533
|
]),
|
|
497
534
|
"",
|
|
498
535
|
renderizarSecaoAscii("Operacional", [
|
|
@@ -548,6 +585,15 @@ const OPCOES_COM_VALOR = new Set([
|
|
|
548
585
|
"--intencao",
|
|
549
586
|
"--arquivo",
|
|
550
587
|
"--doc-lida",
|
|
588
|
+
"--texto",
|
|
589
|
+
"--texto-arquivo",
|
|
590
|
+
"--endpoint",
|
|
591
|
+
"--maturidade",
|
|
592
|
+
"--preset",
|
|
593
|
+
"--artefato",
|
|
594
|
+
"--artifact",
|
|
595
|
+
"--artefato-arquivo",
|
|
596
|
+
"--artifact-file",
|
|
551
597
|
]);
|
|
552
598
|
const ALIAS_OPCOES = {
|
|
553
599
|
"-a": "--alvo",
|
|
@@ -601,6 +647,138 @@ function comandoDisponivel(comando, argumentos = ["--version"]) {
|
|
|
601
647
|
const execucao = spawnSync(comando, argumentos, { stdio: "ignore", shell: process.platform === "win32" });
|
|
602
648
|
return (execucao.status ?? 1) === 0;
|
|
603
649
|
}
|
|
650
|
+
const MCP_CLIENT_TOKEN_ENV_VAR = "SEMA_MCP_AUTH_TOKEN";
|
|
651
|
+
const MCP_CLIENT_ENDPOINT_ENV_VAR = "SEMA_MCP_ENDPOINT";
|
|
652
|
+
const MCP_ENDPOINT_PADRAO = "https://sema.otimitare.online/mcp";
|
|
653
|
+
function tokenPareceChaveMcpComercial(token) {
|
|
654
|
+
return /^sema_mcp_[A-Za-z0-9_-]{16,}$/.test(token);
|
|
655
|
+
}
|
|
656
|
+
function prefixoSeguroTokenMcp(token) {
|
|
657
|
+
return token.slice(0, Math.min(14, token.length));
|
|
658
|
+
}
|
|
659
|
+
function ajudaMcpInstalarChave() {
|
|
660
|
+
return [
|
|
661
|
+
"Uso: sema mcp-instalar-chave --stdin [--endpoint <url>] [--dry-run] [--json]",
|
|
662
|
+
"",
|
|
663
|
+
"Instala a chave MCP comercial no ambiente do usuario local.",
|
|
664
|
+
"",
|
|
665
|
+
"Seguranca:",
|
|
666
|
+
"- a chave deve vir por stdin, nunca por argumento",
|
|
667
|
+
`- grava ${MCP_CLIENT_TOKEN_ENV_VAR} e ${MCP_CLIENT_ENDPOINT_ENV_VAR} no usuario`,
|
|
668
|
+
"- nao imprime o token completo",
|
|
669
|
+
"",
|
|
670
|
+
"Exemplo:",
|
|
671
|
+
" Get-Clipboard | sema mcp-instalar-chave --stdin --json",
|
|
672
|
+
].join("\n");
|
|
673
|
+
}
|
|
674
|
+
async function lerStdinTexto() {
|
|
675
|
+
return await new Promise((resolve, reject) => {
|
|
676
|
+
let conteudo = "";
|
|
677
|
+
process.stdin.setEncoding("utf8");
|
|
678
|
+
process.stdin.on("data", (chunk) => {
|
|
679
|
+
conteudo += chunk;
|
|
680
|
+
});
|
|
681
|
+
process.stdin.on("error", reject);
|
|
682
|
+
process.stdin.on("end", () => resolve(conteudo));
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
function instalarChaveMcpNoUsuarioWindows(token, endpoint, dryRun) {
|
|
686
|
+
if (dryRun) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (process.platform !== "win32") {
|
|
690
|
+
throw new Error("Instalacao persistente automatica da chave MCP esta disponivel apenas no Windows por enquanto.");
|
|
691
|
+
}
|
|
692
|
+
const script = [
|
|
693
|
+
"$ErrorActionPreference = 'Stop'",
|
|
694
|
+
"$token = $env:SEMA_MCP_INSTALL_TOKEN_VALUE",
|
|
695
|
+
"$endpoint = $env:SEMA_MCP_INSTALL_ENDPOINT_VALUE",
|
|
696
|
+
"if ([string]::IsNullOrWhiteSpace($token)) { throw 'Chave MCP vazia.' }",
|
|
697
|
+
`[Environment]::SetEnvironmentVariable('${MCP_CLIENT_TOKEN_ENV_VAR}', $token, 'User')`,
|
|
698
|
+
`[Environment]::SetEnvironmentVariable('${MCP_CLIENT_ENDPOINT_ENV_VAR}', $endpoint, 'User')`,
|
|
699
|
+
].join("; ");
|
|
700
|
+
const execucao = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script], {
|
|
701
|
+
encoding: "utf8",
|
|
702
|
+
windowsHide: true,
|
|
703
|
+
env: {
|
|
704
|
+
...process.env,
|
|
705
|
+
SEMA_MCP_INSTALL_TOKEN_VALUE: token,
|
|
706
|
+
SEMA_MCP_INSTALL_ENDPOINT_VALUE: endpoint,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
if ((execucao.status ?? 1) !== 0) {
|
|
710
|
+
throw new Error((execucao.stderr || execucao.stdout || "PowerShell falhou ao gravar a chave MCP do usuario.").trim());
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async function comandoMcpInstalarChave(args, json) {
|
|
714
|
+
if (possuiFlag(args, "--help") || possuiFlag(args, "-h")) {
|
|
715
|
+
console.log(ajudaMcpInstalarChave());
|
|
716
|
+
return 0;
|
|
717
|
+
}
|
|
718
|
+
const dryRun = possuiFlag(args, "--dry-run");
|
|
719
|
+
const endpoint = obterOpcao(args, "--endpoint", MCP_ENDPOINT_PADRAO) ?? MCP_ENDPOINT_PADRAO;
|
|
720
|
+
const recebePorStdin = possuiFlag(args, "--stdin");
|
|
721
|
+
const tokenOrigem = recebePorStdin ? await lerStdinTexto() : "";
|
|
722
|
+
const token = tokenOrigem.trim();
|
|
723
|
+
const resultadoBase = {
|
|
724
|
+
comando: "mcp-instalar-chave",
|
|
725
|
+
dryRun,
|
|
726
|
+
sistema: process.platform,
|
|
727
|
+
envVar: MCP_CLIENT_TOKEN_ENV_VAR,
|
|
728
|
+
endpointEnvVar: MCP_CLIENT_ENDPOINT_ENV_VAR,
|
|
729
|
+
endpoint,
|
|
730
|
+
tokenPrefix: prefixoSeguroTokenMcp(token),
|
|
731
|
+
tokenLength: token.length,
|
|
732
|
+
reinicioNecessario: true,
|
|
733
|
+
tokenExposto: false,
|
|
734
|
+
};
|
|
735
|
+
try {
|
|
736
|
+
if (!recebePorStdin) {
|
|
737
|
+
throw new Error("Use --stdin para instalar a chave MCP. A chave nao deve ir por argumento nem fallback de ambiente.");
|
|
738
|
+
}
|
|
739
|
+
if (!token) {
|
|
740
|
+
throw new Error("Informe a chave MCP por stdin: sema mcp-instalar-chave --stdin.");
|
|
741
|
+
}
|
|
742
|
+
if (!tokenPareceChaveMcpComercial(token)) {
|
|
743
|
+
throw new Error("A chave MCP precisa comecar com sema_mcp_ e ter formato comercial valido.");
|
|
744
|
+
}
|
|
745
|
+
if (!/^https:\/\/.+\/mcp(?:[?#].*)?$/.test(endpoint)) {
|
|
746
|
+
throw new Error("O endpoint MCP precisa ser HTTPS e terminar em /mcp.");
|
|
747
|
+
}
|
|
748
|
+
instalarChaveMcpNoUsuarioWindows(token, endpoint, dryRun);
|
|
749
|
+
const resultado = {
|
|
750
|
+
...resultadoBase,
|
|
751
|
+
sucesso: true,
|
|
752
|
+
persistente: !dryRun && process.platform === "win32",
|
|
753
|
+
};
|
|
754
|
+
if (json) {
|
|
755
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
console.log("Chave MCP instalada para o usuario atual.");
|
|
759
|
+
console.log(`Env var: ${MCP_CLIENT_TOKEN_ENV_VAR}`);
|
|
760
|
+
console.log(`Endpoint: ${endpoint}`);
|
|
761
|
+
console.log("Reabra Codex, VS Code ou o fork MCP para carregar a variavel.");
|
|
762
|
+
}
|
|
763
|
+
return 0;
|
|
764
|
+
}
|
|
765
|
+
catch (erro) {
|
|
766
|
+
const mensagem = erro instanceof Error ? erro.message : String(erro);
|
|
767
|
+
const resultado = {
|
|
768
|
+
...resultadoBase,
|
|
769
|
+
sucesso: false,
|
|
770
|
+
persistente: false,
|
|
771
|
+
erro: mensagem,
|
|
772
|
+
};
|
|
773
|
+
if (json) {
|
|
774
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
console.error(mensagem);
|
|
778
|
+
}
|
|
779
|
+
return 1;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
604
782
|
function resolverExecucaoPython() {
|
|
605
783
|
if (comandoDisponivel("python")) {
|
|
606
784
|
return { comando: "python", argumentosBase: [], rotulo: "python" };
|
|
@@ -648,7 +826,11 @@ function coletarDependenciasDoctor() {
|
|
|
648
826
|
comando: "verificar/typescript",
|
|
649
827
|
itens: [
|
|
650
828
|
criarItemDependencia("node", comandoDisponivel("node"), "execucao do runner TypeScript"),
|
|
651
|
-
criarItemDependencia("
|
|
829
|
+
criarItemDependencia("tsx", Boolean(TSX_EXECUTOR_CLI ?? TSX_IMPORTADOR_CLI), TSX_EXECUTOR_CLI
|
|
830
|
+
? `runner resolvido em ${TSX_EXECUTOR_CLI}`
|
|
831
|
+
: TSX_IMPORTADOR_CLI
|
|
832
|
+
? `importador resolvido em ${TSX_IMPORTADOR_CLI}`
|
|
833
|
+
: "tsx nao foi encontrado junto da CLI"),
|
|
652
834
|
],
|
|
653
835
|
},
|
|
654
836
|
{
|
|
@@ -698,7 +880,11 @@ function avaliarPreflightVerificacao(configuracoesAlvo) {
|
|
|
698
880
|
if (configuracao.alvo === "typescript") {
|
|
699
881
|
registrar("verificar/typescript", [
|
|
700
882
|
criarItemDependencia("node", comandoDisponivel("node"), "execucao do runner TypeScript"),
|
|
701
|
-
criarItemDependencia("
|
|
883
|
+
criarItemDependencia("tsx", Boolean(TSX_EXECUTOR_CLI ?? TSX_IMPORTADOR_CLI), TSX_EXECUTOR_CLI
|
|
884
|
+
? `runner resolvido em ${TSX_EXECUTOR_CLI}`
|
|
885
|
+
: TSX_IMPORTADOR_CLI
|
|
886
|
+
? `importador resolvido em ${TSX_IMPORTADOR_CLI}`
|
|
887
|
+
: "tsx nao foi encontrado junto da CLI"),
|
|
702
888
|
]);
|
|
703
889
|
continue;
|
|
704
890
|
}
|
|
@@ -825,12 +1011,6 @@ function normalizarFonteImportacao(valor) {
|
|
|
825
1011
|
if (valor === "cpp" || valor === "cxx" || valor === "cc" || valor === "c++") {
|
|
826
1012
|
return "cpp";
|
|
827
1013
|
}
|
|
828
|
-
if (valor === "lua") {
|
|
829
|
-
return "lua";
|
|
830
|
-
}
|
|
831
|
-
if (valor === "php" || valor === "laravel" || valor === "symfony") {
|
|
832
|
-
return "php";
|
|
833
|
-
}
|
|
834
1014
|
if (valor === "nestjs"
|
|
835
1015
|
|| valor === "fastapi"
|
|
836
1016
|
|| valor === "flask"
|
|
@@ -845,11 +1025,9 @@ function normalizarFonteImportacao(valor) {
|
|
|
845
1025
|
|| valor === "go"
|
|
846
1026
|
|| valor === "rust"
|
|
847
1027
|
|| valor === "cpp"
|
|
848
|
-
|| valor === "php"
|
|
849
1028
|
|| valor === "typescript"
|
|
850
1029
|
|| valor === "python"
|
|
851
|
-
|| valor === "dart"
|
|
852
|
-
|| valor === "lua") {
|
|
1030
|
+
|| valor === "dart") {
|
|
853
1031
|
return valor;
|
|
854
1032
|
}
|
|
855
1033
|
return undefined;
|
|
@@ -985,35 +1163,6 @@ function contarCasosDeTesteGerados(alvo, arquivos) {
|
|
|
985
1163
|
}
|
|
986
1164
|
return (arquivoTeste.conteudo.match(/\bdef test_/g) ?? []).length;
|
|
987
1165
|
}
|
|
988
|
-
function reescreverImportsTypeScriptGerados(codigo) {
|
|
989
|
-
return codigo
|
|
990
|
-
.replace(/(from\s+["']\.{1,2}\/[^"']+)\.ts(["'])/g, "$1.cjs$2")
|
|
991
|
-
.replace(/(require\(\s*["']\.{1,2}\/[^"']+)\.ts(["']\s*\))/g, "$1.cjs$2");
|
|
992
|
-
}
|
|
993
|
-
function compilarTypeScriptGeradoParaNodeTest(baseSaida, arquivos, arquivoTeste) {
|
|
994
|
-
const pastaRuntime = path.join(baseSaida, ".sema-js-tests");
|
|
995
|
-
rmSync(pastaRuntime, { recursive: true, force: true });
|
|
996
|
-
for (const arquivo of arquivos) {
|
|
997
|
-
if (!arquivo.caminhoRelativo.endsWith(".ts")) {
|
|
998
|
-
continue;
|
|
999
|
-
}
|
|
1000
|
-
const caminhoRelativoJs = arquivo.caminhoRelativo.replace(/\.ts$/i, ".cjs");
|
|
1001
|
-
const destino = path.join(pastaRuntime, caminhoRelativoJs);
|
|
1002
|
-
mkdirSync(path.dirname(destino), { recursive: true });
|
|
1003
|
-
const transpilado = ts.transpileModule(arquivo.conteudo, {
|
|
1004
|
-
fileName: arquivo.caminhoRelativo,
|
|
1005
|
-
compilerOptions: {
|
|
1006
|
-
module: ts.ModuleKind.CommonJS,
|
|
1007
|
-
target: ts.ScriptTarget.ES2022,
|
|
1008
|
-
esModuleInterop: true,
|
|
1009
|
-
sourceMap: false,
|
|
1010
|
-
inlineSourceMap: false,
|
|
1011
|
-
},
|
|
1012
|
-
}).outputText;
|
|
1013
|
-
writeFileSync(destino, reescreverImportsTypeScriptGerados(transpilado), "utf8");
|
|
1014
|
-
}
|
|
1015
|
-
return path.join(pastaRuntime, arquivoTeste.replace(/\.ts$/i, ".cjs"));
|
|
1016
|
-
}
|
|
1017
1166
|
function executarTestesGerados(alvo, baseSaida, arquivos, silencioso = false) {
|
|
1018
1167
|
const quantidadeTestes = contarCasosDeTesteGerados(alvo, arquivos);
|
|
1019
1168
|
if (quantidadeTestes === 0) {
|
|
@@ -1034,8 +1183,15 @@ function executarTestesGerados(alvo, baseSaida, arquivos, silencioso = false) {
|
|
|
1034
1183
|
}
|
|
1035
1184
|
return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
|
|
1036
1185
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1186
|
+
if (!TSX_EXECUTOR_CLI) {
|
|
1187
|
+
return {
|
|
1188
|
+
codigoSaida: 1,
|
|
1189
|
+
quantidadeTestes,
|
|
1190
|
+
saidaPadrao: "",
|
|
1191
|
+
saidaErro: "Nao foi possivel localizar o runner tsx junto da CLI para executar testes TypeScript.",
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
const execucao = spawnSync("node", [TSX_EXECUTOR_CLI, arquivoTeste], {
|
|
1039
1195
|
stdio: silencioso ? "pipe" : "inherit",
|
|
1040
1196
|
cwd: baseSaida,
|
|
1041
1197
|
encoding: silencioso ? "utf8" : undefined,
|
|
@@ -1317,6 +1473,8 @@ function criarGuiaCapacidadeIa() {
|
|
|
1317
1473
|
}
|
|
1318
1474
|
function coletarResumoSemanticoModulo(contexto) {
|
|
1319
1475
|
const { arquivo, modulo, geradoEm, ir, briefing, drift } = contexto;
|
|
1476
|
+
const modoVerificacaoCodigo = contexto.modoVerificacaoCodigo ?? "codigo_completo";
|
|
1477
|
+
const fontesConclusao = contexto.fontesConclusao ?? ["contrato", "codigo"];
|
|
1320
1478
|
const tarefas = ir?.tasks ?? [];
|
|
1321
1479
|
const rotas = ir?.routes ?? [];
|
|
1322
1480
|
const superficies = ir?.superficies ?? [];
|
|
@@ -1340,10 +1498,17 @@ function coletarResumoSemanticoModulo(contexto) {
|
|
|
1340
1498
|
...rotas.flatMap((route) => route.resumoAgente.entidadesAfetadas),
|
|
1341
1499
|
...superficies.flatMap((superficie) => superficie.resumoAgente.entidadesAfetadas),
|
|
1342
1500
|
]);
|
|
1343
|
-
|
|
1501
|
+
const resumo = {
|
|
1344
1502
|
geradoEm,
|
|
1345
1503
|
arquivo,
|
|
1346
1504
|
modulo,
|
|
1505
|
+
modoVerificacaoCodigo,
|
|
1506
|
+
avisoVerificacaoCodigo: modoVerificacaoCodigo === "contratos_apenas"
|
|
1507
|
+
? "Somente contratos foram enviados; implementacao nao foi verificada neste modo."
|
|
1508
|
+
: modoVerificacaoCodigo === "codigo_selecionado"
|
|
1509
|
+
? "Codigo selecionado foi enviado; conclusoes de implementacao valem apenas para os arquivos do snapshot."
|
|
1510
|
+
: null,
|
|
1511
|
+
fontesConclusao,
|
|
1347
1512
|
perfilCompatibilidade: ir?.perfilCompatibilidade ?? briefing.perfilCompatibilidade,
|
|
1348
1513
|
scoreSemantico: briefing.scoreSemantico,
|
|
1349
1514
|
confiancaGeral: briefing.confiancaGeral,
|
|
@@ -1357,7 +1522,6 @@ function coletarResumoSemanticoModulo(contexto) {
|
|
|
1357
1522
|
...rotas.map((route) => `${route.metodo ?? "?"} ${route.caminho ?? route.nome}`),
|
|
1358
1523
|
]), 8),
|
|
1359
1524
|
regrasCriticas: limitarLista(regrasCriticas, 8),
|
|
1360
|
-
regrasAuthorAutomaticas: extrairRegrasAuthorAutomaticas(ir),
|
|
1361
1525
|
efeitos: limitarLista(efeitos, 8),
|
|
1362
1526
|
erros: limitarLista(erros, 8),
|
|
1363
1527
|
entidadesAfetadas: limitarLista(entidadesAfetadas, 8),
|
|
@@ -1375,19 +1539,87 @@ function coletarResumoSemanticoModulo(contexto) {
|
|
|
1375
1539
|
ancoragensVinculo: limitarLista(unicosOrdenados(briefing.ancoragensVinculo ?? []), 8),
|
|
1376
1540
|
arquivosProvaveisEditar: limitarLista(unicosOrdenados(briefing.arquivosProvaveisEditar ?? briefing.oQueTocar), 8),
|
|
1377
1541
|
};
|
|
1542
|
+
return modoVerificacaoCodigo === "codigo_completo"
|
|
1543
|
+
? resumo
|
|
1544
|
+
: normalizarResumoModoContratosApenas(resumo);
|
|
1378
1545
|
}
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1546
|
+
const LACUNAS_DEPENDENTES_DE_CODIGO = new Set([
|
|
1547
|
+
"sem_impl",
|
|
1548
|
+
"impl_quebrado",
|
|
1549
|
+
"sem_vinculos",
|
|
1550
|
+
"vinculo_quebrado",
|
|
1551
|
+
"rastreabilidade_fraca",
|
|
1552
|
+
]);
|
|
1553
|
+
function normalizarResumoModoContratosApenas(resumo) {
|
|
1554
|
+
const lacunasCodigo = resumo.lacunas.some((lacuna) => LACUNAS_DEPENDENTES_DE_CODIGO.has(lacuna));
|
|
1555
|
+
const riscosCodigo = resumo.riscosPrincipais.some((risco) => /vinculo_fraco|sem_impl|impl_quebrado|vinculo_quebrado|rastreabilidade_fraca/i.test(risco));
|
|
1556
|
+
const modoContratosApenas = resumo.modoVerificacaoCodigo === "contratos_apenas";
|
|
1557
|
+
const lacunas = resumo.lacunas
|
|
1558
|
+
.filter((lacuna) => !LACUNAS_DEPENDENTES_DE_CODIGO.has(lacuna));
|
|
1559
|
+
const riscosPrincipais = resumo.riscosPrincipais
|
|
1560
|
+
.filter((risco) => !/vinculo_fraco|sem_impl|impl_quebrado|vinculo_quebrado|rastreabilidade_fraca/i.test(risco));
|
|
1561
|
+
const checksSugeridos = resumo.checksSugeridos
|
|
1562
|
+
.filter((check) => !/drift|vinculo/i.test(check));
|
|
1563
|
+
return {
|
|
1564
|
+
...resumo,
|
|
1565
|
+
riscosPrincipais: limitarLista(unicosOrdenados([
|
|
1566
|
+
...riscosPrincipais,
|
|
1567
|
+
...(riscosCodigo || lacunasCodigo ? ["codigo_nao_verificado_neste_modo"] : []),
|
|
1568
|
+
]), 6),
|
|
1569
|
+
lacunas: limitarLista(unicosOrdenados([
|
|
1570
|
+
...lacunas,
|
|
1571
|
+
...(lacunasCodigo ? [modoContratosApenas ? "implementacao_nao_enviada" : "implementacao_nao_presente_no_snapshot"] : []),
|
|
1572
|
+
...(lacunasCodigo ? ["codigo_nao_verificado_neste_modo"] : []),
|
|
1573
|
+
]), 6),
|
|
1574
|
+
inferido: limitarLista(unicosOrdenados([
|
|
1575
|
+
...resumo.inferido,
|
|
1576
|
+
modoContratosApenas ? "modo_contratos_apenas" : "modo_codigo_selecionado",
|
|
1577
|
+
modoContratosApenas ? "codigo_nao_verificado_neste_modo" : "codigo_parcial_verificado_neste_modo",
|
|
1578
|
+
]), 6),
|
|
1579
|
+
checksSugeridos: limitarLista(unicosOrdenados([
|
|
1580
|
+
...checksSugeridos,
|
|
1581
|
+
modoContratosApenas
|
|
1582
|
+
? "sincronizar codigo selecionado para verificar implementacao"
|
|
1583
|
+
: "ampliar snapshot se precisar verificar outro codigo",
|
|
1584
|
+
]), 6),
|
|
1585
|
+
};
|
|
1384
1586
|
}
|
|
1385
|
-
function
|
|
1386
|
-
|
|
1587
|
+
async function detectarModoVerificacaoCodigo(baseProjeto, diretoriosCodigo) {
|
|
1588
|
+
try {
|
|
1589
|
+
const snapshot = JSON.parse(await readFile(path.join(baseProjeto, "SEMA_SNAPSHOT.json"), "utf8"));
|
|
1590
|
+
if (snapshot?.modo === "contratos_apenas")
|
|
1591
|
+
return "contratos_apenas";
|
|
1592
|
+
if (snapshot?.modo === "codigo_selecionado")
|
|
1593
|
+
return "codigo_selecionado";
|
|
1594
|
+
}
|
|
1595
|
+
catch { }
|
|
1596
|
+
return diretoriosCodigo.length === 0 ? "contratos_apenas" : "codigo_completo";
|
|
1387
1597
|
}
|
|
1388
|
-
function
|
|
1389
|
-
|
|
1390
|
-
|
|
1598
|
+
async function detectarFontesConclusaoSnapshot(baseProjeto) {
|
|
1599
|
+
try {
|
|
1600
|
+
const snapshot = JSON.parse(await readFile(path.join(baseProjeto, "SEMA_SNAPSHOT.json"), "utf8"));
|
|
1601
|
+
if (Array.isArray(snapshot?.fontesConclusao) && snapshot.fontesConclusao.length > 0) {
|
|
1602
|
+
return snapshot.fontesConclusao.map(String);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
catch { }
|
|
1606
|
+
return ["contrato", "codigo"];
|
|
1607
|
+
}
|
|
1608
|
+
function descreverFontesConclusao(fontes, modo) {
|
|
1609
|
+
return fontes.map((fonte) => {
|
|
1610
|
+
if (fonte === "contrato")
|
|
1611
|
+
return "contrato: intencao oficial e regra normativa";
|
|
1612
|
+
if (fonte === "codigo") {
|
|
1613
|
+
return modo === "codigo_selecionado"
|
|
1614
|
+
? "codigo: evidencia restrita ao snapshot selecionado"
|
|
1615
|
+
: "codigo: evidencia da implementacao disponivel";
|
|
1616
|
+
}
|
|
1617
|
+
if (fonte === "documentacao")
|
|
1618
|
+
return "documentacao: regra de negocio, operacao ou runbook contextual";
|
|
1619
|
+
if (fonte === "indice")
|
|
1620
|
+
return "indice: mapa de arquivos, hashes, timestamps e escopo recebido";
|
|
1621
|
+
return `${fonte}: fonte complementar`;
|
|
1622
|
+
});
|
|
1391
1623
|
}
|
|
1392
1624
|
function renderizarResumoModuloTexto(resumo, tamanho, modo) {
|
|
1393
1625
|
const limite = tamanho === "micro" ? 2 : tamanho === "curto" ? 4 : 6;
|
|
@@ -1396,7 +1628,9 @@ function renderizarResumoModuloTexto(resumo, tamanho, modo) {
|
|
|
1396
1628
|
`MODULO: ${resumo.modulo}`,
|
|
1397
1629
|
`FAZ: ${resumo.faz}`,
|
|
1398
1630
|
`PERFIL: ${resumo.perfilCompatibilidade}`,
|
|
1399
|
-
|
|
1631
|
+
`MODO_CODIGO: ${resumo.modoVerificacaoCodigo}`,
|
|
1632
|
+
...(resumo.avisoVerificacaoCodigo ? [`AVISO_CODIGO: ${resumo.avisoVerificacaoCodigo}`] : []),
|
|
1633
|
+
`FONTES_CONCLUSAO: ${resumirListaTexto(resumo.fontesConclusao, limite)}`,
|
|
1400
1634
|
`CONSUMER_FRAMEWORK: ${resumo.consumerFramework ?? "nenhum"}`,
|
|
1401
1635
|
`APP_ROUTES: ${resumirListaTexto(resumo.appRoutes, limite)}`,
|
|
1402
1636
|
`CONSUMER_SURFACES: ${resumirListaTexto(resumo.consumerSurfaces, limite)}`,
|
|
@@ -1421,8 +1655,7 @@ function renderizarResumoModuloTexto(resumo, tamanho, modo) {
|
|
|
1421
1655
|
`GERADO_EM: ${resumo.geradoEm}`,
|
|
1422
1656
|
];
|
|
1423
1657
|
if (tamanho === "micro") {
|
|
1424
|
-
|
|
1425
|
-
return `${linhas.slice(0, limiteMicro).join("\n")}\n`;
|
|
1658
|
+
return `${linhas.slice(0, 12).join("\n")}\n`;
|
|
1426
1659
|
}
|
|
1427
1660
|
return `${linhas.join("\n")}\n`;
|
|
1428
1661
|
}
|
|
@@ -1434,6 +1667,9 @@ function renderizarResumoModuloMarkdown(resumo, modo, guiaPorCapacidade) {
|
|
|
1434
1667
|
`- Gerado em: \`${resumo.geradoEm}\``,
|
|
1435
1668
|
`- Arquivo: \`${resumo.arquivo}\``,
|
|
1436
1669
|
`- Perfil: \`${resumo.perfilCompatibilidade}\``,
|
|
1670
|
+
`- Modo de codigo: \`${resumo.modoVerificacaoCodigo}\``,
|
|
1671
|
+
...(resumo.avisoVerificacaoCodigo ? [`- Aviso de codigo: ${resumo.avisoVerificacaoCodigo}`] : []),
|
|
1672
|
+
`- Fontes de conclusao: ${resumirListaTexto(resumo.fontesConclusao, 6)}`,
|
|
1437
1673
|
`- Score: \`${resumo.scoreSemantico}\``,
|
|
1438
1674
|
`- Confianca: \`${resumo.confiancaGeral}\``,
|
|
1439
1675
|
`- Risco operacional: \`${resumo.riscoOperacional}\``,
|
|
@@ -1449,9 +1685,6 @@ function renderizarResumoModuloMarkdown(resumo, modo, guiaPorCapacidade) {
|
|
|
1449
1685
|
`- Entradas chave: ${resumirListaTexto(resumo.entradasChave, 6)}`,
|
|
1450
1686
|
`- Saidas chave: ${resumirListaTexto(resumo.saidasChave, 6)}`,
|
|
1451
1687
|
`- Regras criticas: ${resumirListaTexto(resumo.regrasCriticas, 6)}`,
|
|
1452
|
-
...(resumo.regrasAuthorAutomaticas.length > 0
|
|
1453
|
-
? [`- Regras Author automaticas: ${resumirListaTexto(resumo.regrasAuthorAutomaticas, 20)}`]
|
|
1454
|
-
: []),
|
|
1455
1688
|
`- Efeitos: ${resumirListaTexto(resumo.efeitos, 6)}`,
|
|
1456
1689
|
`- Erros: ${resumirListaTexto(resumo.erros, 6)}`,
|
|
1457
1690
|
`- Entidades afetadas: ${resumirListaTexto(resumo.entidadesAfetadas, 6)}`,
|
|
@@ -1501,6 +1734,9 @@ function criarBriefingMinimo(resumo, modo, tamanho) {
|
|
|
1501
1734
|
tamanho,
|
|
1502
1735
|
arquivo: resumo.arquivo,
|
|
1503
1736
|
modulo: resumo.modulo,
|
|
1737
|
+
modoVerificacaoCodigo: resumo.modoVerificacaoCodigo,
|
|
1738
|
+
avisoVerificacaoCodigo: resumo.avisoVerificacaoCodigo,
|
|
1739
|
+
fontesConclusao: resumo.fontesConclusao,
|
|
1504
1740
|
perfilCompatibilidade: resumo.perfilCompatibilidade,
|
|
1505
1741
|
scoreSemantico: resumo.scoreSemantico,
|
|
1506
1742
|
confiancaGeral: resumo.confiancaGeral,
|
|
@@ -1511,7 +1747,6 @@ function criarBriefingMinimo(resumo, modo, tamanho) {
|
|
|
1511
1747
|
entradasChave: resumo.entradasChave,
|
|
1512
1748
|
saidasChave: resumo.saidasChave,
|
|
1513
1749
|
regrasCriticas: resumo.regrasCriticas,
|
|
1514
|
-
regrasAuthorAutomaticas: resumo.regrasAuthorAutomaticas,
|
|
1515
1750
|
efeitos: resumo.efeitos,
|
|
1516
1751
|
erros: resumo.erros,
|
|
1517
1752
|
arquivosProvaveis: resumo.arquivosProvaveis,
|
|
@@ -1533,7 +1768,7 @@ function criarPromptCurtoModulo(resumo, modo, tamanho, capacidade) {
|
|
|
1533
1768
|
const resumoTexto = renderizarResumoModuloTexto(resumo, tamanho, modo).trim();
|
|
1534
1769
|
return `Voce esta operando Sema em modo IA-first.
|
|
1535
1770
|
|
|
1536
|
-
Esta linguagem
|
|
1771
|
+
Esta linguagem existe para traduzir intencao operacional em contrato consumivel por IA. Humanos aprovam; agentes operam.
|
|
1537
1772
|
|
|
1538
1773
|
Capacidade alvo: ${capacidade}
|
|
1539
1774
|
Modo da tarefa: ${modo}
|
|
@@ -1552,11 +1787,10 @@ ${resumoTexto}
|
|
|
1552
1787
|
}
|
|
1553
1788
|
function renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade) {
|
|
1554
1789
|
const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
|
|
1555
|
-
const regrasAuthorAutomaticas = unicosOrdenados(modulos.flatMap((modulo) => modulo.regrasAuthorAutomaticas));
|
|
1556
1790
|
const linhas = [
|
|
1557
1791
|
"# SEMA_BRIEF",
|
|
1558
1792
|
"",
|
|
1559
|
-
"Sema e IA-first. Este arquivo existe para IA achar o ponto de entrada do projeto sem
|
|
1793
|
+
"Sema e IA-first. Este arquivo existe para IA achar o ponto de entrada do projeto sem varrer o repo inteiro no escuro.",
|
|
1560
1794
|
"",
|
|
1561
1795
|
`- Gerado em: \`${geradoEm}\``,
|
|
1562
1796
|
`- Modulos: \`${modulos.length}\``,
|
|
@@ -1567,9 +1801,6 @@ function renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade) {
|
|
|
1567
1801
|
`- IA pequena: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
|
|
1568
1802
|
`- IA media: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
|
|
1569
1803
|
`- IA grande: ${entradaCanonica.porCapacidade.grande.join(" -> ")}`,
|
|
1570
|
-
...(regrasAuthorAutomaticas.length > 0
|
|
1571
|
-
? [`- Regras Author automaticas: ${resumirListaTexto(regrasAuthorAutomaticas, 20)}`]
|
|
1572
|
-
: []),
|
|
1573
1804
|
"",
|
|
1574
1805
|
"## Guia por capacidade",
|
|
1575
1806
|
"",
|
|
@@ -1594,7 +1825,7 @@ function renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade) {
|
|
|
1594
1825
|
}
|
|
1595
1826
|
function criarEntradaCanonicaProjeto(guiaPorCapacidade) {
|
|
1596
1827
|
return {
|
|
1597
|
-
descricao: "Entrada canonica do repositorio para IA. O repo nao e human-first; a IA deve
|
|
1828
|
+
descricao: "Entrada canonica do repositorio para IA. O repo nao e human-first; a IA deve comecar por esses artefatos antes de abrir codigo cru.",
|
|
1598
1829
|
ordemLeitura: [...ARQUIVOS_CANONICOS_IA_RAIZ],
|
|
1599
1830
|
porCapacidade: {
|
|
1600
1831
|
pequena: ["llms.txt", "SEMA_BRIEF.micro.txt", "SEMA_INDEX.json", "AGENTS.md"],
|
|
@@ -1863,6 +2094,8 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1863
2094
|
const geradoEm = new Date().toISOString();
|
|
1864
2095
|
const guiaPorCapacidade = criarGuiaCapacidadeIa();
|
|
1865
2096
|
const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
|
|
2097
|
+
const modoVerificacaoCodigo = await detectarModoVerificacaoCodigo(contextoProjeto.baseProjeto, contextoProjeto.diretoriosCodigo);
|
|
2098
|
+
const fontesConclusao = await detectarFontesConclusaoSnapshot(contextoProjeto.baseProjeto);
|
|
1866
2099
|
const resultadoDrift = await analisarDriftLegado(contextoProjeto);
|
|
1867
2100
|
const modulos = contextoProjeto.modulosSelecionados.map((item) => {
|
|
1868
2101
|
const modulo = item.resultado.modulo?.nome ?? path.basename(item.caminho, ".sema");
|
|
@@ -1882,6 +2115,8 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1882
2115
|
resumo: driftResumo,
|
|
1883
2116
|
drift: resultadoDrift,
|
|
1884
2117
|
},
|
|
2118
|
+
modoVerificacaoCodigo,
|
|
2119
|
+
fontesConclusao,
|
|
1885
2120
|
});
|
|
1886
2121
|
});
|
|
1887
2122
|
const baseProjeto = contextoProjeto.baseProjeto;
|
|
@@ -1892,24 +2127,25 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1892
2127
|
: path.resolve(baseProjeto, ".tmp", "sema-resumo");
|
|
1893
2128
|
await mkdir(pastaSaida, { recursive: true });
|
|
1894
2129
|
const semaBrief = renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade);
|
|
1895
|
-
const regrasAuthorAutomaticas = unicosOrdenados(modulos.flatMap((modulo) => modulo.regrasAuthorAutomaticas));
|
|
1896
|
-
const linhasRegrasAuthorAutomaticas = renderizarLinhasRegrasAuthorAutomaticas(regrasAuthorAutomaticas);
|
|
1897
2130
|
const indexJson = {
|
|
1898
2131
|
comando: "resumo-projeto",
|
|
1899
2132
|
geradoEm,
|
|
1900
2133
|
cliVersao: VERSAO_CLI,
|
|
1901
2134
|
baseProjeto,
|
|
2135
|
+
modoVerificacaoCodigo,
|
|
2136
|
+
fontesConclusao,
|
|
2137
|
+
conclusoesPorFonte: descreverFontesConclusao(fontesConclusao, modoVerificacaoCodigo),
|
|
1902
2138
|
totalModulos: modulos.length,
|
|
1903
2139
|
entradaCanonica,
|
|
1904
2140
|
guiaPorCapacidade,
|
|
1905
|
-
regrasAuthorAutomaticas,
|
|
1906
2141
|
modulos,
|
|
1907
2142
|
};
|
|
1908
2143
|
const micro = [
|
|
1909
2144
|
`PROJETO: ${path.basename(baseProjeto)}`,
|
|
1910
2145
|
`MODULOS: ${modulos.length}`,
|
|
2146
|
+
`MODO_CODIGO: ${modoVerificacaoCodigo}`,
|
|
2147
|
+
`FONTES_CONCLUSAO: ${resumirListaTexto(fontesConclusao, 4)}`,
|
|
1911
2148
|
`ENTRADA_IA: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
|
|
1912
|
-
...linhasRegrasAuthorAutomaticas,
|
|
1913
2149
|
`TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 3)}`,
|
|
1914
2150
|
`TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 3)}`,
|
|
1915
2151
|
`TOP_LACUNAS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.lacunas)), 3)}`,
|
|
@@ -1920,8 +2156,14 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1920
2156
|
`PROJETO: ${path.basename(baseProjeto)}`,
|
|
1921
2157
|
`BASE: ${baseProjeto}`,
|
|
1922
2158
|
`MODULOS: ${modulos.length}`,
|
|
2159
|
+
`MODO_CODIGO: ${modoVerificacaoCodigo}`,
|
|
2160
|
+
...(modoVerificacaoCodigo === "contratos_apenas"
|
|
2161
|
+
? ["AVISO_CODIGO: somente contratos enviados; implementacao nao verificada neste modo"]
|
|
2162
|
+
: modoVerificacaoCodigo === "codigo_selecionado"
|
|
2163
|
+
? ["AVISO_CODIGO: codigo selecionado enviado; conclusoes valem apenas para o snapshot"]
|
|
2164
|
+
: []),
|
|
2165
|
+
`FONTES_CONCLUSAO: ${resumirListaTexto(fontesConclusao, 6)}`,
|
|
1923
2166
|
`ENTRADA_IA: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
|
|
1924
|
-
...linhasRegrasAuthorAutomaticas,
|
|
1925
2167
|
`TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 6)}`,
|
|
1926
2168
|
`TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 6)}`,
|
|
1927
2169
|
`TOP_LACUNAS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.lacunas)), 6)}`,
|
|
@@ -1937,6 +2179,7 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1937
2179
|
geradoEm,
|
|
1938
2180
|
baseProjeto,
|
|
1939
2181
|
pastaSaida,
|
|
2182
|
+
modoVerificacaoCodigo,
|
|
1940
2183
|
artefatos: ["SEMA_BRIEF.md", "SEMA_BRIEF.micro.txt", "SEMA_BRIEF.curto.txt", "SEMA_INDEX.json"],
|
|
1941
2184
|
modulos,
|
|
1942
2185
|
guiaPorCapacidade,
|
|
@@ -3334,6 +3577,1544 @@ async function comandoInstalarExemplos(emJson) {
|
|
|
3334
3577
|
console.log(`- Preservados: ${resultado.preservados.length}`);
|
|
3335
3578
|
return 0;
|
|
3336
3579
|
}
|
|
3580
|
+
const POLITICAS_AUTHOR_GATE = [
|
|
3581
|
+
{
|
|
3582
|
+
id: "escolhido_predestinado",
|
|
3583
|
+
categoria: "cliche",
|
|
3584
|
+
contratoTokens: [
|
|
3585
|
+
"sem_cliche_escolhido_predestinado",
|
|
3586
|
+
"cliche_escolhido_predestinado",
|
|
3587
|
+
"aceitar_cliche_sem_justificativa",
|
|
3588
|
+
],
|
|
3589
|
+
regex: /\b(escolhid[oa]|predestinad[oa]|profecia)\b/gi,
|
|
3590
|
+
severidade: "alta",
|
|
3591
|
+
motivo: "Destino abstrato substitui desejo, decisao e custo concreto.",
|
|
3592
|
+
sugestao: "Troque destino abstrato por desejo, custo e conflito concreto.",
|
|
3593
|
+
sugestaoReescrita: "Comece por uma escolha sob pressao, uma perda concreta ou um prazo que force o personagem a agir.",
|
|
3594
|
+
},
|
|
3595
|
+
{
|
|
3596
|
+
id: "dialogo_resumo",
|
|
3597
|
+
categoria: "dialogo",
|
|
3598
|
+
contratoTokens: [
|
|
3599
|
+
"sem_dialogo_resumo",
|
|
3600
|
+
"dialogo_resumo",
|
|
3601
|
+
"dialogo_expositivo",
|
|
3602
|
+
"aceitar_dialogo_expositivo",
|
|
3603
|
+
],
|
|
3604
|
+
regex: /\b(como voce sabe|deixe-me explicar|resumindo|em outras palavras)\b/gi,
|
|
3605
|
+
severidade: "media",
|
|
3606
|
+
motivo: "Dialogo explica informacao para o leitor em vez de criar atrito entre personagens.",
|
|
3607
|
+
sugestao: "Transforme explicacao em atrito, subtexto ou acao observavel.",
|
|
3608
|
+
sugestaoReescrita: "Troque a explicacao direta por uma acusacao, hesitacao, pergunta hostil ou gesto que revele a informacao.",
|
|
3609
|
+
},
|
|
3610
|
+
{
|
|
3611
|
+
id: "frase_de_efeito_vazia",
|
|
3612
|
+
categoria: "promessa_narrativa",
|
|
3613
|
+
contratoTokens: [
|
|
3614
|
+
"sem_frase_de_efeito_vazia",
|
|
3615
|
+
"frase_de_efeito_vazia",
|
|
3616
|
+
"validar_promessa_narrativa",
|
|
3617
|
+
],
|
|
3618
|
+
regex: /\b(o destino nos chama|a esperanca e tudo|o tempo dira|no fim tudo ficara bem)\b/gi,
|
|
3619
|
+
severidade: "media",
|
|
3620
|
+
motivo: "Frase pronta promete emocao sem consequencia especifica de cena.",
|
|
3621
|
+
sugestao: "Substitua frase pronta por consequencia especifica da cena.",
|
|
3622
|
+
sugestaoReescrita: "Ancore a frase em objeto, dano, promessa quebrada ou consequencia verificavel.",
|
|
3623
|
+
},
|
|
3624
|
+
{
|
|
3625
|
+
id: "voz_generica_de_ia",
|
|
3626
|
+
categoria: "voz",
|
|
3627
|
+
contratoTokens: [
|
|
3628
|
+
"sem_voz_generica_de_ia",
|
|
3629
|
+
"voz_generica_de_ia",
|
|
3630
|
+
"aceitar_voz_generica_de_ia",
|
|
3631
|
+
"politica_estilo_por_autor",
|
|
3632
|
+
],
|
|
3633
|
+
regex: /\b(uma jornada de autodescoberta|emocionante e envolvente|profundamente humano|historia cativante)\b/gi,
|
|
3634
|
+
severidade: "alta",
|
|
3635
|
+
motivo: "Rotulo editorial descreve qualidade pretendida em vez de produzir experiencia narrativa.",
|
|
3636
|
+
sugestao: "Corte rotulo editorial e mostre textura, decisao e perda.",
|
|
3637
|
+
sugestaoReescrita: "Mostre uma imagem concreta, uma decisao desconfortavel ou uma perda que carregue o tom.",
|
|
3638
|
+
},
|
|
3639
|
+
{
|
|
3640
|
+
id: "moralizacao_barata",
|
|
3641
|
+
categoria: "tom",
|
|
3642
|
+
contratoTokens: [
|
|
3643
|
+
"sem_moralizacao_barata",
|
|
3644
|
+
"moralizacao_barata",
|
|
3645
|
+
"score_coerencia_tonal",
|
|
3646
|
+
],
|
|
3647
|
+
regex: /\b(licao importante|todos aprenderam|moral da historia|o bem sempre vence)\b/gi,
|
|
3648
|
+
severidade: "media",
|
|
3649
|
+
motivo: "Conclusao moralizante entrega a interpretacao pronta e reduz ambiguidade.",
|
|
3650
|
+
sugestao: "Deixe a consequencia moral emergir da acao, nao de sermoes.",
|
|
3651
|
+
sugestaoReescrita: "Troque a licao declarada por uma consequencia que obrigue o leitor a inferir o custo moral.",
|
|
3652
|
+
},
|
|
3653
|
+
{
|
|
3654
|
+
id: "exposicao_sem_conflito",
|
|
3655
|
+
categoria: "conflito",
|
|
3656
|
+
contratoTokens: [
|
|
3657
|
+
"sem_exposicao_sem_conflito",
|
|
3658
|
+
"exposicao_sem_conflito",
|
|
3659
|
+
"validar_promessa_narrativa",
|
|
3660
|
+
],
|
|
3661
|
+
regex: /\b(ele explicou tudo|ela contou toda a verdade|a historia era simples)\b/gi,
|
|
3662
|
+
severidade: "media",
|
|
3663
|
+
motivo: "Exposicao resolve informacao sem resistencia, suspeita ou conflito.",
|
|
3664
|
+
sugestao: "Divida informacao em conflito, suspeita, erro ou descoberta.",
|
|
3665
|
+
sugestaoReescrita: "Quebre a informacao em revelacao parcial, mal-entendido, mentira util ou descoberta com preco.",
|
|
3666
|
+
},
|
|
3667
|
+
];
|
|
3668
|
+
function contarOcorrencias(regex, texto) {
|
|
3669
|
+
const flags = regex.flags.includes("g") ? regex.flags : `${regex.flags}g`;
|
|
3670
|
+
return Array.from(texto.matchAll(new RegExp(regex.source, flags))).length;
|
|
3671
|
+
}
|
|
3672
|
+
function posicaoLinhaColunaAuthor(texto, indice) {
|
|
3673
|
+
const antes = texto.slice(0, Math.max(0, indice));
|
|
3674
|
+
const linhas = antes.split(/\r?\n/);
|
|
3675
|
+
return {
|
|
3676
|
+
linha: linhas.length,
|
|
3677
|
+
coluna: (linhas.at(-1)?.length ?? 0) + 1,
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
function recortarContextoAuthor(texto, inicio, fim, margem = 70) {
|
|
3681
|
+
const alvoInicio = Math.max(0, inicio - margem);
|
|
3682
|
+
const alvoFim = Math.min(texto.length, fim + margem);
|
|
3683
|
+
const recorte = texto.slice(alvoInicio, alvoFim).replace(/\s+/g, " ").trim();
|
|
3684
|
+
const prefixo = alvoInicio > 0 ? "... " : "";
|
|
3685
|
+
const sufixo = alvoFim < texto.length ? " ..." : "";
|
|
3686
|
+
return `${prefixo}${recorte}${sufixo}`.trim();
|
|
3687
|
+
}
|
|
3688
|
+
function localizarTrechosRegexAuthor(regex, texto, motivo, sugestao, limite = 5) {
|
|
3689
|
+
const flags = regex.flags.includes("g") ? regex.flags : `${regex.flags}g`;
|
|
3690
|
+
const local = new RegExp(regex.source, flags);
|
|
3691
|
+
const trechos = [];
|
|
3692
|
+
const vistos = new Set();
|
|
3693
|
+
for (const match of texto.matchAll(local)) {
|
|
3694
|
+
const raw = match[0] ?? "";
|
|
3695
|
+
const inicio = match.index ?? -1;
|
|
3696
|
+
if (!raw || inicio < 0)
|
|
3697
|
+
continue;
|
|
3698
|
+
const fim = inicio + raw.length;
|
|
3699
|
+
const { linha, coluna } = posicaoLinhaColunaAuthor(texto, inicio);
|
|
3700
|
+
const chave = `${linha}:${coluna}:${raw.toLowerCase()}`;
|
|
3701
|
+
if (vistos.has(chave))
|
|
3702
|
+
continue;
|
|
3703
|
+
vistos.add(chave);
|
|
3704
|
+
trechos.push({
|
|
3705
|
+
texto: recortarContextoAuthor(texto, inicio, fim),
|
|
3706
|
+
linha,
|
|
3707
|
+
coluna,
|
|
3708
|
+
inicio,
|
|
3709
|
+
fim,
|
|
3710
|
+
motivo,
|
|
3711
|
+
sugestao,
|
|
3712
|
+
});
|
|
3713
|
+
if (trechos.length >= limite)
|
|
3714
|
+
break;
|
|
3715
|
+
}
|
|
3716
|
+
return trechos;
|
|
3717
|
+
}
|
|
3718
|
+
function localizarTrechosPorEvidenciasAuthor(texto, evidencias, motivo, sugestao) {
|
|
3719
|
+
const trechos = [];
|
|
3720
|
+
const vistos = new Set();
|
|
3721
|
+
for (const evidencia of evidencias.filter(Boolean)) {
|
|
3722
|
+
const regex = regexFraseAuthor(evidencia);
|
|
3723
|
+
for (const trecho of localizarTrechosRegexAuthor(regex, texto, motivo, sugestao, 3)) {
|
|
3724
|
+
const chave = `${trecho.linha}:${trecho.coluna}:${trecho.texto}`;
|
|
3725
|
+
if (vistos.has(chave))
|
|
3726
|
+
continue;
|
|
3727
|
+
vistos.add(chave);
|
|
3728
|
+
trechos.push(trecho);
|
|
3729
|
+
if (trechos.length >= 5)
|
|
3730
|
+
return trechos;
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
return trechos;
|
|
3734
|
+
}
|
|
3735
|
+
function criarTrechoContextualAuthor(texto, motivo, sugestao) {
|
|
3736
|
+
const match = texto.match(/\S[\s\S]{0,180}/);
|
|
3737
|
+
if (!match || match.index === undefined)
|
|
3738
|
+
return [];
|
|
3739
|
+
const inicio = match.index;
|
|
3740
|
+
const fim = inicio + match[0].length;
|
|
3741
|
+
const { linha, coluna } = posicaoLinhaColunaAuthor(texto, inicio);
|
|
3742
|
+
return [{
|
|
3743
|
+
texto: recortarContextoAuthor(texto, inicio, fim, 0),
|
|
3744
|
+
linha,
|
|
3745
|
+
coluna,
|
|
3746
|
+
inicio,
|
|
3747
|
+
fim,
|
|
3748
|
+
motivo,
|
|
3749
|
+
sugestao,
|
|
3750
|
+
}];
|
|
3751
|
+
}
|
|
3752
|
+
function normalizarTextoAuthor(texto) {
|
|
3753
|
+
return texto
|
|
3754
|
+
.normalize("NFD")
|
|
3755
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
3756
|
+
.toLowerCase();
|
|
3757
|
+
}
|
|
3758
|
+
function extrairTokensContratoAuthor(conteudo) {
|
|
3759
|
+
const tokens = new Set();
|
|
3760
|
+
for (const match of conteudo.matchAll(/\b[A-Za-z_][A-Za-z0-9_.-]*\b/g)) {
|
|
3761
|
+
tokens.add(match[0]);
|
|
3762
|
+
}
|
|
3763
|
+
return tokens;
|
|
3764
|
+
}
|
|
3765
|
+
function politicaAuthorAtiva(politica, tokensContrato) {
|
|
3766
|
+
return [
|
|
3767
|
+
politica.id,
|
|
3768
|
+
`sem_${politica.id}`,
|
|
3769
|
+
`validar_${politica.id}`,
|
|
3770
|
+
`aceitar_${politica.id}`,
|
|
3771
|
+
...politica.contratoTokens,
|
|
3772
|
+
].some((token) => tokensContrato.has(token));
|
|
3773
|
+
}
|
|
3774
|
+
function regexFraseAuthor(frase) {
|
|
3775
|
+
const escapada = frase
|
|
3776
|
+
.trim()
|
|
3777
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
3778
|
+
.replace(/\s+/g, "\\s+");
|
|
3779
|
+
return new RegExp(`\\b${escapada}\\b`, "gi");
|
|
3780
|
+
}
|
|
3781
|
+
function slugParaTextoAuthor(slug) {
|
|
3782
|
+
return slug.replace(/_/g, " ").trim();
|
|
3783
|
+
}
|
|
3784
|
+
function riscoAuthorPorSeveridade(severidade) {
|
|
3785
|
+
return severidade === "alta" ? "alto" : severidade === "media" ? "medio" : "baixo";
|
|
3786
|
+
}
|
|
3787
|
+
function severidadeAuthorPadrao(severidade, categoria) {
|
|
3788
|
+
if (categoria === "tema_sensivel")
|
|
3789
|
+
return "critical";
|
|
3790
|
+
if (severidade === "alta")
|
|
3791
|
+
return categoria === "proibicao_literal" ? "blocking" : "blocking";
|
|
3792
|
+
if (severidade === "media")
|
|
3793
|
+
return "warning";
|
|
3794
|
+
return "info";
|
|
3795
|
+
}
|
|
3796
|
+
function normalizarIdAuthor(valor) {
|
|
3797
|
+
return normalizarTextoAuthor(valor)
|
|
3798
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
3799
|
+
.replace(/^_+|_+$/g, "");
|
|
3800
|
+
}
|
|
3801
|
+
function removerComentarioSema(linha) {
|
|
3802
|
+
return linha
|
|
3803
|
+
.replace(/\/\/.*$/g, "")
|
|
3804
|
+
.replace(/#.*$/g, "")
|
|
3805
|
+
.trim();
|
|
3806
|
+
}
|
|
3807
|
+
function extrairLinhasForbiddenAuthor(conteudo) {
|
|
3808
|
+
const linhas = [];
|
|
3809
|
+
for (const bloco of conteudo.matchAll(/forbidden\s*\{([\s\S]*?)\}/gi)) {
|
|
3810
|
+
for (const linha of bloco[1].split(/\r?\n/)) {
|
|
3811
|
+
const limpa = removerComentarioSema(linha);
|
|
3812
|
+
if (!limpa || limpa === "}")
|
|
3813
|
+
continue;
|
|
3814
|
+
linhas.push(limpa);
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
return linhas;
|
|
3818
|
+
}
|
|
3819
|
+
function extrairFrasesProibidasAuthor(conteudo) {
|
|
3820
|
+
const frases = [];
|
|
3821
|
+
const regex = /\b(?:avoid|evitar|proibir|proibido|proibida|banir|banido|banida|nao_usar|nao usar)\s*["'`](.+?)["'`]/gi;
|
|
3822
|
+
for (const match of conteudo.matchAll(regex)) {
|
|
3823
|
+
const frase = match[1]?.trim();
|
|
3824
|
+
if (frase)
|
|
3825
|
+
frases.push(frase);
|
|
3826
|
+
}
|
|
3827
|
+
return frases;
|
|
3828
|
+
}
|
|
3829
|
+
function politicaCatalogoConhecidaAuthor(token) {
|
|
3830
|
+
const chave = normalizarIdAuthor(token.replace(/^(sem|validar|aceitar)_/, ""));
|
|
3831
|
+
return POLITICAS_AUTHOR_GATE.some((politica) => (politica.id === token
|
|
3832
|
+
|| politica.id === chave
|
|
3833
|
+
|| politica.contratoTokens.includes(token)
|
|
3834
|
+
|| politica.contratoTokens.includes(chave)));
|
|
3835
|
+
}
|
|
3836
|
+
function textoProibicaoLiteralAuthor(valor) {
|
|
3837
|
+
const bruto = valor.trim().replace(/^["'`]|["'`]$/g, "").trim();
|
|
3838
|
+
if (!bruto)
|
|
3839
|
+
return null;
|
|
3840
|
+
if (/\s/.test(bruto))
|
|
3841
|
+
return bruto.replace(/\s+/g, " ");
|
|
3842
|
+
const dinamica = bruto.match(/^(?:frase|palavra|termo)_(?:banida|banido|proibida|proibido)_(.+)$/);
|
|
3843
|
+
if (dinamica)
|
|
3844
|
+
return slugParaTextoAuthor(dinamica[1]);
|
|
3845
|
+
const explicita = bruto.match(/^(?:proibido|proibida|banido|banida|nao_usar|evitar|sem)_(.+)$/);
|
|
3846
|
+
if (explicita && !politicaCatalogoConhecidaAuthor(explicita[1])) {
|
|
3847
|
+
return slugParaTextoAuthor(explicita[1]);
|
|
3848
|
+
}
|
|
3849
|
+
if (politicaCatalogoConhecidaAuthor(bruto))
|
|
3850
|
+
return null;
|
|
3851
|
+
if (/^(aceitar|gerar|publicar|obra|fontes|estilo|voz|dialogo|cliche|exposicao|moralizacao|score|tratar|ignorar|aprovar|validar|contrato|texto|diagnostico|author|revisor|sensitivity|proibicao)_/.test(bruto)) {
|
|
3852
|
+
return null;
|
|
3853
|
+
}
|
|
3854
|
+
if (!bruto.includes("_"))
|
|
3855
|
+
return null;
|
|
3856
|
+
return slugParaTextoAuthor(bruto);
|
|
3857
|
+
}
|
|
3858
|
+
function politicasProibicoesLiteraisAuthor(conteudo, ir) {
|
|
3859
|
+
const tarefas = ir?.tasks ?? [];
|
|
3860
|
+
const superficies = ir?.superficies ?? [];
|
|
3861
|
+
const declaradas = [
|
|
3862
|
+
...tarefas.flatMap((task) => task.forbidden.regras),
|
|
3863
|
+
...superficies.flatMap((superficie) => superficie.forbidden.regras),
|
|
3864
|
+
...extrairLinhasForbiddenAuthor(conteudo),
|
|
3865
|
+
...extrairFrasesProibidasAuthor(conteudo),
|
|
3866
|
+
];
|
|
3867
|
+
const politicas = [];
|
|
3868
|
+
const vistos = new Set();
|
|
3869
|
+
for (const declarada of declaradas) {
|
|
3870
|
+
const texto = textoProibicaoLiteralAuthor(declarada);
|
|
3871
|
+
if (!texto)
|
|
3872
|
+
continue;
|
|
3873
|
+
const idBase = normalizarIdAuthor(texto);
|
|
3874
|
+
if (!idBase || vistos.has(idBase))
|
|
3875
|
+
continue;
|
|
3876
|
+
vistos.add(idBase);
|
|
3877
|
+
politicas.push({
|
|
3878
|
+
id: `proibicao_literal_author_${idBase}`,
|
|
3879
|
+
categoria: "proibicao_literal",
|
|
3880
|
+
contratoTokens: [declarada],
|
|
3881
|
+
regex: regexFraseAuthor(texto),
|
|
3882
|
+
severidade: "alta",
|
|
3883
|
+
motivo: `O texto usa uma proibicao literal declarada no contrato Author: "${texto}".`,
|
|
3884
|
+
sugestao: `Remova "${texto}" ou edite o contrato antes de manter esse elemento narrativo.`,
|
|
3885
|
+
sugestaoReescrita: `Troque "${texto}" por um evento, objeto ou relacao permitida pelo contrato atual.`,
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
return politicas;
|
|
3889
|
+
}
|
|
3890
|
+
function politicasDinamicasContratoAuthor(tokensContrato) {
|
|
3891
|
+
const politicas = [];
|
|
3892
|
+
for (const token of tokensContrato) {
|
|
3893
|
+
const match = token.match(/^(frase|palavra|termo)_(?:banida|banido|proibida|proibido)_(.+)$/);
|
|
3894
|
+
if (!match) {
|
|
3895
|
+
continue;
|
|
3896
|
+
}
|
|
3897
|
+
const [, tipo, slug] = match;
|
|
3898
|
+
const texto = slugParaTextoAuthor(slug);
|
|
3899
|
+
const regex = tipo === "palavra"
|
|
3900
|
+
? new RegExp(`\\b${texto.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "gi")
|
|
3901
|
+
: regexFraseAuthor(texto);
|
|
3902
|
+
politicas.push({
|
|
3903
|
+
id: token,
|
|
3904
|
+
categoria: tipo === "palavra" ? "palavra_generica" : "frase_generica",
|
|
3905
|
+
contratoTokens: [token],
|
|
3906
|
+
regex,
|
|
3907
|
+
severidade: "alta",
|
|
3908
|
+
motivo: `Termo proibido pelo contrato Author: "${texto}".`,
|
|
3909
|
+
sugestao: `Remova "${texto}" ou justifique com detalhe concreto de cena.`,
|
|
3910
|
+
sugestaoReescrita: `Substitua "${texto}" por uma imagem, decisao ou consequencia especifica da obra.`,
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3913
|
+
return politicas;
|
|
3914
|
+
}
|
|
3915
|
+
function criarAchadoAuthor(politica, textoFonte, origem) {
|
|
3916
|
+
const trechos = localizarTrechosRegexAuthor(politica.regex, textoFonte, politica.motivo, politica.sugestaoReescrita);
|
|
3917
|
+
const ocorrencias = contarOcorrencias(politica.regex, textoFonte);
|
|
3918
|
+
if (ocorrencias <= 0) {
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3921
|
+
return {
|
|
3922
|
+
id: politica.id,
|
|
3923
|
+
categoria: politica.categoria,
|
|
3924
|
+
severidade: politica.severidade,
|
|
3925
|
+
severidadePadrao: severidadeAuthorPadrao(politica.severidade, politica.categoria),
|
|
3926
|
+
ocorrencias,
|
|
3927
|
+
risco: riscoAuthorPorSeveridade(politica.severidade),
|
|
3928
|
+
motivo: politica.motivo,
|
|
3929
|
+
sugestao: politica.sugestao,
|
|
3930
|
+
sugestaoReescrita: politica.sugestaoReescrita,
|
|
3931
|
+
origem,
|
|
3932
|
+
bloqueante: politica.severidade === "alta" || politica.categoria !== "observacao",
|
|
3933
|
+
trechos,
|
|
3934
|
+
evidencias: trechos.map((trecho) => trecho.texto),
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3937
|
+
function contarPalavrasAuthor(textoNormalizado, palavras) {
|
|
3938
|
+
return palavras.reduce((total, palavra) => {
|
|
3939
|
+
const regex = new RegExp(`\\b${palavra}\\b`, "g");
|
|
3940
|
+
return total + Array.from(textoNormalizado.matchAll(regex)).length;
|
|
3941
|
+
}, 0);
|
|
3942
|
+
}
|
|
3943
|
+
function criarAchadoHeuristicoAuthor(id, categoria, severidade, ocorrencias, sugestao, evidencias = [], textoFonte = "", motivo = sugestao, sugestaoReescrita = sugestao) {
|
|
3944
|
+
if (ocorrencias <= 0) {
|
|
3945
|
+
return null;
|
|
3946
|
+
}
|
|
3947
|
+
const trechos = textoFonte
|
|
3948
|
+
? localizarTrechosPorEvidenciasAuthor(textoFonte, evidencias, motivo, sugestaoReescrita)
|
|
3949
|
+
: [];
|
|
3950
|
+
return {
|
|
3951
|
+
id,
|
|
3952
|
+
categoria,
|
|
3953
|
+
severidade,
|
|
3954
|
+
severidadePadrao: severidadeAuthorPadrao(severidade, categoria),
|
|
3955
|
+
ocorrencias,
|
|
3956
|
+
risco: riscoAuthorPorSeveridade(severidade),
|
|
3957
|
+
motivo,
|
|
3958
|
+
sugestao,
|
|
3959
|
+
sugestaoReescrita,
|
|
3960
|
+
origem: "heuristica",
|
|
3961
|
+
bloqueante: true,
|
|
3962
|
+
trechos: trechos.length > 0 ? trechos : (textoFonte ? criarTrechoContextualAuthor(textoFonte, motivo, sugestaoReescrita) : []),
|
|
3963
|
+
evidencias,
|
|
3964
|
+
};
|
|
3965
|
+
}
|
|
3966
|
+
function contratoAutorizaTemaSensivel(conteudoContrato) {
|
|
3967
|
+
return /sensitivity_reviewer|validar_tema_sensivel|PublicoAuthor[\s\S]*SENSIVEL|PublicoSensivel/i.test(conteudoContrato);
|
|
3968
|
+
}
|
|
3969
|
+
function avaliarHeuristicasNarrativasAuthor(textoFonte, conteudoContrato, tokensContrato) {
|
|
3970
|
+
const normalizado = normalizarTextoAuthor(textoFonte);
|
|
3971
|
+
const achados = [];
|
|
3972
|
+
const gateAtivo = (token) => tokensContrato.has(token) || tokensContrato.has(`validar_${token}`) || tokensContrato.has(`sem_${token}`);
|
|
3973
|
+
if (gateAtivo("palavra_generica_sem_concreto") || tokensContrato.has("score_coerencia_tonal")) {
|
|
3974
|
+
const genericas = ["algo", "coisa", "muito", "incrivel", "especial", "importante", "profundo", "intenso", "cativante", "emocionante"];
|
|
3975
|
+
const ocorrencias = contarPalavrasAuthor(normalizado, genericas);
|
|
3976
|
+
achados.push(criarAchadoHeuristicoAuthor("palavra_generica_sem_concreto", "palavra_generica", ocorrencias >= 4 ? "alta" : "media", ocorrencias >= 3 ? ocorrencias : 0, "Troque adjetivo abstrato por imagem, acao, escolha ou custo observavel.", genericas.filter((palavra) => new RegExp(`\\b${palavra}\\b`).test(normalizado)), textoFonte, "Palavra generica aparece sem ancoragem concreta suficiente.", "Substitua por imagem fisica, acao especifica, escolha ou custo observavel."));
|
|
3977
|
+
}
|
|
3978
|
+
if (gateAtivo("arco_emocional_sem_mudanca")) {
|
|
3979
|
+
const temMarcadorMudanca = /\b(passou de|deixou de|percebeu|decidiu|renunciou|custou|perdeu|escolheu|mudou|foi obrigado|teve que)\b/i.test(normalizado);
|
|
3980
|
+
achados.push(criarAchadoHeuristicoAuthor("arco_emocional_sem_mudanca", "arco_emocional", "alta", textoFonte.trim().length > 120 && !temMarcadorMudanca ? 1 : 0, "Declare a mudanca emocional da cena: estado inicial, pressao, decisao e custo.", [], textoFonte, "Texto longo nao mostra mudanca emocional rastreavel.", "Mostre estado inicial, pressao, decisao e custo emocional em uma sequencia concreta."));
|
|
3981
|
+
}
|
|
3982
|
+
if (gateAtivo("personagem_sem_memoria")) {
|
|
3983
|
+
const temNome = /\b[A-Z][a-z]{2,}\b/.test(textoFonte);
|
|
3984
|
+
const temEstado = /\b(quer|teme|esconde|prometeu|lembra|culpa|ferida|objetivo|segredo)\b/i.test(normalizado);
|
|
3985
|
+
achados.push(criarAchadoHeuristicoAuthor("personagem_sem_memoria", "memoria_personagem", "alta", !temNome || !temEstado ? 1 : 0, "Amarre personagem a memoria persistente: desejo, medo, segredo, ferida ou promessa ja declarada.", [], textoFonte, "Personagem aparece sem memoria operacional suficiente para guiar continuidade.", "Declare desejo, medo, segredo, ferida ou promessa que possa ser reutilizada depois."));
|
|
3986
|
+
}
|
|
3987
|
+
if (gateAtivo("promessa_narrativa_quebrada")) {
|
|
3988
|
+
const temPromessa = /\b(promessa|prometeu|proteger|objetivo|misterio|conflito|pergunta|ameaca|custo)\b/i.test(normalizado);
|
|
3989
|
+
const resolveSemCusto = /\b(no fim tudo ficara bem|de repente tudo se resolveu|por sorte)\b/i.test(normalizado);
|
|
3990
|
+
achados.push(criarAchadoHeuristicoAuthor("promessa_narrativa_quebrada", "promessa_narrativa", resolveSemCusto ? "alta" : "media", resolveSemCusto || !temPromessa ? 1 : 0, "Toda promessa narrativa precisa de pergunta, tensao, consequencia e validacao antes de ser resolvida.", resolveSemCusto ? ["no fim tudo ficara bem", "de repente tudo se resolveu", "por sorte"].filter((termo) => normalizado.includes(termo)) : [], textoFonte, resolveSemCusto ? "Resolucao sem custo enfraquece a promessa narrativa." : "Texto nao explicita promessa, pergunta, ameaca ou custo narrativo.", "Amarre a promessa a pergunta, tensao, consequencia e validacao antes de resolver."));
|
|
3991
|
+
}
|
|
3992
|
+
if (gateAtivo("impacto_capitulo_nao_mapeado")) {
|
|
3993
|
+
const mudancaCanonica = /\b(morreu|matou|traiu|revelou|destruiu|rompeu|desapareceu|casou|venceu|perdeu)\b/i.test(normalizado);
|
|
3994
|
+
const temConsequencia = /\b(por isso|como consequencia|afetou|obriga|altera|muda|impacta|no capitulo seguinte)\b/i.test(normalizado);
|
|
3995
|
+
achados.push(criarAchadoHeuristicoAuthor("impacto_capitulo_nao_mapeado", "impact_map", "alta", mudancaCanonica && !temConsequencia ? 1 : 0, "Mudanca canonica precisa de impact map entre capitulos, relacoes, promessas e cenas futuras.", [], textoFonte, "Mudanca canonica aparece sem consequencia mapeada para continuidade.", "Declare quem muda, que promessa quebra e qual cena futura fica obrigada a responder."));
|
|
3996
|
+
}
|
|
3997
|
+
if (gateAtivo("tema_sensivel_sem_autorizacao")) {
|
|
3998
|
+
const termosSensiveis = ["suicidio", "abuso", "luto", "trauma", "violencia sexual", "automutilacao", "racismo"];
|
|
3999
|
+
const ocorrencias = termosSensiveis.filter((termo) => normalizado.includes(termo)).length;
|
|
4000
|
+
achados.push(criarAchadoHeuristicoAuthor("tema_sensivel_sem_autorizacao", "tema_sensivel", "alta", ocorrencias > 0 && !contratoAutorizaTemaSensivel(conteudoContrato) ? ocorrencias : 0, "Tema sensivel so pode entrar quando publico, fontes, limites e sensitivity reviewer estiverem contratados.", termosSensiveis.filter((termo) => normalizado.includes(termo)), textoFonte, "Tema sensivel detectado sem autorizacao explicita no contrato Author.", "Declare publico, limites, fontes e sensitivity reviewer antes de manter esse tema."));
|
|
4001
|
+
}
|
|
4002
|
+
if (gateAtivo("rpg_campanha_sem_estado")) {
|
|
4003
|
+
const mencionaCampanha = /\b(campanha|sessao|npc|jogador|party|mestre|dado|quest)\b/i.test(normalizado);
|
|
4004
|
+
const temEstadoCampanha = /\b(estado|inventario|local|missao|relacao|faccao|reputacao|checkpoint)\b/i.test(normalizado);
|
|
4005
|
+
achados.push(criarAchadoHeuristicoAuthor("rpg_campanha_sem_estado", "campanha_persistente", "media", mencionaCampanha && !temEstadoCampanha ? 1 : 0, "Modo RPG/campanha precisa preservar estado: local, inventario, relacoes, faccoes, missao e consequencias.", ["campanha", "sessao", "npc", "jogador", "party", "mestre", "dado", "quest"].filter((termo) => normalizado.includes(termo)), textoFonte, "Campanha/RPG foi mencionado sem estado persistente suficiente.", "Inclua local, inventario, relacoes, faccao, missao, checkpoint ou consequencia persistente."));
|
|
4006
|
+
}
|
|
4007
|
+
return achados.filter((achado) => achado !== null);
|
|
4008
|
+
}
|
|
4009
|
+
function extrairIrNarrativoAuthor(texto) {
|
|
4010
|
+
const normalizado = normalizarTextoAuthor(texto);
|
|
4011
|
+
const personagens = unicosOrdenados(Array.from(texto.matchAll(/\b[A-Z][a-z]{2,}\b/g))
|
|
4012
|
+
.map((match) => match[0])
|
|
4013
|
+
.filter((nome) => !["O", "A", "Os", "As", "Um", "Uma", "Por", "Para", "Isso", "Como"].includes(nome))).slice(0, 12);
|
|
4014
|
+
const capitulos = unicosOrdenados(Array.from(normalizado.matchAll(/\b(?:capitulo|sessao)\s+([a-z0-9_-]+)/g)).map((match) => match[0])).slice(0, 12);
|
|
4015
|
+
const eventos = unicosOrdenados(Array.from(normalizado.matchAll(/\b(morreu|matou|traiu|revelou|decidiu|perdeu|venceu|rompeu|descobriu|prometeu)\b/g)).map((match) => match[0])).slice(0, 12);
|
|
4016
|
+
const temasSensiveis = ["suicidio", "abuso", "luto", "trauma", "violencia sexual", "automutilacao", "racismo"]
|
|
4017
|
+
.filter((termo) => normalizado.includes(termo));
|
|
4018
|
+
const modoCampanha = /\b(campanha|sessao|npc|jogador|party|mestre|quest)\b/i.test(normalizado);
|
|
4019
|
+
return { personagens, capitulos, eventos, temasSensiveis, modoCampanha };
|
|
4020
|
+
}
|
|
4021
|
+
function compararDiffSemanticoAuthor(textoAnterior, textoAtual) {
|
|
4022
|
+
if (!textoAnterior) {
|
|
4023
|
+
return {
|
|
4024
|
+
disponivel: false,
|
|
4025
|
+
personagensAdicionados: [],
|
|
4026
|
+
personagensRemovidos: [],
|
|
4027
|
+
eventosAdicionados: [],
|
|
4028
|
+
eventosRemovidos: [],
|
|
4029
|
+
riscoDrift: "baixo",
|
|
4030
|
+
};
|
|
4031
|
+
}
|
|
4032
|
+
const anterior = extrairIrNarrativoAuthor(textoAnterior);
|
|
4033
|
+
const atual = extrairIrNarrativoAuthor(textoAtual);
|
|
4034
|
+
const adicionados = (atuais, antigos) => atuais.filter((item) => !antigos.includes(item));
|
|
4035
|
+
const removidos = (antigos, atuais) => antigos.filter((item) => !atuais.includes(item));
|
|
4036
|
+
const personagensAdicionados = adicionados(atual.personagens, anterior.personagens);
|
|
4037
|
+
const personagensRemovidos = removidos(anterior.personagens, atual.personagens);
|
|
4038
|
+
const eventosAdicionados = adicionados(atual.eventos, anterior.eventos);
|
|
4039
|
+
const eventosRemovidos = removidos(anterior.eventos, atual.eventos);
|
|
4040
|
+
const mudancas = personagensAdicionados.length + personagensRemovidos.length + eventosAdicionados.length + eventosRemovidos.length;
|
|
4041
|
+
return {
|
|
4042
|
+
disponivel: true,
|
|
4043
|
+
personagensAdicionados,
|
|
4044
|
+
personagensRemovidos,
|
|
4045
|
+
eventosAdicionados,
|
|
4046
|
+
eventosRemovidos,
|
|
4047
|
+
riscoDrift: mudancas >= 5 ? "alto" : mudancas >= 2 ? "medio" : "baixo",
|
|
4048
|
+
};
|
|
4049
|
+
}
|
|
4050
|
+
function calcularScoreAuthor(achados, categorias) {
|
|
4051
|
+
const relevantes = categorias ? achados.filter((achado) => categorias.includes(achado.categoria)) : achados;
|
|
4052
|
+
const penalidade = relevantes.reduce((total, achado) => {
|
|
4053
|
+
const peso = achado.severidade === "alta" ? 30 : achado.severidade === "media" ? 15 : 5;
|
|
4054
|
+
return total + (peso * Math.max(1, achado.ocorrencias));
|
|
4055
|
+
}, 0);
|
|
4056
|
+
return Math.max(0, 100 - penalidade);
|
|
4057
|
+
}
|
|
4058
|
+
function calcularScoreRiscoAuthor(achados, contratoValido) {
|
|
4059
|
+
const risco = achados.reduce((total, achado) => {
|
|
4060
|
+
const peso = achado.severidade === "alta" ? 35 : achado.severidade === "media" ? 18 : 6;
|
|
4061
|
+
return total + (peso * Math.max(1, achado.ocorrencias));
|
|
4062
|
+
}, contratoValido ? 0 : 40);
|
|
4063
|
+
return Math.min(100, risco);
|
|
4064
|
+
}
|
|
4065
|
+
function montarImpactMapAuthor(achados) {
|
|
4066
|
+
const camadasPorCategoria = {
|
|
4067
|
+
arco_emocional: ["personagem", "cena", "capitulo"],
|
|
4068
|
+
campanha_persistente: ["campanha", "sessao", "estado"],
|
|
4069
|
+
cliche: ["premissa", "personagem", "promessa"],
|
|
4070
|
+
conflito: ["cena", "informacao", "ritmo"],
|
|
4071
|
+
dialogo: ["dialogo", "subtexto", "exposicao"],
|
|
4072
|
+
frase_generica: ["estilo", "voz", "promessa"],
|
|
4073
|
+
impact_map: ["capitulo", "continuidade", "cenas futuras"],
|
|
4074
|
+
memoria_personagem: ["personagem", "relacoes", "canon"],
|
|
4075
|
+
palavra_generica: ["estilo", "imagem", "voz"],
|
|
4076
|
+
proibicao_literal: ["contrato", "texto", "guardrail"],
|
|
4077
|
+
promessa_narrativa: ["promessa", "resolucao", "expectativa"],
|
|
4078
|
+
tema_sensivel: ["publico", "fontes", "limites"],
|
|
4079
|
+
tom: ["tom", "publico", "estilo"],
|
|
4080
|
+
voz: ["voz", "estilo", "autor"],
|
|
4081
|
+
};
|
|
4082
|
+
return achados.map((achado) => ({
|
|
4083
|
+
alvo: achado.id,
|
|
4084
|
+
camadas: camadasPorCategoria[achado.categoria] ?? ["texto"],
|
|
4085
|
+
motivo: achado.sugestao,
|
|
4086
|
+
}));
|
|
4087
|
+
}
|
|
4088
|
+
function extrairGuardrailsAuthor(conteudo, ir) {
|
|
4089
|
+
const tarefas = ir?.tasks ?? [];
|
|
4090
|
+
const superficies = ir?.superficies ?? [];
|
|
4091
|
+
const linhasMarcadas = conteudo
|
|
4092
|
+
.split(/\r?\n/)
|
|
4093
|
+
.map((linha) => linha.trim())
|
|
4094
|
+
.filter((linha) => /(cliche|proibid|forbidden|sensivel|publico|fonte|estilo|voz|dialogo|conflito|atrito|dano|limite)/i.test(linha));
|
|
4095
|
+
return limitarLista(unicosOrdenados([
|
|
4096
|
+
...tarefas.flatMap((task) => task.rules),
|
|
4097
|
+
...tarefas.flatMap((task) => task.guarantees),
|
|
4098
|
+
...tarefas.flatMap((task) => task.forbidden.regras),
|
|
4099
|
+
...superficies.flatMap((superficie) => superficie.forbidden.regras),
|
|
4100
|
+
...linhasMarcadas,
|
|
4101
|
+
]), 18);
|
|
4102
|
+
}
|
|
4103
|
+
function extrairProibicoesAuthor(ir, conteudo) {
|
|
4104
|
+
const tarefas = ir?.tasks ?? [];
|
|
4105
|
+
const superficies = ir?.superficies ?? [];
|
|
4106
|
+
const proibicoesDeclaradas = [
|
|
4107
|
+
...tarefas.flatMap((task) => task.forbidden.regras),
|
|
4108
|
+
...superficies.flatMap((superficie) => superficie.forbidden.regras),
|
|
4109
|
+
];
|
|
4110
|
+
const proibicoesLiterais = politicasProibicoesLiteraisAuthor(conteudo, ir)
|
|
4111
|
+
.map((politica) => slugParaTextoAuthor(politica.id.replace(/^proibicao_literal_author_/, "")));
|
|
4112
|
+
const proibicoesTextuais = conteudo
|
|
4113
|
+
.split(/\r?\n/)
|
|
4114
|
+
.map((linha) => linha.trim())
|
|
4115
|
+
.filter((linha) => /^(aceitar_|gerar_|publicar_|obra_|fontes_|estilo_|voz_|dialogo_|cliche_|exposicao_|moralizacao_)/.test(linha));
|
|
4116
|
+
return limitarLista(unicosOrdenados([...proibicoesDeclaradas, ...proibicoesTextuais, ...proibicoesLiterais]), 16);
|
|
4117
|
+
}
|
|
4118
|
+
const REQUISITOS_PROFILE = {
|
|
4119
|
+
software: [
|
|
4120
|
+
{ id: "contrato_antes_codigo", descricao: "declara contrato antes de codigo vivo", termos: [/contrato/i, /codigo|implementacao|vivo/i], obrigatorio: true },
|
|
4121
|
+
{ id: "drift_e_impacto", descricao: "exige drift e mapa de impacto antes de editar", termos: [/drift/i, /impacto|impact map|mapa/i], obrigatorio: true },
|
|
4122
|
+
{ id: "vinculos_rastreaveis", descricao: "mantem vinculos rastreaveis entre contrato e implementacao", termos: [/vinculos?|impl/i, /arquivo|simbolo/i], obrigatorio: true },
|
|
4123
|
+
{ id: "checks_de_fechamento", descricao: "declara validacao, testes e verificacao de fechamento", termos: [/validar|verificar/i, /testes?|checks?/i], obrigatorio: true },
|
|
4124
|
+
],
|
|
4125
|
+
workflow: [
|
|
4126
|
+
{ id: "runtime_como_alvo", descricao: "trata workflow runtime como alvo de compatibilidade", termos: [/runtime|workflow|orquestracao/i, /compatibilidade|scorecard/i], obrigatorio: true },
|
|
4127
|
+
{ id: "adapter_honesto", descricao: "declara adapter sem fingir paridade total", termos: [/adapter|adaptado|n8n/i, /parcial|invalido|equivalencia/i], obrigatorio: true },
|
|
4128
|
+
{ id: "superficies_nativas", descricao: "mapeia webhook, cron, HTTP e branching", termos: [/webhook/i, /cron/i, /http|branching|transformacao/i], obrigatorio: true },
|
|
4129
|
+
{ id: "lacunas_operacionais", descricao: "marca lacunas de authz, dados, audit, compensacao e guarantees", termos: [/authz|autorizacao/i, /audit|auditoria/i, /compensacao|guarantees/i], obrigatorio: true },
|
|
4130
|
+
],
|
|
4131
|
+
ops: [
|
|
4132
|
+
{ id: "runbook_e_rollback", descricao: "exige runbook, rollback e criterio de recuperacao", termos: [/runbook/i, /rollback|recuperacao/i], obrigatorio: true },
|
|
4133
|
+
{ id: "observabilidade", descricao: "declara healthcheck, metricas, logs ou traces", termos: [/healthcheck|metricas|logs?|traces?|observabilidade/i], obrigatorio: true },
|
|
4134
|
+
{ id: "incidente_e_oncall", descricao: "modela incidente, severidade e responsavel operacional", termos: [/incidente|severidade/i, /responsavel|oncall|operador/i], obrigatorio: true },
|
|
4135
|
+
{ id: "segredos_e_acesso", descricao: "trata segredos e acesso como superficie operacional", termos: [/segredos?|secret/i, /authz|acesso|permissao/i], obrigatorio: true },
|
|
4136
|
+
],
|
|
4137
|
+
game: [
|
|
4138
|
+
{ id: "loop_jogavel", descricao: "declara loop jogavel e objetivo do jogador", termos: [/loop/i, /jogador|player/i], obrigatorio: true },
|
|
4139
|
+
{ id: "estado_e_regras", descricao: "declara estado, regras e transicoes de jogo", termos: [/estado|state/i, /regras?|transitions?|transicoes/i], obrigatorio: true },
|
|
4140
|
+
{ id: "falha_balanceamento", descricao: "modela falha, balanceamento e abuso", termos: [/falha|derrota|erro/i, /balanceamento|abuso|exploit/i], obrigatorio: true },
|
|
4141
|
+
{ id: "telemetria_jogo", descricao: "define telemetria ou replay de decisao", termos: [/telemetria|replay|metricas/i], obrigatorio: false },
|
|
4142
|
+
],
|
|
4143
|
+
legal: [
|
|
4144
|
+
{ id: "jurisdicao_e_escopo", descricao: "declara jurisdicao, escopo e limite de uso", termos: [/jurisdicao/i, /escopo|limite/i], obrigatorio: true },
|
|
4145
|
+
{ id: "fontes_normativas", descricao: "exige fontes normativas ou base documental", termos: [/fonte|norma|lei|regulamento/i, /citacao|evidencia|documento/i], obrigatorio: true },
|
|
4146
|
+
{ id: "revisao_humana", descricao: "bloqueia parecer final sem revisao humana", termos: [/revisao_humana|humana|advogado|aprovador/i, /bloqueio|forbidden|proib/i], obrigatorio: true },
|
|
4147
|
+
{ id: "auditoria_legal", descricao: "registra auditoria e retencao de decisao", termos: [/audit|auditoria/i, /retencao|correlacao|motivo/i], obrigatorio: true },
|
|
4148
|
+
],
|
|
4149
|
+
research: [
|
|
4150
|
+
{ id: "pergunta_metodo", descricao: "declara pergunta, metodo e criterio de evidencia", termos: [/pergunta|hipotese/i, /metodo|metodologia/i], obrigatorio: true },
|
|
4151
|
+
{ id: "fontes_e_citacoes", descricao: "exige fontes, citacoes e qualidade de evidencia", termos: [/fonte|citacao|referencia/i, /evidencia|confianca|qualidade/i], obrigatorio: true },
|
|
4152
|
+
{ id: "incerteza_e_limite", descricao: "declara incerteza, lacunas e limites da conclusao", termos: [/incerteza|lacuna|limite/i, /conclusao|afirmacao/i], obrigatorio: true },
|
|
4153
|
+
{ id: "reprodutibilidade", descricao: "preserva protocolo, dados e reproducibilidade", termos: [/protocolo|dataset|dados/i, /reprodut|replic/i], obrigatorio: false },
|
|
4154
|
+
],
|
|
4155
|
+
};
|
|
4156
|
+
const REQUISITOS_PROFILE_COMUNS = [
|
|
4157
|
+
{
|
|
4158
|
+
id: "contrato_antes_de_acao",
|
|
4159
|
+
descricao: "exige criar, editar ou remover contrato antes de qualquer acao",
|
|
4160
|
+
termos: [/contrato/i, /criar|editar|remover/i, /antes de qualquer acao|antes da acao|antes de agir|contrato primeiro/i],
|
|
4161
|
+
obrigatorio: true,
|
|
4162
|
+
},
|
|
4163
|
+
];
|
|
4164
|
+
const ALIASES_PROFILE = {
|
|
4165
|
+
software: "software",
|
|
4166
|
+
codigo: "software",
|
|
4167
|
+
code: "software",
|
|
4168
|
+
workflow: "workflow",
|
|
4169
|
+
workflow_ops: "workflow",
|
|
4170
|
+
workflows: "workflow",
|
|
4171
|
+
n8n: "workflow",
|
|
4172
|
+
automacao: "workflow",
|
|
4173
|
+
orquestracao: "workflow",
|
|
4174
|
+
ops: "ops",
|
|
4175
|
+
operacao: "ops",
|
|
4176
|
+
operacional: "ops",
|
|
4177
|
+
devops: "ops",
|
|
4178
|
+
game: "game",
|
|
4179
|
+
jogo: "game",
|
|
4180
|
+
games: "game",
|
|
4181
|
+
legal: "legal",
|
|
4182
|
+
juridico: "legal",
|
|
4183
|
+
research: "research",
|
|
4184
|
+
pesquisa: "research",
|
|
4185
|
+
};
|
|
4186
|
+
function normalizarProfileSemantico(valor) {
|
|
4187
|
+
if (!valor) {
|
|
4188
|
+
return null;
|
|
4189
|
+
}
|
|
4190
|
+
const chave = valor.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
4191
|
+
return ALIASES_PROFILE[chave] ?? null;
|
|
4192
|
+
}
|
|
4193
|
+
function normalizarMaturidadeProfile(valor) {
|
|
4194
|
+
const chave = (valor ?? "production").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
4195
|
+
if (chave === "draft" || chave === "rascunho")
|
|
4196
|
+
return "draft";
|
|
4197
|
+
if (chave === "prototype" || chave === "prototipo")
|
|
4198
|
+
return "prototype";
|
|
4199
|
+
if (chave === "critical" || chave === "critico" || chave === "critica")
|
|
4200
|
+
return "critical";
|
|
4201
|
+
return "production";
|
|
4202
|
+
}
|
|
4203
|
+
const PRESETS_PROFILE = {
|
|
4204
|
+
software: ["api", "modulo", "refactor", "persistencia", "security"],
|
|
4205
|
+
workflow: ["webhook", "fila", "n8n", "cron", "integracao"],
|
|
4206
|
+
ops: ["deploy", "migration", "incidente", "rollback", "critical"],
|
|
4207
|
+
legal: ["lgpd", "contrato", "dpa", "termos_uso", "privacidade", "due_diligence", "compliance"],
|
|
4208
|
+
research: ["rapida", "tecnica", "decisoria", "critica"],
|
|
4209
|
+
game: ["casual", "arcade", "rpg", "economia", "playtest"],
|
|
4210
|
+
};
|
|
4211
|
+
const REQUISITOS_PRESET_PROFILE = {
|
|
4212
|
+
security: [
|
|
4213
|
+
{ id: "security_entrada_saida", descricao: "declara validacao de entrada e saida segura", termos: [/entrada|input/i, /saida|output|retorno/i, /segur|sanitiz|valid/i], obrigatorio: true, severidade: "blocking" },
|
|
4214
|
+
],
|
|
4215
|
+
webhook: [
|
|
4216
|
+
{ id: "webhook_idempotencia", descricao: "webhook declara idempotencia e retry seguro", termos: [/webhook/i, /idempot/i, /retry|retent/i], obrigatorio: true, severidade: "blocking" },
|
|
4217
|
+
],
|
|
4218
|
+
fila: [
|
|
4219
|
+
{ id: "fila_dlq", descricao: "fila declara erro, retentativa e fila morta", termos: [/fila|queue/i, /erro|falha/i, /dlq|dead.?letter|fila morta/i], obrigatorio: true, severidade: "blocking" },
|
|
4220
|
+
],
|
|
4221
|
+
cron: [
|
|
4222
|
+
{ id: "cron_janela_execucao", descricao: "cron declara janela, concorrencia e reprocessamento", termos: [/cron|agenda/i, /janela|concorr/i, /reprocess|idempot/i], obrigatorio: true, severidade: "blocking" },
|
|
4223
|
+
],
|
|
4224
|
+
deploy: [
|
|
4225
|
+
{ id: "deploy_healthcheck", descricao: "deploy declara healthcheck, rollback e validacao live", termos: [/deploy/i, /healthcheck|healthz/i, /rollback/i], obrigatorio: true, severidade: "blocking" },
|
|
4226
|
+
],
|
|
4227
|
+
migration: [
|
|
4228
|
+
{ id: "migration_reversivel", descricao: "migration declara reversibilidade, backup e criterio de rollback", termos: [/migration|migracao/i, /revers|rollback/i, /backup|snapshot/i], obrigatorio: true, severidade: "critical" },
|
|
4229
|
+
],
|
|
4230
|
+
incidente: [
|
|
4231
|
+
{ id: "incidente_severidade_comunicacao", descricao: "incidente declara severidade, comunicacao e dono", termos: [/incidente|sev/i, /comunic/i, /responsavel|dono/i], obrigatorio: true, severidade: "critical" },
|
|
4232
|
+
],
|
|
4233
|
+
rollback: [
|
|
4234
|
+
{ id: "rollback_criterio_recuperacao", descricao: "rollback declara criterio objetivo de recuperacao", termos: [/rollback/i, /criterio|gatilho/i, /recuper/i], obrigatorio: true, severidade: "blocking" },
|
|
4235
|
+
],
|
|
4236
|
+
lgpd: [
|
|
4237
|
+
{ id: "lgpd_matriz_dados", descricao: "LGPD declara dado, finalidade, base legal e retencao", termos: [/dado|titular/i, /finalidade/i, /base legal/i, /retencao|retenção/i], obrigatorio: true, severidade: "critical" },
|
|
4238
|
+
],
|
|
4239
|
+
contrato: [
|
|
4240
|
+
{ id: "contrato_clausulas_nucleo", descricao: "contrato declara partes, objeto, obrigacoes e rescisao", termos: [/partes/i, /objeto/i, /obrigac|obrigaç/i, /rescis/i], obrigatorio: true, severidade: "blocking" },
|
|
4241
|
+
],
|
|
4242
|
+
dpa: [
|
|
4243
|
+
{ id: "dpa_operador_controlador", descricao: "DPA declara controlador, operador, dados e subprocessadores", termos: [/controlador/i, /operador/i, /subprocess/i, /dados/i], obrigatorio: true, severidade: "critical" },
|
|
4244
|
+
],
|
|
4245
|
+
termos_uso: [
|
|
4246
|
+
{ id: "termos_uso_limites", descricao: "termos de uso declaram uso permitido, proibicoes e limitacao", termos: [/uso permitido/i, /proibid|vedad/i, /limitacao|limitação/i], obrigatorio: true, severidade: "blocking" },
|
|
4247
|
+
],
|
|
4248
|
+
privacidade: [
|
|
4249
|
+
{ id: "privacidade_dados_direitos", descricao: "privacidade declara dados, finalidade, direitos e contato", termos: [/dados/i, /finalidade/i, /direitos/i, /contato/i], obrigatorio: true, severidade: "blocking" },
|
|
4250
|
+
],
|
|
4251
|
+
due_diligence: [
|
|
4252
|
+
{ id: "due_diligence_risco_evidencia", descricao: "due diligence declara risco, evidencia e pendencias", termos: [/risco/i, /evidencia|evidência/i, /pendenc|pendênc/i], obrigatorio: true, severidade: "blocking" },
|
|
4253
|
+
],
|
|
4254
|
+
compliance: [
|
|
4255
|
+
{ id: "compliance_controle_auditoria", descricao: "compliance declara controles, auditoria e responsavel", termos: [/controle/i, /auditoria/i, /responsavel|responsável/i], obrigatorio: true, severidade: "blocking" },
|
|
4256
|
+
],
|
|
4257
|
+
decisoria: [
|
|
4258
|
+
{ id: "research_matriz_decisao", descricao: "pesquisa decisoria declara criterio, peso e alternativa", termos: [/criterio|critério/i, /peso/i, /alternativa/i], obrigatorio: true, severidade: "blocking" },
|
|
4259
|
+
],
|
|
4260
|
+
critica: [
|
|
4261
|
+
{ id: "research_contraditorio", descricao: "pesquisa critica tenta derrubar a propria conclusao", termos: [/contradit|contra.?evidencia|contra.?evidência/i, /hipotese|hipótese|conclusao|conclusão/i], obrigatorio: true, severidade: "blocking" },
|
|
4262
|
+
],
|
|
4263
|
+
playtest: [
|
|
4264
|
+
{ id: "game_playtest_temporal", descricao: "playtest declara primeiros segundos, primeiro minuto e tensao", termos: [/primeiros? 10 segundos|10s/i, /1 minuto|um minuto/i, /tensao|tensão/i], obrigatorio: true, severidade: "blocking" },
|
|
4265
|
+
],
|
|
4266
|
+
economia: [
|
|
4267
|
+
{ id: "game_economia_balanceada", descricao: "economia declara moeda, fonte, gasto e exploit", termos: [/moeda|economia/i, /fonte/i, /gasto|sink/i, /exploit/i], obrigatorio: true, severidade: "blocking" },
|
|
4268
|
+
],
|
|
4269
|
+
};
|
|
4270
|
+
function normalizarPresetProfile(profile, valor) {
|
|
4271
|
+
if (!valor)
|
|
4272
|
+
return null;
|
|
4273
|
+
const chave = valor.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
4274
|
+
const alias = {
|
|
4275
|
+
privacidade: "privacidade",
|
|
4276
|
+
privacy: "privacidade",
|
|
4277
|
+
termos: "termos_uso",
|
|
4278
|
+
termos_de_uso: "termos_uso",
|
|
4279
|
+
due: "due_diligence",
|
|
4280
|
+
diligence: "due_diligence",
|
|
4281
|
+
pesquisa_rapida: "rapida",
|
|
4282
|
+
pesquisa_tecnica: "tecnica",
|
|
4283
|
+
pesquisa_decisoria: "decisoria",
|
|
4284
|
+
pesquisa_critica: "critica",
|
|
4285
|
+
critico: "critical",
|
|
4286
|
+
critica_ops: "critical",
|
|
4287
|
+
modulo_codigo: "modulo",
|
|
4288
|
+
seguranca: "security",
|
|
4289
|
+
seguranca_codigo: "security",
|
|
4290
|
+
};
|
|
4291
|
+
const preset = alias[chave] ?? chave;
|
|
4292
|
+
return PRESETS_PROFILE[profile].includes(preset) ? preset : null;
|
|
4293
|
+
}
|
|
4294
|
+
function moduloCombinaComProfile(modulo, caminho, profile) {
|
|
4295
|
+
const alvo = `${modulo ?? ""} ${caminho}`.toLowerCase();
|
|
4296
|
+
if (profile === "workflow") {
|
|
4297
|
+
return /workflow|n8n|orquestracao/.test(alvo);
|
|
4298
|
+
}
|
|
4299
|
+
return alvo.includes(profile);
|
|
4300
|
+
}
|
|
4301
|
+
function severidadeRequisitoProfile(requisito, profile, maturidade) {
|
|
4302
|
+
if (requisito.severidade)
|
|
4303
|
+
return requisito.severidade;
|
|
4304
|
+
if (!requisito.obrigatorio)
|
|
4305
|
+
return "warning";
|
|
4306
|
+
if (maturidade === "critical")
|
|
4307
|
+
return "critical";
|
|
4308
|
+
if ((profile === "legal" && /revisao|jurisdicao|fontes|auditoria/i.test(requisito.id)) ||
|
|
4309
|
+
(profile === "ops" && /runbook|rollback|segredos|incidente/i.test(requisito.id))) {
|
|
4310
|
+
return "critical";
|
|
4311
|
+
}
|
|
4312
|
+
return maturidade === "draft" ? "warning" : "blocking";
|
|
4313
|
+
}
|
|
4314
|
+
function avaliarRequisitosProfile(conteudo, requisitos, profile, maturidade, fonte = "contrato") {
|
|
4315
|
+
return requisitos.map((requisito) => ({
|
|
4316
|
+
id: requisito.id,
|
|
4317
|
+
descricao: requisito.descricao,
|
|
4318
|
+
obrigatorio: requisito.obrigatorio,
|
|
4319
|
+
atendido: requisito.termos.every((termo) => termo.test(conteudo)),
|
|
4320
|
+
severidade: severidadeRequisitoProfile(requisito, profile, maturidade),
|
|
4321
|
+
termos: requisito.termos.map((termo) => termo.source),
|
|
4322
|
+
fonte,
|
|
4323
|
+
}));
|
|
4324
|
+
}
|
|
4325
|
+
function decidirAcaoAgenteProfile(contratoValido, achados) {
|
|
4326
|
+
if (!contratoValido)
|
|
4327
|
+
return "parar";
|
|
4328
|
+
const pendentes = achados.filter((achado) => !achado.atendido);
|
|
4329
|
+
if (pendentes.some((achado) => achado.severidade === "critical"))
|
|
4330
|
+
return "chamar_humano";
|
|
4331
|
+
if (pendentes.some((achado) => achado.severidade === "blocking"))
|
|
4332
|
+
return "parar";
|
|
4333
|
+
if (pendentes.some((achado) => achado.severidade === "warning"))
|
|
4334
|
+
return "continuar_com_ressalva";
|
|
4335
|
+
return "continuar";
|
|
4336
|
+
}
|
|
4337
|
+
function calcularScoreProfile(contratoValido, achados) {
|
|
4338
|
+
const pendentesObrigatorios = achados.filter((achado) => achado.obrigatorio && !achado.atendido).length;
|
|
4339
|
+
const pendentesRecomendados = achados.filter((achado) => !achado.obrigatorio && !achado.atendido).length;
|
|
4340
|
+
return Math.max(0, 100 - (contratoValido ? 0 : 40) - pendentesObrigatorios * 20 - pendentesRecomendados * 8);
|
|
4341
|
+
}
|
|
4342
|
+
function penalidadeAchadoProfile(achado) {
|
|
4343
|
+
if (achado.atendido)
|
|
4344
|
+
return 0;
|
|
4345
|
+
if (achado.severidade === "critical")
|
|
4346
|
+
return 40;
|
|
4347
|
+
if (achado.severidade === "blocking")
|
|
4348
|
+
return 25;
|
|
4349
|
+
if (achado.severidade === "warning")
|
|
4350
|
+
return 10;
|
|
4351
|
+
return 0;
|
|
4352
|
+
}
|
|
4353
|
+
function calcularScoreAchadosProfile(base, achados) {
|
|
4354
|
+
return Math.max(0, base - achados.reduce((total, achado) => total + penalidadeAchadoProfile(achado), 0));
|
|
4355
|
+
}
|
|
4356
|
+
function contratoProibeTermoProfile(contrato, termo) {
|
|
4357
|
+
const escaped = termo.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4358
|
+
const padroes = [
|
|
4359
|
+
new RegExp(`(forbidden|proibid|vedad|nao usar|não usar|sem)\\s+[^\\n]{0,80}${escaped}`, "i"),
|
|
4360
|
+
new RegExp(`${escaped}[^\\n]{0,80}(forbidden|proibid|vedad|nao permitido|não permitido|nao usar|não usar)`, "i"),
|
|
4361
|
+
];
|
|
4362
|
+
return padroes.some((padrao) => padrao.test(contrato));
|
|
4363
|
+
}
|
|
4364
|
+
function criarAchadoArtefatoProfile(id, descricao, atendido, severidade, trecho, sugestao, motivo) {
|
|
4365
|
+
return {
|
|
4366
|
+
id,
|
|
4367
|
+
descricao,
|
|
4368
|
+
obrigatorio: severidade === "blocking" || severidade === "critical",
|
|
4369
|
+
atendido,
|
|
4370
|
+
severidade,
|
|
4371
|
+
termos: [],
|
|
4372
|
+
fonte: "artefato",
|
|
4373
|
+
trecho,
|
|
4374
|
+
motivo,
|
|
4375
|
+
sugestao,
|
|
4376
|
+
};
|
|
4377
|
+
}
|
|
4378
|
+
function trechoRegexProfile(texto, regex) {
|
|
4379
|
+
const match = regex.exec(texto);
|
|
4380
|
+
if (!match || match.index < 0)
|
|
4381
|
+
return undefined;
|
|
4382
|
+
const inicio = Math.max(0, match.index - 60);
|
|
4383
|
+
const fim = Math.min(texto.length, match.index + match[0].length + 60);
|
|
4384
|
+
return texto.slice(inicio, fim).replace(/\s+/g, " ").trim();
|
|
4385
|
+
}
|
|
4386
|
+
function contemArtefatoProfile(texto, regex) {
|
|
4387
|
+
regex.lastIndex = 0;
|
|
4388
|
+
return regex.test(texto);
|
|
4389
|
+
}
|
|
4390
|
+
function regexGlobalProfile(regex) {
|
|
4391
|
+
const flags = regex.flags.includes("g") ? regex.flags : `${regex.flags}g`;
|
|
4392
|
+
return new RegExp(regex.source, flags);
|
|
4393
|
+
}
|
|
4394
|
+
function trechoDoMatchProfile(texto, indice, tamanho) {
|
|
4395
|
+
const inicio = Math.max(0, indice - 60);
|
|
4396
|
+
const fim = Math.min(texto.length, indice + tamanho + 60);
|
|
4397
|
+
return texto.slice(inicio, fim).replace(/\s+/g, " ").trim();
|
|
4398
|
+
}
|
|
4399
|
+
function termoNegadoNoArtefato(texto, indice, tamanho) {
|
|
4400
|
+
const antes = texto.slice(Math.max(0, indice - 90), indice);
|
|
4401
|
+
const depois = texto.slice(indice + tamanho, Math.min(texto.length, indice + tamanho + 90));
|
|
4402
|
+
const negacaoAntes = /\b(?:sem|ausente|ausencia|ausência|faltando|falta|faltam|inexistente|indefinid[ao]s?|pendente|nao\s+(?:ha|há|define|declara|especifica|possui|tem|existe|inclui|contem|contém|configura|implementa|mapeia|separa|informa|descreve|testa|valida)|não\s+(?:ha|há|define|declara|especifica|possui|tem|existe|inclui|contem|contém|configura|implementa|mapeia|separa|informa|descreve|testa|valida))\b[\s\S]{0,80}$/i;
|
|
4403
|
+
const negacaoDepois = /^[\s\S]{0,80}\b(?:ausente|ausentes|ausencia|ausência|inexistente|indefinid[ao]s?|faltando|pendente|nao\s+(?:definid[ao]|declarad[ao]|especificad[ao]|testad[ao]|valid[ao]|implementad[ao]|configurad[ao]|mapead[ao])|não\s+(?:definid[ao]|declarad[ao]|especificad[ao]|testad[ao]|valid[ao]|implementad[ao]|configurad[ao]|mapead[ao]))\b/i;
|
|
4404
|
+
return negacaoAntes.test(antes) || negacaoDepois.test(depois);
|
|
4405
|
+
}
|
|
4406
|
+
function avaliarPresencaPositivaArtefato(texto, regex) {
|
|
4407
|
+
const busca = regexGlobalProfile(regex);
|
|
4408
|
+
let primeiroNegado = null;
|
|
4409
|
+
let match;
|
|
4410
|
+
let iteracoes = 0;
|
|
4411
|
+
while ((match = busca.exec(texto)) && iteracoes < 80) {
|
|
4412
|
+
iteracoes += 1;
|
|
4413
|
+
if (match[0].length === 0)
|
|
4414
|
+
busca.lastIndex += 1;
|
|
4415
|
+
if (!termoNegadoNoArtefato(texto, match.index, match[0].length)) {
|
|
4416
|
+
return { atendido: true };
|
|
4417
|
+
}
|
|
4418
|
+
primeiroNegado ??= { indice: match.index, tamanho: match[0].length };
|
|
4419
|
+
}
|
|
4420
|
+
if (primeiroNegado) {
|
|
4421
|
+
return {
|
|
4422
|
+
atendido: false,
|
|
4423
|
+
trecho: trechoDoMatchProfile(texto, primeiroNegado.indice, primeiroNegado.tamanho),
|
|
4424
|
+
motivo: "termo encontrado em contexto de negacao/ausencia; mencao negativa nao satisfaz requisito.",
|
|
4425
|
+
};
|
|
4426
|
+
}
|
|
4427
|
+
return { atendido: false };
|
|
4428
|
+
}
|
|
4429
|
+
function validarTermosObrigatoriosArtefato(texto, checks) {
|
|
4430
|
+
return checks.map((check) => {
|
|
4431
|
+
const presenca = avaliarPresencaPositivaArtefato(texto, check.regex);
|
|
4432
|
+
return criarAchadoArtefatoProfile(check.id, check.descricao, presenca.atendido, check.severidade ?? "blocking", presenca.trecho, check.sugestao, presenca.motivo);
|
|
4433
|
+
});
|
|
4434
|
+
}
|
|
4435
|
+
function avaliarArtefatoSoftwareProfile(contrato, artefato, maturidade, preset) {
|
|
4436
|
+
const regras = [
|
|
4437
|
+
{ id: "software_eval_proibido", termo: "eval", regex: /\beval\s*\(/i, descricao: "codigo usa eval, risco basico de execucao dinamica", sugestao: "troque por parser explicito, tabela de operacoes ou validacao estruturada." },
|
|
4438
|
+
{ id: "software_function_proibido", termo: "Function", regex: /\bnew\s+Function\b|\bFunction\s*\(/i, descricao: "codigo usa Function dinamico, risco basico de execucao dinamica", sugestao: "remova geracao dinamica de codigo e modele comandos permitidos." },
|
|
4439
|
+
{ id: "software_innerhtml_inseguro", termo: "innerHTML", regex: /\.innerHTML\s*=|\bdangerouslySetInnerHTML\b/i, descricao: "codigo escreve HTML diretamente em superficie insegura", sugestao: "use textContent, template seguro ou sanitizacao comprovada." },
|
|
4440
|
+
{ id: "software_sql_concatenado", termo: "SQL", regex: /\b(SELECT|INSERT|UPDATE|DELETE)\b[\s\S]{0,220}(\+|\$\{)/i, descricao: "codigo aparenta concatenar SQL dinamico", sugestao: "use query parametrizada ou builder seguro." },
|
|
4441
|
+
{ id: "software_segredo_hardcoded", termo: "secret", regex: /\b(api[_-]?key|secret|token|password|senha|credential)\b\s*[:=]\s*["'`][^"'`\s]{8,}["'`]/i, descricao: "codigo aparenta carregar segredo fixo no artefato", sugestao: "mova segredo para variavel de ambiente/cofre e use redacao em exemplos." },
|
|
4442
|
+
{ id: "software_tls_desativado", termo: "TLS", regex: /rejectUnauthorized\s*:\s*false|NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*["']?0/i, descricao: "codigo aparenta desativar validacao TLS/certificado", sugestao: "corrija cadeia de certificado ou use ambiente de teste isolado sem desligar TLS em producao." },
|
|
4443
|
+
{ id: "software_shell_input_dinamico", termo: "shell", regex: /\b(exec|execSync|spawn|spawnSync)\s*\([\s\S]{0,160}(req\.|request\.|params|query|body|input|usuario|userInput)/i, descricao: "codigo aparenta executar shell com entrada dinamica", sugestao: "modele allowlist de comandos/argumentos e evite interpolar entrada do usuario." },
|
|
4444
|
+
];
|
|
4445
|
+
return regras.flatMap((regra) => {
|
|
4446
|
+
const achou = contemArtefatoProfile(artefato, regra.regex);
|
|
4447
|
+
if (!achou)
|
|
4448
|
+
return [];
|
|
4449
|
+
const politicaEstrita = contratoProibeTermoProfile(contrato, regra.termo) || maturidade === "critical" || preset === "security";
|
|
4450
|
+
const severidade = maturidade === "critical"
|
|
4451
|
+
? "critical"
|
|
4452
|
+
: politicaEstrita
|
|
4453
|
+
? "blocking"
|
|
4454
|
+
: "warning";
|
|
4455
|
+
return [criarAchadoArtefatoProfile(regra.id, regra.descricao, false, severidade, trechoRegexProfile(artefato, regra.regex), regra.sugestao)];
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4458
|
+
function avaliarArtefatoWorkflowProfile(contrato, artefato, preset) {
|
|
4459
|
+
const achados = validarTermosObrigatoriosArtefato(artefato, [
|
|
4460
|
+
{ id: "workflow_idempotencia_artefato", descricao: "workflow real declara idempotencia", regex: /idempot/i, sugestao: "adicione chave idempotente por evento/webhook ou etapa." },
|
|
4461
|
+
{ id: "workflow_retry_artefato", descricao: "workflow real declara retry/retentativa", regex: /retry|retent/i, sugestao: "declare politica de retentativa, limites e erro final." },
|
|
4462
|
+
{ id: "workflow_compensacao_artefato", descricao: "workflow real declara compensacao/fallback", regex: /compens|fallback|revers/i, sugestao: "declare compensacao para efeitos ja aplicados." },
|
|
4463
|
+
]);
|
|
4464
|
+
const contratoExigePagamentoAntes = /validar pagamento[\s\S]{0,120}antes[\s\S]{0,120}criar pedido/i.test(contrato);
|
|
4465
|
+
const criaPedido = artefato.search(/criar pedido/i);
|
|
4466
|
+
const validaPagamento = artefato.search(/validar pagamento/i);
|
|
4467
|
+
if (contratoExigePagamentoAntes && criaPedido >= 0 && validaPagamento >= 0 && criaPedido < validaPagamento) {
|
|
4468
|
+
achados.push(criarAchadoArtefatoProfile("workflow_ordem_pagamento_invertida", "workflow cria pedido antes de validar pagamento, contrariando ordem contratada", false, "critical", trechoRegexProfile(artefato, /criar pedido[\s\S]{0,160}validar pagamento/i), "reordene a etapa de validacao antes da criacao persistente ou declare reserva reversivel."));
|
|
4469
|
+
}
|
|
4470
|
+
if (preset === "webhook" && !avaliarPresencaPositivaArtefato(artefato, /webhook/i).atendido) {
|
|
4471
|
+
achados.push(criarAchadoArtefatoProfile("workflow_preset_webhook_sem_superficie", "preset webhook exige superficie webhook no artefato", false, "blocking", undefined, "nomeie endpoint/evento, payload e idempotencia."));
|
|
4472
|
+
}
|
|
4473
|
+
return achados;
|
|
4474
|
+
}
|
|
4475
|
+
function avaliarArtefatoOpsProfile(artefato, preset) {
|
|
4476
|
+
const checks = [
|
|
4477
|
+
{ id: "ops_runbook_artefato", descricao: "plano operacional contem runbook", regex: /runbook|passo a passo|procedimento/i, sugestao: "adicione runbook executavel com passos, dono e criterio de parada." },
|
|
4478
|
+
{ id: "ops_rollback_artefato", descricao: "plano operacional contem rollback", regex: /rollback|reverter|revers/i, sugestao: "adicione comando/criterio de rollback." },
|
|
4479
|
+
{ id: "ops_healthcheck_artefato", descricao: "plano operacional contem healthcheck", regex: /healthcheck|healthz|smoke|verificacao live|verificação live/i, sugestao: "declare endpoint, comando ou metrica que prova recuperacao." },
|
|
4480
|
+
{ id: "ops_responsavel_artefato", descricao: "plano operacional declara responsavel/oncall", regex: /responsavel|responsável|owner|oncall|dono/i, sugestao: "declare responsavel operacional, oncall ou dono da mudanca." },
|
|
4481
|
+
{ id: "ops_comunicacao_artefato", descricao: "plano operacional contem comunicacao", regex: /comunic|avisar|status page|cliente/i, severidade: "warning", sugestao: "adicione quando e quem comunicar." },
|
|
4482
|
+
];
|
|
4483
|
+
const achados = validarTermosObrigatoriosArtefato(artefato, checks);
|
|
4484
|
+
if ((preset === "migration" || preset === "critical") && !avaliarPresencaPositivaArtefato(artefato, /backup|snapshot|revers/i).atendido) {
|
|
4485
|
+
achados.push(criarAchadoArtefatoProfile("ops_migration_sem_reversibilidade", "migration/critical exige reversibilidade comprovada", false, "critical", undefined, "declare backup, snapshot, down migration ou estrategia manual testada."));
|
|
4486
|
+
}
|
|
4487
|
+
if (preset === "rollback" && !avaliarPresencaPositivaArtefato(artefato, /criterio|critério|gatilho|recuper/i).atendido) {
|
|
4488
|
+
achados.push(criarAchadoArtefatoProfile("ops_rollback_sem_criterio_recuperacao", "rollback exige criterio objetivo de recuperacao", false, "blocking", undefined, "declare gatilho de rollback e criterio verificavel de recuperacao."));
|
|
4489
|
+
}
|
|
4490
|
+
return achados;
|
|
4491
|
+
}
|
|
4492
|
+
function avaliarArtefatoLegalProfile(artefato, preset) {
|
|
4493
|
+
const achados = [];
|
|
4494
|
+
const pareceFinal = /parecer final|opiniao definitiva|opinião definitiva|conclusao juridica definitiva|conclusão jurídica definitiva/i.test(artefato);
|
|
4495
|
+
const temRevisaoHumana = avaliarPresencaPositivaArtefato(artefato, /revisao humana|revisão humana|advogado|minuta|rascunho|preliminar/i).atendido;
|
|
4496
|
+
const temAvisoNaoParecer = /nao e parecer|não é parecer/i.test(artefato);
|
|
4497
|
+
const temFreioHumano = temRevisaoHumana || temAvisoNaoParecer;
|
|
4498
|
+
if (pareceFinal && !temFreioHumano) {
|
|
4499
|
+
achados.push(criarAchadoArtefatoProfile("legal_parecer_final_sem_revisao", "documento legal parece parecer final sem revisao humana obrigatoria", false, "critical", trechoRegexProfile(artefato, /parecer final|opiniao definitiva|opinião definitiva/i), "marque como minuta/preliminar e exija revisao humana por profissional habilitado."));
|
|
4500
|
+
}
|
|
4501
|
+
if (preset === "lgpd") {
|
|
4502
|
+
achados.push(...validarTermosObrigatoriosArtefato(artefato, [
|
|
4503
|
+
{ id: "legal_lgpd_base_finalidade", descricao: "artefato LGPD declara base legal e finalidade", regex: /base legal[\s\S]{0,160}finalidade|finalidade[\s\S]{0,160}base legal/i, severidade: "critical", sugestao: "inclua matriz dado -> finalidade -> base legal." },
|
|
4504
|
+
{ id: "legal_lgpd_retencao_direitos", descricao: "artefato LGPD declara retencao e direitos do titular", regex: /retencao|retenção/i, severidade: "blocking", sugestao: "inclua retencao, descarte e direitos do titular." },
|
|
4505
|
+
]));
|
|
4506
|
+
}
|
|
4507
|
+
if (preset === "privacidade") {
|
|
4508
|
+
achados.push(...validarTermosObrigatoriosArtefato(artefato, [
|
|
4509
|
+
{ id: "legal_privacidade_dados_finalidade", descricao: "politica de privacidade declara dados tratados e finalidade", regex: /dados?[\s\S]{0,120}finalidade|finalidade[\s\S]{0,120}dados?/i, severidade: "critical", sugestao: "inclua dados coletados/tratados e finalidade de cada uso." },
|
|
4510
|
+
{ id: "legal_privacidade_base_legal", descricao: "politica de privacidade declara base legal", regex: /base legal|consentimento|legitimo interesse|legítimo interesse|execucao de contrato|execução de contrato/i, severidade: "critical", sugestao: "declare base legal por finalidade." },
|
|
4511
|
+
{ id: "legal_privacidade_retencao", descricao: "politica de privacidade declara retencao/descarte", regex: /retencao|retenção|descarte|prazo de guarda/i, severidade: "blocking", sugestao: "declare prazo de retencao e descarte." },
|
|
4512
|
+
{ id: "legal_privacidade_direitos_contato", descricao: "politica de privacidade declara direitos do titular e contato/DPO", regex: /direitos? do titular[\s\S]{0,160}(contato|dpo|encarregado)|(?:contato|dpo|encarregado)[\s\S]{0,160}direitos? do titular/i, severidade: "blocking", sugestao: "declare direitos do titular e canal de contato/encarregado." },
|
|
4513
|
+
]));
|
|
4514
|
+
}
|
|
4515
|
+
return achados;
|
|
4516
|
+
}
|
|
4517
|
+
function avaliarArtefatoResearchProfile(artefato, preset) {
|
|
4518
|
+
const achados = validarTermosObrigatoriosArtefato(artefato, [
|
|
4519
|
+
{ id: "research_fontes_artefato", descricao: "resposta final cita fontes ou referencias", regex: /https?:\/\/|\[[0-9]+\]|fonte|referencia|referência/i, sugestao: "inclua fontes citaveis e separe evidencia de opiniao." },
|
|
4520
|
+
{ id: "research_incerteza_artefato", descricao: "resposta final declara incerteza ou limite", regex: /incerteza|limite|baixa confianca|baixa confiança|lacuna/i, sugestao: "declare limites, incertezas e o que nao foi verificado." },
|
|
4521
|
+
{ id: "research_fato_inferencia_artefato", descricao: "resposta separa fato de inferencia", regex: /fato|inferencia|inferência/i, sugestao: "rotule conclusoes como fato, inferencia ou recomendacao." },
|
|
4522
|
+
]);
|
|
4523
|
+
if (preset === "critica" && !avaliarPresencaPositivaArtefato(artefato, /contra.?evidencia|contra.?evidência|evidencia contra|evidência contra|refut/i).atendido) {
|
|
4524
|
+
achados.push(criarAchadoArtefatoProfile("research_sem_contraditorio", "pesquisa critica nao tentou derrubar a conclusao", false, "blocking", undefined, "adicione secao de evidencias contra a recomendacao principal."));
|
|
4525
|
+
}
|
|
4526
|
+
return achados;
|
|
4527
|
+
}
|
|
4528
|
+
function avaliarArtefatoGameProfile(artefato, preset) {
|
|
4529
|
+
const achados = validarTermosObrigatoriosArtefato(artefato, [
|
|
4530
|
+
{ id: "game_loop_artefato", descricao: "artefato de jogo declara core loop", regex: /core loop|loop|ciclo/i, sugestao: "declare acao principal, feedback, recompensa e reinicio." },
|
|
4531
|
+
{ id: "game_estado_artefato", descricao: "artefato de jogo declara estado e transicoes", regex: /estado|state|playing|game_over|transic|transiç/i, sugestao: "declare estados e transicoes permitidas." },
|
|
4532
|
+
{ id: "game_pacing_artefato", descricao: "artefato de jogo declara pacing/curva", regex: /pacing|curva|dificuldade|sessao|sessão/i, severidade: "warning", sugestao: "adicione ritmo esperado e variacao de dificuldade." },
|
|
4533
|
+
]);
|
|
4534
|
+
if (preset === "playtest" && !avaliarPresencaPositivaArtefato(artefato, /10 segundos|1 minuto|um minuto|primeiro minuto/i).atendido) {
|
|
4535
|
+
achados.push(criarAchadoArtefatoProfile("game_playtest_sem_tempo", "playtest nao descreve primeiros segundos e primeiro minuto", false, "blocking", undefined, "adicione simulacao dos primeiros 10s e do primeiro minuto."));
|
|
4536
|
+
}
|
|
4537
|
+
return achados;
|
|
4538
|
+
}
|
|
4539
|
+
function avaliarArtefatoProfile(profile, contrato, artefato, maturidade, preset) {
|
|
4540
|
+
if (!artefato || !artefato.trim())
|
|
4541
|
+
return [];
|
|
4542
|
+
switch (profile) {
|
|
4543
|
+
case "software":
|
|
4544
|
+
return avaliarArtefatoSoftwareProfile(contrato, artefato, maturidade, preset);
|
|
4545
|
+
case "workflow":
|
|
4546
|
+
return avaliarArtefatoWorkflowProfile(contrato, artefato, preset);
|
|
4547
|
+
case "ops":
|
|
4548
|
+
return avaliarArtefatoOpsProfile(artefato, preset);
|
|
4549
|
+
case "legal":
|
|
4550
|
+
return avaliarArtefatoLegalProfile(artefato, preset);
|
|
4551
|
+
case "research":
|
|
4552
|
+
return avaliarArtefatoResearchProfile(artefato, preset);
|
|
4553
|
+
case "game":
|
|
4554
|
+
return avaliarArtefatoGameProfile(artefato, preset);
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
async function carregarArtefatoProfile(args) {
|
|
4558
|
+
const texto = obterOpcao(args, "--artefato") ?? obterOpcao(args, "--artifact");
|
|
4559
|
+
const arquivo = obterOpcao(args, "--artefato-arquivo") ?? obterOpcao(args, "--artifact-file");
|
|
4560
|
+
if (arquivo) {
|
|
4561
|
+
const absoluto = path.resolve(process.cwd(), arquivo);
|
|
4562
|
+
return { texto: await readFile(absoluto, "utf8"), arquivo: absoluto };
|
|
4563
|
+
}
|
|
4564
|
+
return { texto: texto ?? null, arquivo: null };
|
|
4565
|
+
}
|
|
4566
|
+
async function validarProfileSemantico(entrada, profile, opcoes) {
|
|
4567
|
+
if (!entrada) {
|
|
4568
|
+
throw new Error("Uso: sema profile validar <software|workflow|ops|game|legal|research> <arquivo-ou-pasta> [--maturidade draft|prototype|production|critical] [--preset <preset>] [--artefato <texto>|--artefato-arquivo <arquivo>] [--json]");
|
|
4569
|
+
}
|
|
4570
|
+
const modulos = await carregarModulos(entrada);
|
|
4571
|
+
const escolhido = modulos.find((item) => moduloCombinaComProfile(item.resultado.modulo?.nome, item.caminho, profile)) ?? modulos[0];
|
|
4572
|
+
if (!escolhido) {
|
|
4573
|
+
throw new Error(`Nenhum modulo Sema encontrado para ${entrada}.`);
|
|
4574
|
+
}
|
|
4575
|
+
const conteudo = await readFile(escolhido.caminho, "utf8");
|
|
4576
|
+
const maturidade = opcoes.maturidade;
|
|
4577
|
+
const preset = opcoes.preset ?? null;
|
|
4578
|
+
const contratoValido = !temErros(escolhido.resultado.diagnosticos);
|
|
4579
|
+
const requisitosPreset = preset ? REQUISITOS_PRESET_PROFILE[preset] ?? [] : [];
|
|
4580
|
+
const achadosContrato = avaliarRequisitosProfile(conteudo, [...REQUISITOS_PROFILE_COMUNS, ...REQUISITOS_PROFILE[profile]], profile, maturidade);
|
|
4581
|
+
const achadosPreset = avaliarRequisitosProfile(conteudo, requisitosPreset, profile, maturidade, "preset");
|
|
4582
|
+
const achadosArtefato = avaliarArtefatoProfile(profile, conteudo, opcoes.artefatoTexto, maturidade, preset);
|
|
4583
|
+
const achados = [...achadosContrato, ...achadosPreset, ...achadosArtefato];
|
|
4584
|
+
const requisitosPendentes = achados.filter((achado) => !achado.atendido).map((achado) => achado.id);
|
|
4585
|
+
const obrigatoriosPendentes = achados.filter((achado) => achado.obrigatorio && !achado.atendido).map((achado) => achado.id);
|
|
4586
|
+
const scoreContrato = calcularScoreProfile(contratoValido, [...achadosContrato, ...achadosPreset]);
|
|
4587
|
+
const scoreArtefato = opcoes.artefatoTexto ? calcularScoreAchadosProfile(100, achadosArtefato) : null;
|
|
4588
|
+
const scoreProntoParaAcao = Math.min(scoreContrato, scoreArtefato ?? scoreContrato);
|
|
4589
|
+
const decisaoAgente = decidirAcaoAgenteProfile(contratoValido, achados);
|
|
4590
|
+
const aprovado = contratoValido && obrigatoriosPendentes.length === 0 && scoreProntoParaAcao >= 80;
|
|
4591
|
+
const podeContinuar = decisaoAgente === "continuar" || decisaoAgente === "continuar_com_ressalva";
|
|
4592
|
+
return {
|
|
4593
|
+
comando: "profile validar",
|
|
4594
|
+
sucesso: aprovado,
|
|
4595
|
+
profile,
|
|
4596
|
+
arquivo: escolhido.caminho,
|
|
4597
|
+
modulo: escolhido.resultado.modulo?.nome ?? null,
|
|
4598
|
+
maturidade,
|
|
4599
|
+
preset,
|
|
4600
|
+
presetsDisponiveis: PRESETS_PROFILE[profile],
|
|
4601
|
+
contratoValido,
|
|
4602
|
+
artefatoRecebido: Boolean(opcoes.artefatoTexto),
|
|
4603
|
+
artefatoArquivo: opcoes.artefatoArquivo ?? null,
|
|
4604
|
+
aprovado,
|
|
4605
|
+
bloqueado: !aprovado,
|
|
4606
|
+
podeContinuar,
|
|
4607
|
+
decisaoAgente,
|
|
4608
|
+
scoreContrato,
|
|
4609
|
+
scoreArtefato,
|
|
4610
|
+
scoreProntoParaAcao,
|
|
4611
|
+
achados,
|
|
4612
|
+
achadosArtefato,
|
|
4613
|
+
requisitosAtendidos: achados.filter((achado) => achado.atendido).map((achado) => achado.id),
|
|
4614
|
+
requisitosPendentes,
|
|
4615
|
+
checks: [
|
|
4616
|
+
`sema profile validar ${profile} <arquivo> --preset ${preset ?? "<opcional>"} --artefato-arquivo <artefato> --json`,
|
|
4617
|
+
"sema validar <arquivo> --json",
|
|
4618
|
+
"sema drift <arquivo> --escopo modulo --json",
|
|
4619
|
+
"sema contexto-ia <arquivo>",
|
|
4620
|
+
],
|
|
4621
|
+
diagnosticos: escolhido.resultado.diagnosticos,
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4624
|
+
function renderizarProfileValidarTexto(resultado) {
|
|
4625
|
+
const linhas = [
|
|
4626
|
+
"PROFILE_VALIDAR",
|
|
4627
|
+
`PROFILE: ${resultado.profile}`,
|
|
4628
|
+
`ARQUIVO: ${resultado.arquivo}`,
|
|
4629
|
+
`MODULO: ${resultado.modulo ?? "desconhecido"}`,
|
|
4630
|
+
`MATURIDADE: ${resultado.maturidade}`,
|
|
4631
|
+
`PRESET: ${resultado.preset ?? "nenhum"}`,
|
|
4632
|
+
`ARTEFATO_RECEBIDO: ${resultado.artefatoRecebido ? "sim" : "nao"}`,
|
|
4633
|
+
`APROVADO: ${resultado.aprovado ? "sim" : "nao"}`,
|
|
4634
|
+
`BLOQUEADO: ${resultado.bloqueado ? "sim" : "nao"}`,
|
|
4635
|
+
`DECISAO_AGENTE: ${resultado.decisaoAgente}`,
|
|
4636
|
+
`SCORE_CONTRATO: ${resultado.scoreContrato}`,
|
|
4637
|
+
`SCORE_ARTEFATO: ${resultado.scoreArtefato ?? "nao_avaliado"}`,
|
|
4638
|
+
`SCORE_PRONTO_PARA_ACAO: ${resultado.scoreProntoParaAcao}`,
|
|
4639
|
+
];
|
|
4640
|
+
for (const achado of resultado.achados) {
|
|
4641
|
+
linhas.push(`- ${achado.id}: ${achado.atendido ? "ok" : "pendente"} ${achado.severidade}${achado.obrigatorio ? " obrigatorio" : " recomendado"} ${achado.fonte} - ${achado.descricao}`);
|
|
4642
|
+
if (achado.trecho)
|
|
4643
|
+
linhas.push(` trecho: ${achado.trecho}`);
|
|
4644
|
+
if (achado.motivo)
|
|
4645
|
+
linhas.push(` motivo: ${achado.motivo}`);
|
|
4646
|
+
if (achado.sugestao)
|
|
4647
|
+
linhas.push(` sugestao: ${achado.sugestao}`);
|
|
4648
|
+
}
|
|
4649
|
+
return linhas.join("\n");
|
|
4650
|
+
}
|
|
4651
|
+
async function comandoProfile(posicionais, args, emJson) {
|
|
4652
|
+
const subcomando = posicionais[0];
|
|
4653
|
+
if (!subcomando || subcomando === "help" || subcomando === "ajuda") {
|
|
4654
|
+
console.log([
|
|
4655
|
+
"Uso: sema profile <validar>",
|
|
4656
|
+
"",
|
|
4657
|
+
"Comandos:",
|
|
4658
|
+
" sema profile validar <software|workflow|ops|game|legal|research> <arquivo-ou-pasta> [--maturidade draft|prototype|production|critical] [--preset <preset>] [--artefato <texto>|--artefato-arquivo <arquivo>] [--json]",
|
|
4659
|
+
"",
|
|
4660
|
+
"O profile e um gate semantico: se requisito obrigatorio faltar, a saida bloqueia.",
|
|
4661
|
+
].join("\n"));
|
|
4662
|
+
return subcomando ? 0 : 1;
|
|
4663
|
+
}
|
|
4664
|
+
if (subcomando !== "validar") {
|
|
4665
|
+
console.error(`Subcomando profile desconhecido: ${subcomando}`);
|
|
4666
|
+
return 1;
|
|
4667
|
+
}
|
|
4668
|
+
const profile = normalizarProfileSemantico(posicionais[1] ?? obterOpcao(args, "--profile"));
|
|
4669
|
+
const entrada = posicionais[2] ?? obterOpcao(args, "--arquivo");
|
|
4670
|
+
if (!profile) {
|
|
4671
|
+
console.error("Profile invalido. Use software, workflow, ops, game, legal ou research.");
|
|
4672
|
+
return 1;
|
|
4673
|
+
}
|
|
4674
|
+
const preset = normalizarPresetProfile(profile, obterOpcao(args, "--preset"));
|
|
4675
|
+
if (obterOpcao(args, "--preset") && !preset) {
|
|
4676
|
+
console.error(`Preset invalido para ${profile}. Use: ${PRESETS_PROFILE[profile].join(", ")}.`);
|
|
4677
|
+
return 1;
|
|
4678
|
+
}
|
|
4679
|
+
const artefato = await carregarArtefatoProfile(args);
|
|
4680
|
+
const resultado = await validarProfileSemantico(entrada, profile, {
|
|
4681
|
+
maturidade: normalizarMaturidadeProfile(obterOpcao(args, "--maturidade")),
|
|
4682
|
+
preset,
|
|
4683
|
+
artefatoTexto: artefato.texto,
|
|
4684
|
+
artefatoArquivo: artefato.arquivo,
|
|
4685
|
+
});
|
|
4686
|
+
if (emJson) {
|
|
4687
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
4688
|
+
}
|
|
4689
|
+
else {
|
|
4690
|
+
console.log(renderizarProfileValidarTexto(resultado));
|
|
4691
|
+
}
|
|
4692
|
+
return resultado.sucesso ? 0 : 1;
|
|
4693
|
+
}
|
|
4694
|
+
async function iniciarProfileAuthor(destinoOpcional, temaSensivel, cwd = process.cwd()) {
|
|
4695
|
+
const exemplos = await localizarDiretorioExemplosOficiais();
|
|
4696
|
+
const nomeTemplate = temaSensivel ? "author_tema_sensivel.sema" : "author_obra_comum.sema";
|
|
4697
|
+
const destino = path.resolve(cwd, destinoOpcional ?? path.join("contratos", "author.sema"));
|
|
4698
|
+
if (!exemplos) {
|
|
4699
|
+
return {
|
|
4700
|
+
comando: "author iniciar",
|
|
4701
|
+
sucesso: false,
|
|
4702
|
+
destino,
|
|
4703
|
+
template: null,
|
|
4704
|
+
temaSensivel,
|
|
4705
|
+
erro: "Diretorio de exemplos oficiais nao foi encontrado no pacote da CLI.",
|
|
4706
|
+
};
|
|
4707
|
+
}
|
|
4708
|
+
const template = path.join(exemplos, nomeTemplate);
|
|
4709
|
+
if (!(await caminhoExiste(template))) {
|
|
4710
|
+
return {
|
|
4711
|
+
comando: "author iniciar",
|
|
4712
|
+
sucesso: false,
|
|
4713
|
+
destino,
|
|
4714
|
+
template,
|
|
4715
|
+
temaSensivel,
|
|
4716
|
+
erro: `Template oficial ${nomeTemplate} nao foi encontrado.`,
|
|
4717
|
+
};
|
|
4718
|
+
}
|
|
4719
|
+
if (await caminhoExiste(destino)) {
|
|
4720
|
+
return {
|
|
4721
|
+
comando: "author iniciar",
|
|
4722
|
+
sucesso: false,
|
|
4723
|
+
destino,
|
|
4724
|
+
template,
|
|
4725
|
+
temaSensivel,
|
|
4726
|
+
erro: `Arquivo ja existe: ${destino}`,
|
|
4727
|
+
};
|
|
4728
|
+
}
|
|
4729
|
+
const moduloDestino = temaSensivel ? "author.tema_sensivel" : "author.obra_comum";
|
|
4730
|
+
const conteudo = (await readFile(template, "utf8"))
|
|
4731
|
+
.replace(/^module exemplos\.author\.(obra_comum|tema_sensivel) \{/m, `module ${moduloDestino} {`);
|
|
4732
|
+
await mkdir(path.dirname(destino), { recursive: true });
|
|
4733
|
+
await writeFile(destino, conteudo, "utf8");
|
|
4734
|
+
return {
|
|
4735
|
+
comando: "author iniciar",
|
|
4736
|
+
sucesso: true,
|
|
4737
|
+
destino,
|
|
4738
|
+
template,
|
|
4739
|
+
temaSensivel,
|
|
4740
|
+
};
|
|
4741
|
+
}
|
|
4742
|
+
async function validarProfileAuthor(entrada, emJson) {
|
|
4743
|
+
const modulos = await carregarModulos(entrada);
|
|
4744
|
+
const resultados = modulos.map((item) => ({
|
|
4745
|
+
caminho: item.caminho,
|
|
4746
|
+
modulo: item.resultado.modulo?.nome ?? null,
|
|
4747
|
+
sucesso: !temErros(item.resultado.diagnosticos),
|
|
4748
|
+
diagnosticos: item.resultado.diagnosticos,
|
|
4749
|
+
}));
|
|
4750
|
+
const payload = {
|
|
4751
|
+
comando: "author validar",
|
|
4752
|
+
sucesso: resultados.every((resultado) => resultado.sucesso),
|
|
4753
|
+
resultados,
|
|
4754
|
+
};
|
|
4755
|
+
if (emJson) {
|
|
4756
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
4757
|
+
return payload.sucesso ? 0 : 1;
|
|
4758
|
+
}
|
|
4759
|
+
console.log(formatarDiagnosticos(modulos.flatMap((item) => item.resultado.diagnosticos)));
|
|
4760
|
+
return payload.sucesso ? 0 : 1;
|
|
4761
|
+
}
|
|
4762
|
+
async function criarAuthorBriefing(entrada, preset = null) {
|
|
4763
|
+
if (!entrada) {
|
|
4764
|
+
throw new Error("Uso: sema author briefing <arquivo-ou-pasta> [--preset conto|romance|roteiro|lore|campanha] [--json]");
|
|
4765
|
+
}
|
|
4766
|
+
const modulos = await carregarModulos(entrada);
|
|
4767
|
+
const escolhido = modulos.find((item) => /author/i.test(item.resultado.modulo?.nome ?? item.caminho)) ?? modulos[0];
|
|
4768
|
+
if (!escolhido) {
|
|
4769
|
+
throw new Error(`Nenhum modulo Sema encontrado para ${entrada}.`);
|
|
4770
|
+
}
|
|
4771
|
+
const conteudo = await readFile(escolhido.caminho, "utf8");
|
|
4772
|
+
const ir = escolhido.resultado.ir ?? null;
|
|
4773
|
+
const tasks = ir?.tasks.map((task) => task.nome) ?? [];
|
|
4774
|
+
const flows = ir?.flows.map((flow) => flow.nome) ?? [];
|
|
4775
|
+
const guardrails = extrairGuardrailsAuthor(conteudo, ir);
|
|
4776
|
+
const proibicoes = extrairProibicoesAuthor(ir, conteudo);
|
|
4777
|
+
const coreDetectado = /AuthorCore|author core|declarar_author_core|core:/i.test(conteudo);
|
|
4778
|
+
const agentsDetectados = /AgentAuthor|AuthorAgent|agents_author|sensitivity_reviewer|revisor_cliche|guardiao/i.test(conteudo);
|
|
4779
|
+
const flowDetectado = flows.length > 0 || /flow\s+author/i.test(conteudo);
|
|
4780
|
+
return {
|
|
4781
|
+
comando: "author briefing",
|
|
4782
|
+
sucesso: !temErros(escolhido.resultado.diagnosticos),
|
|
4783
|
+
arquivo: escolhido.caminho,
|
|
4784
|
+
modulo: escolhido.resultado.modulo?.nome ?? null,
|
|
4785
|
+
profile: "author",
|
|
4786
|
+
preset,
|
|
4787
|
+
presetsDisponiveis: PRESETS_AUTHOR,
|
|
4788
|
+
coreDetectado,
|
|
4789
|
+
agentsDetectados,
|
|
4790
|
+
flowDetectado,
|
|
4791
|
+
tasks,
|
|
4792
|
+
flows,
|
|
4793
|
+
guardrails,
|
|
4794
|
+
proibicoes,
|
|
4795
|
+
checks: [
|
|
4796
|
+
...checksPresetAuthor(preset).map((check) => `preset ${preset}: ${check}`),
|
|
4797
|
+
"sema author validar <arquivo> --json",
|
|
4798
|
+
"sema author revisar-cliches <arquivo> --texto <texto> --json",
|
|
4799
|
+
"sema author validar-narrativa <arquivo> --texto <texto> --json",
|
|
4800
|
+
"sema author validar-proibicoes <arquivo> --texto <texto> --json",
|
|
4801
|
+
"sema validar <arquivo> --json",
|
|
4802
|
+
"sema drift <arquivo> --json",
|
|
4803
|
+
],
|
|
4804
|
+
diagnosticos: escolhido.resultado.diagnosticos,
|
|
4805
|
+
};
|
|
4806
|
+
}
|
|
4807
|
+
async function revisarClichesAuthor(entrada, textoOpcional, textoArquivoOpcional, textoAnteriorOpcional, textoAnteriorArquivoOpcional, acao = "revisar-cliches", preset = null) {
|
|
4808
|
+
if (!entrada) {
|
|
4809
|
+
throw new Error(`Uso: sema author ${acao} <arquivo.sema> [--texto <texto>|--texto-arquivo <arquivo>] [--texto-anterior <texto>|--texto-anterior-arquivo <arquivo>] [--json]`);
|
|
4810
|
+
}
|
|
4811
|
+
const modulos = await carregarModulos(entrada);
|
|
4812
|
+
const escolhido = modulos.find((item) => /author/i.test(item.resultado.modulo?.nome ?? item.caminho)) ?? modulos[0];
|
|
4813
|
+
if (!escolhido) {
|
|
4814
|
+
throw new Error(`Nenhum modulo Sema encontrado para ${entrada}.`);
|
|
4815
|
+
}
|
|
4816
|
+
const conteudoContrato = await readFile(escolhido.caminho, "utf8");
|
|
4817
|
+
const textoFonte = textoArquivoOpcional
|
|
4818
|
+
? await readFile(path.resolve(textoArquivoOpcional), "utf8")
|
|
4819
|
+
: textoOpcional ?? conteudoContrato;
|
|
4820
|
+
const textoAnterior = textoAnteriorArquivoOpcional
|
|
4821
|
+
? await readFile(path.resolve(textoAnteriorArquivoOpcional), "utf8")
|
|
4822
|
+
: textoAnteriorOpcional ?? null;
|
|
4823
|
+
const tokensContrato = extrairTokensContratoAuthor(conteudoContrato);
|
|
4824
|
+
const politicasAplicadasCatalogo = POLITICAS_AUTHOR_GATE
|
|
4825
|
+
.filter((politica) => politicaAuthorAtiva(politica, tokensContrato));
|
|
4826
|
+
const politicasDinamicas = politicasDinamicasContratoAuthor(tokensContrato);
|
|
4827
|
+
const politicasProibicoes = politicasProibicoesLiteraisAuthor(conteudoContrato, escolhido.resultado.ir ?? null);
|
|
4828
|
+
const achadosCatalogo = politicasAplicadasCatalogo
|
|
4829
|
+
.map((politica) => criarAchadoAuthor(politica, textoFonte, "catalogo"))
|
|
4830
|
+
.filter((achado) => achado !== null);
|
|
4831
|
+
const achadosDinamicos = politicasDinamicas
|
|
4832
|
+
.map((politica) => criarAchadoAuthor(politica, textoFonte, "contrato"))
|
|
4833
|
+
.filter((achado) => achado !== null);
|
|
4834
|
+
const achadosProibicoes = politicasProibicoes
|
|
4835
|
+
.map((politica) => criarAchadoAuthor(politica, textoFonte, "contrato"))
|
|
4836
|
+
.filter((achado) => achado !== null);
|
|
4837
|
+
const achadosHeuristicos = avaliarHeuristicasNarrativasAuthor(textoFonte, conteudoContrato, tokensContrato);
|
|
4838
|
+
const achadosPreset = avaliarPresetAuthor(preset, textoFonte);
|
|
4839
|
+
const achados = acao === "validar-proibicoes"
|
|
4840
|
+
? achadosProibicoes
|
|
4841
|
+
: [
|
|
4842
|
+
...achadosCatalogo,
|
|
4843
|
+
...achadosDinamicos,
|
|
4844
|
+
...achadosProibicoes,
|
|
4845
|
+
...achadosHeuristicos,
|
|
4846
|
+
...achadosPreset,
|
|
4847
|
+
];
|
|
4848
|
+
const bloqueios = achados
|
|
4849
|
+
.filter((achado) => achado.bloqueante)
|
|
4850
|
+
.map((achado) => achado.id);
|
|
4851
|
+
const contratoValido = !temErros(escolhido.resultado.diagnosticos);
|
|
4852
|
+
const scoreCoerenciaTonal = calcularScoreAuthor(achados, ["tom", "voz", "palavra_generica", "frase_generica"]);
|
|
4853
|
+
const scoreDriftNarrativo = calcularScoreAuthor(achados, [
|
|
4854
|
+
"arco_emocional",
|
|
4855
|
+
"campanha_persistente",
|
|
4856
|
+
"impact_map",
|
|
4857
|
+
"memoria_personagem",
|
|
4858
|
+
"promessa_narrativa",
|
|
4859
|
+
"proibicao_literal",
|
|
4860
|
+
"tema_sensivel",
|
|
4861
|
+
]);
|
|
4862
|
+
const scoreContratoFormal = contratoValido ? 100 : 0;
|
|
4863
|
+
const scoreContratoBase = contratoValido ? calcularScoreAuthor(achados) : 0;
|
|
4864
|
+
const scoreRisco = calcularScoreRiscoAuthor(achados, contratoValido);
|
|
4865
|
+
const confiancaValidacao = textoFonte.trim().length === 0
|
|
4866
|
+
? "baixa"
|
|
4867
|
+
: acao === "validar-narrativa" || acao === "validar-proibicoes" || politicasProibicoes.length > 0
|
|
4868
|
+
? "alta"
|
|
4869
|
+
: "parcial";
|
|
4870
|
+
const scoreAderenciaSemantica = confiancaValidacao === "parcial"
|
|
4871
|
+
? Math.min(scoreContratoBase, 85)
|
|
4872
|
+
: scoreContratoBase;
|
|
4873
|
+
const scoreContrato = Math.min(scoreContratoFormal, scoreAderenciaSemantica);
|
|
4874
|
+
const aprovado = contratoValido
|
|
4875
|
+
&& bloqueios.length === 0
|
|
4876
|
+
&& scoreCoerenciaTonal >= 80
|
|
4877
|
+
&& scoreDriftNarrativo >= 80
|
|
4878
|
+
&& scoreAderenciaSemantica >= 80
|
|
4879
|
+
&& scoreRisco < 40;
|
|
4880
|
+
const prontoParaAcao = aprovado && confiancaValidacao !== "baixa";
|
|
4881
|
+
const guardrailsDeclarados = extrairGuardrailsAuthor(conteudoContrato, escolhido.resultado.ir ?? null);
|
|
4882
|
+
const irNarrativo = extrairIrNarrativoAuthor(textoFonte);
|
|
4883
|
+
const diffSemantico = compararDiffSemanticoAuthor(textoAnterior, textoFonte);
|
|
4884
|
+
const impactMap = montarImpactMapAuthor(achados);
|
|
4885
|
+
const diagnosticoLocalizado = achados.filter((achado) => achado.bloqueante);
|
|
4886
|
+
const trechosBloqueantes = diagnosticoLocalizado.flatMap((achado) => achado.trechos);
|
|
4887
|
+
const violacoesProibicoes = achados.filter((achado) => achado.categoria === "proibicao_literal");
|
|
4888
|
+
const decisaoAgente = !contratoValido
|
|
4889
|
+
? "parar"
|
|
4890
|
+
: diagnosticoLocalizado.some((achado) => achado.severidadePadrao === "critical")
|
|
4891
|
+
? "chamar_humano"
|
|
4892
|
+
: bloqueios.length > 0
|
|
4893
|
+
? "parar"
|
|
4894
|
+
: confiancaValidacao === "parcial"
|
|
4895
|
+
? "continuar_com_ressalva"
|
|
4896
|
+
: "continuar";
|
|
4897
|
+
const podeContinuar = decisaoAgente === "continuar" || decisaoAgente === "continuar_com_ressalva";
|
|
4898
|
+
return {
|
|
4899
|
+
comando: acao === "revisar-cliches"
|
|
4900
|
+
? "author revisar-cliches"
|
|
4901
|
+
: acao === "validar-narrativa"
|
|
4902
|
+
? "author validar-narrativa"
|
|
4903
|
+
: "author validar-proibicoes",
|
|
4904
|
+
sucesso: aprovado,
|
|
4905
|
+
contratoValido,
|
|
4906
|
+
arquivo: escolhido.caminho,
|
|
4907
|
+
textoFonte: textoArquivoOpcional ? path.resolve(textoArquivoOpcional) : "inline",
|
|
4908
|
+
maturidade: "production",
|
|
4909
|
+
preset,
|
|
4910
|
+
presetsDisponiveis: PRESETS_AUTHOR,
|
|
4911
|
+
aprovado,
|
|
4912
|
+
bloqueado: !aprovado,
|
|
4913
|
+
podeContinuar,
|
|
4914
|
+
decisaoAgente,
|
|
4915
|
+
scoreCoerenciaTonal,
|
|
4916
|
+
scoreDriftNarrativo,
|
|
4917
|
+
scoreContrato,
|
|
4918
|
+
scoreContratoFormal,
|
|
4919
|
+
scoreAderenciaSemantica,
|
|
4920
|
+
scoreRisco,
|
|
4921
|
+
prontoParaAcao,
|
|
4922
|
+
confiancaValidacao,
|
|
4923
|
+
achados,
|
|
4924
|
+
bloqueios,
|
|
4925
|
+
diagnosticoLocalizado,
|
|
4926
|
+
trechosBloqueantes,
|
|
4927
|
+
violacoesProibicoes,
|
|
4928
|
+
modoSaidaAgente: "cirurgico",
|
|
4929
|
+
politicasAplicadas: unicosOrdenados([
|
|
4930
|
+
...politicasAplicadasCatalogo.map((politica) => politica.id),
|
|
4931
|
+
...politicasDinamicas.map((politica) => politica.id),
|
|
4932
|
+
...politicasProibicoes.map((politica) => politica.id),
|
|
4933
|
+
...achadosHeuristicos.map((achado) => achado.id),
|
|
4934
|
+
...achadosPreset.map((achado) => achado.id),
|
|
4935
|
+
]),
|
|
4936
|
+
guardrailsDeclarados,
|
|
4937
|
+
irNarrativo,
|
|
4938
|
+
impactMap,
|
|
4939
|
+
diffSemantico,
|
|
4940
|
+
recomendacoes: achados.length === 0
|
|
4941
|
+
? ["Contrato respeitado: nenhum bloqueio narrativo detectado. Ainda revise tensao, voz e consequencia da cena."]
|
|
4942
|
+
: unicosOrdenados(achados.map((achado) => achado.sugestao)),
|
|
4943
|
+
diagnosticos: escolhido.resultado.diagnosticos,
|
|
4944
|
+
};
|
|
4945
|
+
}
|
|
4946
|
+
function renderizarAuthorBriefingTexto(resultado) {
|
|
4947
|
+
return [
|
|
4948
|
+
"AUTHOR_BRIEFING",
|
|
4949
|
+
`ARQUIVO: ${resultado.arquivo}`,
|
|
4950
|
+
`MODULO: ${resultado.modulo ?? "desconhecido"}`,
|
|
4951
|
+
`PRESET: ${resultado.preset ?? "nenhum"}`,
|
|
4952
|
+
`CORE: ${resultado.coreDetectado ? "ok" : "ausente"}`,
|
|
4953
|
+
`AGENTS: ${resultado.agentsDetectados ? "ok" : "ausente"}`,
|
|
4954
|
+
`FLOW: ${resultado.flowDetectado ? "ok" : "ausente"}`,
|
|
4955
|
+
`TASKS: ${resumirListaTexto(resultado.tasks, 8)}`,
|
|
4956
|
+
`FLOWS: ${resumirListaTexto(resultado.flows, 8)}`,
|
|
4957
|
+
`PROIBICOES: ${resumirListaTexto(resultado.proibicoes, 8)}`,
|
|
4958
|
+
`CHECKS: ${resumirListaTexto(resultado.checks, 4)}`,
|
|
4959
|
+
].join("\n");
|
|
4960
|
+
}
|
|
4961
|
+
function renderizarAuthorClichesTexto(resultado) {
|
|
4962
|
+
const linhas = [
|
|
4963
|
+
resultado.comando === "author revisar-cliches" ? "AUTHOR_CLICHES" : resultado.comando.toUpperCase().replace(/[\s-]+/g, "_"),
|
|
4964
|
+
`ARQUIVO: ${resultado.arquivo}`,
|
|
4965
|
+
`TEXTO: ${resultado.textoFonte}`,
|
|
4966
|
+
`PRESET: ${resultado.preset ?? "nenhum"}`,
|
|
4967
|
+
`APROVADO: ${resultado.aprovado ? "sim" : "nao"}`,
|
|
4968
|
+
`BLOQUEADO: ${resultado.bloqueado ? "sim" : "nao"}`,
|
|
4969
|
+
`DECISAO_AGENTE: ${resultado.decisaoAgente}`,
|
|
4970
|
+
`SCORE_TOM: ${resultado.scoreCoerenciaTonal}`,
|
|
4971
|
+
`SCORE_DRIFT: ${resultado.scoreDriftNarrativo}`,
|
|
4972
|
+
`SCORE_CONTRATO: ${resultado.scoreContrato}`,
|
|
4973
|
+
`SCORE_CONTRATO_FORMAL: ${resultado.scoreContratoFormal}`,
|
|
4974
|
+
`SCORE_ADERENCIA_SEMANTICA: ${resultado.scoreAderenciaSemantica}`,
|
|
4975
|
+
`SCORE_RISCO: ${resultado.scoreRisco}`,
|
|
4976
|
+
`PRONTO_PARA_ACAO: ${resultado.prontoParaAcao ? "sim" : "nao"}`,
|
|
4977
|
+
`CONFIANCA_VALIDACAO: ${resultado.confiancaValidacao}`,
|
|
4978
|
+
`ACHADOS: ${resultado.achados.length}`,
|
|
4979
|
+
`VIOLACOES_PROIBICOES: ${resultado.violacoesProibicoes.length}`,
|
|
4980
|
+
];
|
|
4981
|
+
for (const achado of resultado.achados) {
|
|
4982
|
+
linhas.push(`- ${achado.id} (${achado.categoria}, ${achado.severidade}, ${achado.ocorrencias} ocorrencia(s)): ${achado.motivo}`);
|
|
4983
|
+
const primeiroTrecho = achado.trechos[0];
|
|
4984
|
+
if (primeiroTrecho) {
|
|
4985
|
+
linhas.push(` trecho ${primeiroTrecho.linha}:${primeiroTrecho.coluna}: ${primeiroTrecho.texto}`);
|
|
4986
|
+
}
|
|
4987
|
+
linhas.push(` sugestao: ${achado.sugestaoReescrita}`);
|
|
4988
|
+
}
|
|
4989
|
+
if (resultado.achados.length === 0) {
|
|
4990
|
+
linhas.push("- nenhum bloqueio narrativo detectado");
|
|
4991
|
+
}
|
|
4992
|
+
return linhas.join("\n");
|
|
4993
|
+
}
|
|
4994
|
+
function normalizarSubcomandoAuthor(subcomando) {
|
|
4995
|
+
return subcomando?.replace(/_/g, "-");
|
|
4996
|
+
}
|
|
4997
|
+
const PRESETS_AUTHOR = ["conto", "romance", "roteiro", "lore", "campanha"];
|
|
4998
|
+
function normalizarPresetAuthor(valor) {
|
|
4999
|
+
if (!valor)
|
|
5000
|
+
return null;
|
|
5001
|
+
const chave = valor.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
5002
|
+
const alias = {
|
|
5003
|
+
short_story: "conto",
|
|
5004
|
+
novela: "romance",
|
|
5005
|
+
script: "roteiro",
|
|
5006
|
+
screenplay: "roteiro",
|
|
5007
|
+
worldbuilding: "lore",
|
|
5008
|
+
mundo: "lore",
|
|
5009
|
+
campanha_persistente: "campanha",
|
|
5010
|
+
};
|
|
5011
|
+
const preset = alias[chave] ?? chave;
|
|
5012
|
+
return PRESETS_AUTHOR.includes(preset) ? preset : null;
|
|
5013
|
+
}
|
|
5014
|
+
function checksPresetAuthor(preset) {
|
|
5015
|
+
if (!preset)
|
|
5016
|
+
return [];
|
|
5017
|
+
const mapa = {
|
|
5018
|
+
conto: ["conflito concreto", "virada", "fechamento com consequencia"],
|
|
5019
|
+
romance: ["arco de personagem", "continuidade entre capitulos", "promessa narrativa"],
|
|
5020
|
+
roteiro: ["cenas", "dialogo", "acao visual", "estrutura de ato"],
|
|
5021
|
+
lore: ["canon", "mundo", "linha do tempo", "restricao de continuidade"],
|
|
5022
|
+
campanha: ["memoria persistente", "estado de personagens", "eventos irreversiveis", "drift entre sessoes"],
|
|
5023
|
+
};
|
|
5024
|
+
return mapa[preset];
|
|
5025
|
+
}
|
|
5026
|
+
function avaliarPresetAuthor(preset, textoFonte) {
|
|
5027
|
+
if (!preset)
|
|
5028
|
+
return [];
|
|
5029
|
+
const checks = {
|
|
5030
|
+
conto: [
|
|
5031
|
+
{ id: "author_conto_sem_conflito", regex: /conflito|quer|precisa|perde|arrisca|custo/i, sugestao: "conto precisa de desejo, custo e conflito visiveis." },
|
|
5032
|
+
{ id: "author_conto_sem_fechamento", regex: /fim|final|consequencia|consequência|mudou|perdeu|ganhou/i, sugestao: "feche o conto com mudanca ou consequencia, nao so atmosfera." },
|
|
5033
|
+
],
|
|
5034
|
+
romance: [
|
|
5035
|
+
{ id: "author_romance_sem_arco", regex: /arco|capitulo|capítulo|continuidade|promessa|personagem/i, sugestao: "romance precisa preservar arco, promessa e continuidade entre capitulos." },
|
|
5036
|
+
],
|
|
5037
|
+
roteiro: [
|
|
5038
|
+
{ id: "author_roteiro_sem_cena", regex: /cena|ato|dialogo|diálogo|acao visual|ação visual|int\.|ext\./i, sugestao: "roteiro precisa de cena, acao visual e/ou dialogo estruturado." },
|
|
5039
|
+
],
|
|
5040
|
+
lore: [
|
|
5041
|
+
{ id: "author_lore_sem_canon", regex: /canon|linha do tempo|mundo|facção|faccao|regra do mundo|continuidade/i, sugestao: "lore precisa de canon, mundo e restricoes de continuidade." },
|
|
5042
|
+
],
|
|
5043
|
+
campanha: [
|
|
5044
|
+
{ id: "author_campanha_sem_estado", regex: /estado|memoria|memória|sessao|sessão|evento irreversivel|evento irreversível|personagem/i, sugestao: "campanha precisa guardar estado, memoria e eventos irreversiveis." },
|
|
5045
|
+
],
|
|
5046
|
+
};
|
|
5047
|
+
return checks[preset].flatMap((check) => {
|
|
5048
|
+
if (check.regex.test(textoFonte))
|
|
5049
|
+
return [];
|
|
5050
|
+
const achado = criarAchadoHeuristicoAuthor(check.id, "preset_author", "media", 1, check.sugestao, [], textoFonte, check.sugestao, check.sugestao);
|
|
5051
|
+
return achado ? [achado] : [];
|
|
5052
|
+
});
|
|
5053
|
+
}
|
|
5054
|
+
async function comandoAuthor(posicionais, args, emJson) {
|
|
5055
|
+
const subcomandoOriginal = posicionais[0];
|
|
5056
|
+
const subcomando = normalizarSubcomandoAuthor(subcomandoOriginal);
|
|
5057
|
+
if (!subcomando || subcomando === "help" || subcomando === "ajuda") {
|
|
5058
|
+
console.log([
|
|
5059
|
+
"Uso: sema author <iniciar|validar|briefing|revisar-cliches|validar-narrativa|validar-proibicoes>",
|
|
5060
|
+
"",
|
|
5061
|
+
"Comandos:",
|
|
5062
|
+
" sema author iniciar [--tema-sensivel] [--saida contratos/author.sema] [--json]",
|
|
5063
|
+
" sema author validar <arquivo-ou-pasta> [--json]",
|
|
5064
|
+
" sema author briefing <arquivo-ou-pasta> [--preset conto|romance|roteiro|lore|campanha] [--json]",
|
|
5065
|
+
" sema author revisar-cliches <arquivo.sema> [--preset conto|romance|roteiro|lore|campanha] [--texto <texto>|--texto-arquivo <arquivo>] [--texto-anterior <texto>|--texto-anterior-arquivo <arquivo>] [--json]",
|
|
5066
|
+
" sema author validar-narrativa <arquivo.sema> [--preset conto|romance|roteiro|lore|campanha] [--texto <texto>|--texto-arquivo <arquivo>] [--json]",
|
|
5067
|
+
" sema author validar-proibicoes <arquivo.sema> [--texto <texto>|--texto-arquivo <arquivo>] [--json]",
|
|
5068
|
+
"",
|
|
5069
|
+
"Aliases aceitos: revisar_cliches, validar_narrativa e validar_proibicoes.",
|
|
5070
|
+
].join("\n"));
|
|
5071
|
+
return subcomando ? 0 : 1;
|
|
5072
|
+
}
|
|
5073
|
+
if (subcomando === "iniciar") {
|
|
5074
|
+
const resultado = await iniciarProfileAuthor(obterOpcao(args, "--saida"), possuiFlag(args, "--tema-sensivel"));
|
|
5075
|
+
if (emJson) {
|
|
5076
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
5077
|
+
}
|
|
5078
|
+
else if (resultado.sucesso) {
|
|
5079
|
+
console.log(`Profile Author criado em ${resultado.destino}`);
|
|
5080
|
+
}
|
|
5081
|
+
else {
|
|
5082
|
+
console.error(resultado.erro ?? "Falha ao iniciar profile Author.");
|
|
5083
|
+
}
|
|
5084
|
+
return resultado.sucesso ? 0 : 1;
|
|
5085
|
+
}
|
|
5086
|
+
const entrada = posicionais[1] ?? obterOpcao(args, "--arquivo");
|
|
5087
|
+
const presetAuthor = normalizarPresetAuthor(obterOpcao(args, "--preset"));
|
|
5088
|
+
if (obterOpcao(args, "--preset") && !presetAuthor) {
|
|
5089
|
+
console.error(`Preset Author invalido. Use: ${PRESETS_AUTHOR.join(", ")}.`);
|
|
5090
|
+
return 1;
|
|
5091
|
+
}
|
|
5092
|
+
if (subcomando === "validar") {
|
|
5093
|
+
return validarProfileAuthor(entrada, emJson);
|
|
5094
|
+
}
|
|
5095
|
+
if (subcomando === "briefing") {
|
|
5096
|
+
const resultado = await criarAuthorBriefing(entrada, presetAuthor);
|
|
5097
|
+
if (emJson) {
|
|
5098
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
5099
|
+
}
|
|
5100
|
+
else {
|
|
5101
|
+
console.log(renderizarAuthorBriefingTexto(resultado));
|
|
5102
|
+
}
|
|
5103
|
+
return resultado.sucesso ? 0 : 1;
|
|
5104
|
+
}
|
|
5105
|
+
if (subcomando === "revisar-cliches" || subcomando === "validar-narrativa" || subcomando === "validar-proibicoes") {
|
|
5106
|
+
const resultado = await revisarClichesAuthor(entrada, obterOpcao(args, "--texto"), obterOpcao(args, "--texto-arquivo"), obterOpcao(args, "--texto-anterior"), obterOpcao(args, "--texto-anterior-arquivo"), subcomando, presetAuthor);
|
|
5107
|
+
if (emJson) {
|
|
5108
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
5109
|
+
}
|
|
5110
|
+
else {
|
|
5111
|
+
console.log(renderizarAuthorClichesTexto(resultado));
|
|
5112
|
+
}
|
|
5113
|
+
return resultado.sucesso ? 0 : 1;
|
|
5114
|
+
}
|
|
5115
|
+
console.error(`Subcomando author desconhecido: ${subcomandoOriginal}`);
|
|
5116
|
+
return 1;
|
|
5117
|
+
}
|
|
3337
5118
|
function resolverEntradaDocs(posicionais, args) {
|
|
3338
5119
|
const intencao = obterOpcao(args, "--intencao") ?? posicionais[0] ?? "";
|
|
3339
5120
|
const arquivosPorFlag = obterOpcoesRepetidas(args, "--arquivo");
|
|
@@ -3411,381 +5192,6 @@ async function comandoFinalizarMudanca(posicionais, args, emJson) {
|
|
|
3411
5192
|
}
|
|
3412
5193
|
return 1;
|
|
3413
5194
|
}
|
|
3414
|
-
const BLOCOS_AUTHOR_CLI = [
|
|
3415
|
-
"work/book",
|
|
3416
|
-
"part/chapter/section",
|
|
3417
|
-
"scene/character/arc",
|
|
3418
|
-
"audience",
|
|
3419
|
-
"claim/source",
|
|
3420
|
-
"voice/style_rule/lexicon/motif/canon",
|
|
3421
|
-
"agent",
|
|
3422
|
-
"flow",
|
|
3423
|
-
];
|
|
3424
|
-
const FLUXO_EDITORIAL_AUTHOR_CLI = [
|
|
3425
|
-
"modele a obra e seus limites em work/book",
|
|
3426
|
-
"declare publico, canon, personagens, linha do tempo e regras de mundo quando existirem",
|
|
3427
|
-
"declare style_rule para cortar cliches, ecos, muletas e repeticoes",
|
|
3428
|
-
"declare claim/source para material factual ou sensivel",
|
|
3429
|
-
"governe revisores como agents com tools, memory e policy",
|
|
3430
|
-
"orquestre revisao, checagem de fontes e continuidade em flow",
|
|
3431
|
-
"rode docs-impacto antes de mexer em capitulo importante",
|
|
3432
|
-
"gere contexto-ia para compactar canon, arcos e continuidade",
|
|
3433
|
-
"feche com finalizar-mudanca informando docs lidas",
|
|
3434
|
-
];
|
|
3435
|
-
const REGRAS_AUTHOR_AUTOMATICAS_CLI = [
|
|
3436
|
-
{
|
|
3437
|
-
id: "mostrar_por_evidencia",
|
|
3438
|
-
nome: "Mostrar Por Evidência",
|
|
3439
|
-
aplica: "emocao, trauma, revolta e mudanca social precisam aparecer por evidencia concreta em cena",
|
|
3440
|
-
evitar: ["moral explicada", "emocao declarada", "simbolo sem uso concreto"],
|
|
3441
|
-
preferir: ["objeto alterado", "rotina quebrada", "gesto observavel", "decisao visivel"],
|
|
3442
|
-
},
|
|
3443
|
-
{
|
|
3444
|
-
id: "causa_e_consequencia",
|
|
3445
|
-
nome: "Causa E Consequência",
|
|
3446
|
-
aplica: "acao relevante precisa gerar consequencia proporcional",
|
|
3447
|
-
evitar: ["vitoria sem custo", "erro sem punicao", "revelacao sem efeito"],
|
|
3448
|
-
preferir: ["custo emocional", "custo fisico", "custo politico", "custo relacional"],
|
|
3449
|
-
},
|
|
3450
|
-
{
|
|
3451
|
-
id: "cena_com_dupla_funcao",
|
|
3452
|
-
nome: "Cena Com Dupla Função",
|
|
3453
|
-
aplica: "toda cena deve cumprir pelo menos duas funcoes narrativas sem exigir personagem novo",
|
|
3454
|
-
evitar: ["cena morta", "deslocamento vazio", "clima sem funcao"],
|
|
3455
|
-
preferir: ["avancar trama", "revelar personagem", "plantar pista", "mostrar consequencia"],
|
|
3456
|
-
},
|
|
3457
|
-
{
|
|
3458
|
-
id: "exposicao_com_atrito",
|
|
3459
|
-
nome: "Exposição Com Atrito",
|
|
3460
|
-
aplica: "informacao de mundo ou passado deve aparecer em situacao com tensao",
|
|
3461
|
-
evitar: ["palestra", "lore sem conflito", "dialogo em que todos ja sabem"],
|
|
3462
|
-
preferir: ["discussao", "ameaca", "documento incompleto", "erro operacional"],
|
|
3463
|
-
},
|
|
3464
|
-
{
|
|
3465
|
-
id: "antagonista_com_logica_interna",
|
|
3466
|
-
nome: "Antagonista Com Lógica Interna",
|
|
3467
|
-
aplica: "antagonistas precisam ter objetivo compreensivel e poder real de afetar a trama",
|
|
3468
|
-
evitar: ["malvadeza decorativa", "plano explicado inteiro", "incompetencia conveniente"],
|
|
3469
|
-
preferir: ["argumento parcialmente convincente", "vantagem estrutural", "contradicao humana"],
|
|
3470
|
-
},
|
|
3471
|
-
{
|
|
3472
|
-
id: "conflito_sem_solucao_limpa",
|
|
3473
|
-
nome: "Conflito Sem Solução Limpa",
|
|
3474
|
-
aplica: "problemas importantes nao devem ser resolvidos de forma perfeita ou gratuita",
|
|
3475
|
-
evitar: ["solucao magica", "perdao instantaneo", "plano sem imprevisto"],
|
|
3476
|
-
preferir: ["vitoria parcial", "dilema moral", "perda permanente", "novo problema"],
|
|
3477
|
-
},
|
|
3478
|
-
{
|
|
3479
|
-
id: "continuidade_de_personagens",
|
|
3480
|
-
nome: "Continuidade De Personagens",
|
|
3481
|
-
aplica: "personagem relevante precisa manter estado narrativo rastreavel",
|
|
3482
|
-
evitar: ["personagem sumir sem explicacao", "arco abandonado", "mudanca sem transicao"],
|
|
3483
|
-
preferir: ["onde esta", "o que quer", "o que sabe", "o que perdeu", "relacao alterada"],
|
|
3484
|
-
},
|
|
3485
|
-
{
|
|
3486
|
-
id: "misterio_em_escada",
|
|
3487
|
-
nome: "Mistério Em Escada",
|
|
3488
|
-
aplica: "revelacoes devem ser construidas por etapas",
|
|
3489
|
-
evitar: ["revelacao sem pista", "segredo tardio", "misterio mantido por falta de conversa"],
|
|
3490
|
-
preferir: ["pista", "contradicao", "interpretacao errada", "verdade parcial"],
|
|
3491
|
-
},
|
|
3492
|
-
{
|
|
3493
|
-
id: "dialogo_com_subtexto",
|
|
3494
|
-
nome: "Diálogo Com Subtexto",
|
|
3495
|
-
aplica: "dialogo deve carregar intencao, defesa, disputa ou tensao",
|
|
3496
|
-
evitar: ["fala explicativa", "todos com a mesma voz", "frase de efeito no lugar de conflito"],
|
|
3497
|
-
preferir: ["resposta indireta", "mentira util", "silencio significativo", "pergunta como ataque"],
|
|
3498
|
-
},
|
|
3499
|
-
{
|
|
3500
|
-
id: "acao_com_geografia_e_dano",
|
|
3501
|
-
nome: "Ação Com Geografia E Dano",
|
|
3502
|
-
aplica: "acao precisa ter espaco claro, objetivo concreto e consequencia depois",
|
|
3503
|
-
evitar: ["correria generica", "tiro para fingir ritmo", "sair ileso sem motivo"],
|
|
3504
|
-
preferir: ["mapa mental da cena", "obstaculo crescente", "uso do ambiente", "dano posterior"],
|
|
3505
|
-
},
|
|
3506
|
-
{
|
|
3507
|
-
id: "final_de_capitulo_sem_slogan",
|
|
3508
|
-
nome: "Final De Capítulo Sem Slogan",
|
|
3509
|
-
aplica: "capitulo termina com decisao, imagem, perda, ameaca ou descoberta, nao com moral de trailer",
|
|
3510
|
-
evitar: ["frase filosofica", "slogan sobre destino", "resumo da moral da cena"],
|
|
3511
|
-
preferir: ["decisao irreversivel", "imagem forte", "perda concreta", "ameaca especifica"],
|
|
3512
|
-
},
|
|
3513
|
-
{
|
|
3514
|
-
id: "morte_com_peso_estrutural",
|
|
3515
|
-
nome: "Morte Com Peso Estrutural",
|
|
3516
|
-
aplica: "morte importante precisa alterar relacoes, objetivo, poder, estado emocional ou rumo da obra",
|
|
3517
|
-
evitar: ["morte so para chocar", "luto pulado", "morte revertida sem custo"],
|
|
3518
|
-
preferir: ["mudanca em tres dimensoes", "custo narrativo", "consequencia no proximo capitulo"],
|
|
3519
|
-
},
|
|
3520
|
-
{
|
|
3521
|
-
id: "especificidade_sensorial",
|
|
3522
|
-
nome: "Especificidade Sensorial",
|
|
3523
|
-
aplica: "descricao deve trocar abstracao generica por detalhe observavel",
|
|
3524
|
-
evitar: ["ambiente sombrio", "dor insuportavel", "cidade caotica", "olhar triste"],
|
|
3525
|
-
preferir: ["som especifico", "cheiro especifico", "textura", "temperatura", "objeto fora do lugar"],
|
|
3526
|
-
},
|
|
3527
|
-
{
|
|
3528
|
-
id: "voz_distinta_por_personagem",
|
|
3529
|
-
nome: "Voz Distinta Por Personagem",
|
|
3530
|
-
aplica: "personagens recorrentes precisam ter forma propria de pensar, decidir e falar",
|
|
3531
|
-
evitar: ["todos falando como o narrador", "mesmo humor", "mesma reacao ao medo"],
|
|
3532
|
-
preferir: ["vocabulario proprio", "ritmo de fala", "tipo de silencio", "forma unica de mentir"],
|
|
3533
|
-
},
|
|
3534
|
-
{
|
|
3535
|
-
id: "anti_cliche_adaptativa",
|
|
3536
|
-
nome: "Anti-Clichê Adaptativa",
|
|
3537
|
-
aplica: "estrutura reconhecivel demais deve ser substituida por alternativa especifica",
|
|
3538
|
-
evitar: ["nao era sobre X, era sobre Y", "a esperanca renasceu", "o destino cobraria seu preco"],
|
|
3539
|
-
preferir: ["acao concreta", "consequencia", "gesto observavel", "pista plantada"],
|
|
3540
|
-
},
|
|
3541
|
-
];
|
|
3542
|
-
function resumirRegrasAuthorAutomaticas() {
|
|
3543
|
-
return REGRAS_AUTHOR_AUTOMATICAS_CLI.map((regra) => `${regra.nome}: ${regra.aplica}`);
|
|
3544
|
-
}
|
|
3545
|
-
function criarContratoAuthorExemplo(sensivel) {
|
|
3546
|
-
if (sensivel) {
|
|
3547
|
-
return `module author.starter.sensivel {
|
|
3548
|
-
work obra_sensivel {
|
|
3549
|
-
titulo: "Guia responsavel"
|
|
3550
|
-
proposito: "explicar um tema sensivel sem prometer verdade clinica"
|
|
3551
|
-
tipo: nao_ficcao
|
|
3552
|
-
tema: autismo
|
|
3553
|
-
sensivel: verdadeiro
|
|
3554
|
-
}
|
|
3555
|
-
|
|
3556
|
-
audience publico_principal {
|
|
3557
|
-
publico: familias_educadores_pessoas_autistas
|
|
3558
|
-
limites: "nao substitui avaliacao clinica ou orientacao profissional"
|
|
3559
|
-
}
|
|
3560
|
-
|
|
3561
|
-
claim autismo_e_espectro {
|
|
3562
|
-
texto: "autismo e um espectro com manifestacoes variadas"
|
|
3563
|
-
source: fonte_principal
|
|
3564
|
-
confidence: alta
|
|
3565
|
-
}
|
|
3566
|
-
|
|
3567
|
-
source fonte_principal {
|
|
3568
|
-
tipo: revisao_bibliografica
|
|
3569
|
-
confianca: alta
|
|
3570
|
-
}
|
|
3571
|
-
|
|
3572
|
-
style_rule linguagem_respeitosa {
|
|
3573
|
-
require {
|
|
3574
|
-
mostrar_por_evidencia
|
|
3575
|
-
causa_e_consequencia
|
|
3576
|
-
exposicao_com_atrito
|
|
3577
|
-
final_de_capitulo_sem_slogan
|
|
3578
|
-
anti_cliche_adaptativa
|
|
3579
|
-
}
|
|
3580
|
-
proibido {
|
|
3581
|
-
frase: "superar o autismo"
|
|
3582
|
-
generalizacao: "todo autista"
|
|
3583
|
-
promessa: "diagnostico garantido"
|
|
3584
|
-
slogan: "a esperanca renasceu"
|
|
3585
|
-
}
|
|
3586
|
-
evitar {
|
|
3587
|
-
tom: infantilizacao
|
|
3588
|
-
abordagem: sensacionalismo
|
|
3589
|
-
estrutura: "nao era sobre X, era sobre Y"
|
|
3590
|
-
}
|
|
3591
|
-
preferir {
|
|
3592
|
-
evidencia: gesto_observavel
|
|
3593
|
-
consequencia: custo_visivel
|
|
3594
|
-
linguagem: detalhe_especifico
|
|
3595
|
-
}
|
|
3596
|
-
tolerancia {
|
|
3597
|
-
max_repeticao_palavra_por_trecho: 2
|
|
3598
|
-
cena_funcoes_minimas: 2
|
|
3599
|
-
}
|
|
3600
|
-
}
|
|
3601
|
-
|
|
3602
|
-
agent checador_fontes {
|
|
3603
|
-
role: revisor_fontes
|
|
3604
|
-
goal: bloquear_afirmacao_sensivel_sem_fonte
|
|
3605
|
-
tools {
|
|
3606
|
-
ler_contrato
|
|
3607
|
-
verificar_fontes
|
|
3608
|
-
analisar_trecho
|
|
3609
|
-
}
|
|
3610
|
-
memory {
|
|
3611
|
-
claim
|
|
3612
|
-
source
|
|
3613
|
-
style_rule
|
|
3614
|
-
}
|
|
3615
|
-
policy {
|
|
3616
|
-
nao_afirmar_sem_fonte
|
|
3617
|
-
sinalizar_incerteza
|
|
3618
|
-
citar_trechos_afetados
|
|
3619
|
-
}
|
|
3620
|
-
}
|
|
3621
|
-
|
|
3622
|
-
flow revisar_secao_sensivel {
|
|
3623
|
-
trecho: Texto
|
|
3624
|
-
etapa fontes usa checador_fontes com trecho = trecho
|
|
3625
|
-
}
|
|
3626
|
-
}
|
|
3627
|
-
`;
|
|
3628
|
-
}
|
|
3629
|
-
return `module author.starter.obra {
|
|
3630
|
-
work obra {
|
|
3631
|
-
titulo: "Projeto autoral"
|
|
3632
|
-
proposito: "governar continuidade, arcos, personagens e tom editorial"
|
|
3633
|
-
tipo: ficcao
|
|
3634
|
-
}
|
|
3635
|
-
|
|
3636
|
-
audience leitores {
|
|
3637
|
-
publico: leitores_ficcao_especulativa
|
|
3638
|
-
promessa: "continuidade consistente e cenas com causa e consequencia"
|
|
3639
|
-
}
|
|
3640
|
-
|
|
3641
|
-
character protagonista {
|
|
3642
|
-
funcao: protagonista
|
|
3643
|
-
arco: queda_e_reconstrucao
|
|
3644
|
-
}
|
|
3645
|
-
|
|
3646
|
-
arc arco_principal {
|
|
3647
|
-
inicio: "desejo claro"
|
|
3648
|
-
virada: "custo moral visivel"
|
|
3649
|
-
fim: "mudanca irreversivel"
|
|
3650
|
-
}
|
|
3651
|
-
|
|
3652
|
-
canon universo {
|
|
3653
|
-
regra: "nada muda no mundo sem custo emocional, logistico ou relacional"
|
|
3654
|
-
}
|
|
3655
|
-
|
|
3656
|
-
style_rule anti_texto_raso {
|
|
3657
|
-
require {
|
|
3658
|
-
mostrar_por_evidencia
|
|
3659
|
-
causa_e_consequencia
|
|
3660
|
-
cena_com_dupla_funcao
|
|
3661
|
-
exposicao_com_atrito
|
|
3662
|
-
dialogo_com_subtexto
|
|
3663
|
-
final_de_capitulo_sem_slogan
|
|
3664
|
-
anti_cliche_adaptativa
|
|
3665
|
-
}
|
|
3666
|
-
proibido {
|
|
3667
|
-
frase: "de alguma forma"
|
|
3668
|
-
cliche: "o tempo parecia parar"
|
|
3669
|
-
estrutura: "nao era sobre X, era sobre Y"
|
|
3670
|
-
slogan: "a esperanca renasceu"
|
|
3671
|
-
moral: "todos finalmente entenderam a verdade"
|
|
3672
|
-
}
|
|
3673
|
-
ecos {
|
|
3674
|
-
palavra: "silencio"
|
|
3675
|
-
}
|
|
3676
|
-
preferir {
|
|
3677
|
-
evidencia: gesto_concreto
|
|
3678
|
-
consequencia: custo_visivel
|
|
3679
|
-
misterio: pista_contradicao_verdade_parcial
|
|
3680
|
-
final: decisao_ou_imagem_concreta
|
|
3681
|
-
}
|
|
3682
|
-
tolerancia {
|
|
3683
|
-
max_repeticao_palavra_por_trecho: 2
|
|
3684
|
-
cena_funcoes_minimas: 2
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
|
|
3688
|
-
agent revisor_continuidade {
|
|
3689
|
-
role: editor_author
|
|
3690
|
-
goal: preservar_canon_arcos_e_continuidade
|
|
3691
|
-
tools {
|
|
3692
|
-
ler_contrato
|
|
3693
|
-
analisar_trecho
|
|
3694
|
-
comparar_canon
|
|
3695
|
-
}
|
|
3696
|
-
memory {
|
|
3697
|
-
canon
|
|
3698
|
-
character
|
|
3699
|
-
arc
|
|
3700
|
-
style_rule
|
|
3701
|
-
}
|
|
3702
|
-
policy {
|
|
3703
|
-
nao_reescrever_sem_aprovacao
|
|
3704
|
-
citar_trechos_afetados
|
|
3705
|
-
registrar_decisoes_editoriais
|
|
3706
|
-
}
|
|
3707
|
-
}
|
|
3708
|
-
|
|
3709
|
-
flow revisar_capitulo {
|
|
3710
|
-
trecho: Texto
|
|
3711
|
-
etapa continuidade usa revisor_continuidade com trecho = trecho
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
`;
|
|
3715
|
-
}
|
|
3716
|
-
function criarPayloadAuthorCli(modo, sensivel) {
|
|
3717
|
-
return {
|
|
3718
|
-
comando: "author",
|
|
3719
|
-
profile: "Author",
|
|
3720
|
-
modo,
|
|
3721
|
-
sensivel,
|
|
3722
|
-
blocos: [...BLOCOS_AUTHOR_CLI],
|
|
3723
|
-
fluxoEditorial: [...FLUXO_EDITORIAL_AUTHOR_CLI],
|
|
3724
|
-
regrasAutomaticas: REGRAS_AUTHOR_AUTOMATICAS_CLI.map((regra) => ({
|
|
3725
|
-
...regra,
|
|
3726
|
-
evitar: [...regra.evitar],
|
|
3727
|
-
preferir: [...regra.preferir],
|
|
3728
|
-
})),
|
|
3729
|
-
comandos: [
|
|
3730
|
-
"sema author --help",
|
|
3731
|
-
"sema author exemplo",
|
|
3732
|
-
"sema author exemplo --sensivel --json",
|
|
3733
|
-
"sema validar contratos/<obra>.sema --json",
|
|
3734
|
-
"sema docs-impacto --intencao \"editar capitulo\" --arquivo contratos/<obra>.sema --criar-ausentes --json",
|
|
3735
|
-
"sema contexto-ia contratos/<obra>.sema --saida ./.tmp/contexto-author --json",
|
|
3736
|
-
"sema finalizar-mudanca --intencao \"editar capitulo\" --arquivo contratos/<obra>.sema --doc-lida docs/<doc>.md --json",
|
|
3737
|
-
],
|
|
3738
|
-
contratoExemplo: criarContratoAuthorExemplo(sensivel),
|
|
3739
|
-
};
|
|
3740
|
-
}
|
|
3741
|
-
function renderizarAjudaAuthor() {
|
|
3742
|
-
return [
|
|
3743
|
-
"Sema Author",
|
|
3744
|
-
"",
|
|
3745
|
-
renderizarSecaoAscii("Uso", [
|
|
3746
|
-
"sema author --help",
|
|
3747
|
-
"sema author exemplo",
|
|
3748
|
-
"sema author exemplo --sensivel",
|
|
3749
|
-
"sema author exemplo --sensivel --json",
|
|
3750
|
-
]),
|
|
3751
|
-
"",
|
|
3752
|
-
renderizarSecaoAscii("Serve para", [
|
|
3753
|
-
"obra, livro, capitulo, cena, personagem, arco, canon, tese, claim, fonte e continuidade",
|
|
3754
|
-
"ficcao, nao ficcao, livro tecnico, ensaio, biografia, pesquisa aplicada, manual e curso em livro",
|
|
3755
|
-
"evitar cliche, eco, muleta, generalizacao, infantilizacao e reescrita sem politica",
|
|
3756
|
-
]),
|
|
3757
|
-
"",
|
|
3758
|
-
renderizarSecaoAscii("Fluxo recomendado", [...FLUXO_EDITORIAL_AUTHOR_CLI]),
|
|
3759
|
-
"",
|
|
3760
|
-
renderizarSecaoAscii("Regras automaticas", resumirRegrasAuthorAutomaticas()),
|
|
3761
|
-
"",
|
|
3762
|
-
renderizarSecaoAscii("Blocos principais", [...BLOCOS_AUTHOR_CLI]),
|
|
3763
|
-
"",
|
|
3764
|
-
renderizarSecaoAscii("Comandos que encaixam", [
|
|
3765
|
-
"sema docs-impacto --intencao \"editar capitulo\" --arquivo contratos/<obra>.sema --criar-ausentes --json",
|
|
3766
|
-
"sema contexto-ia contratos/<obra>.sema --saida ./.tmp/contexto-author --json",
|
|
3767
|
-
"sema finalizar-mudanca --intencao \"editar capitulo\" --arquivo contratos/<obra>.sema --doc-lida docs/<doc>.md --json",
|
|
3768
|
-
]),
|
|
3769
|
-
].join("\n");
|
|
3770
|
-
}
|
|
3771
|
-
async function comandoAuthor(posicionais, args, emJson) {
|
|
3772
|
-
const subcomando = posicionais[0];
|
|
3773
|
-
const sensivel = possuiFlag(args, "--sensivel") || possuiFlag(args, "--sensitive");
|
|
3774
|
-
const modo = subcomando === "exemplo" || subcomando === "template" || subcomando === "starter"
|
|
3775
|
-
? "exemplo"
|
|
3776
|
-
: "ajuda";
|
|
3777
|
-
const payload = criarPayloadAuthorCli(modo, sensivel);
|
|
3778
|
-
if (emJson) {
|
|
3779
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
3780
|
-
return 0;
|
|
3781
|
-
}
|
|
3782
|
-
if (modo === "exemplo") {
|
|
3783
|
-
console.log(payload.contratoExemplo);
|
|
3784
|
-
return 0;
|
|
3785
|
-
}
|
|
3786
|
-
console.log(renderizarAjudaAuthor());
|
|
3787
|
-
return 0;
|
|
3788
|
-
}
|
|
3789
5195
|
async function comandoValidar(entrada) {
|
|
3790
5196
|
const modulos = await carregarModulos(entrada);
|
|
3791
5197
|
const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
|
|
@@ -4128,23 +5534,43 @@ async function comandoAstJson(arquivo) {
|
|
|
4128
5534
|
return temErros(resultado.diagnosticos) ? 1 : 0;
|
|
4129
5535
|
}
|
|
4130
5536
|
async function comandoIr(arquivo) {
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
5537
|
+
if (!arquivo) {
|
|
5538
|
+
console.error("Informe o arquivo .sema para gerar a IR.");
|
|
5539
|
+
return 1;
|
|
5540
|
+
}
|
|
5541
|
+
const modulos = await carregarModulos(arquivo);
|
|
5542
|
+
const resultado = modulos[0]?.resultado;
|
|
5543
|
+
console.log(JSON.stringify(resultado?.ir ?? null, null, 2));
|
|
5544
|
+
return resultado && !temErros(resultado.diagnosticos) ? 0 : 1;
|
|
4135
5545
|
}
|
|
4136
5546
|
async function comandoIrJson(arquivo) {
|
|
4137
|
-
|
|
4138
|
-
|
|
5547
|
+
if (!arquivo) {
|
|
5548
|
+
console.log(JSON.stringify({
|
|
5549
|
+
comando: "ir",
|
|
5550
|
+
caminho: null,
|
|
5551
|
+
modulo: null,
|
|
5552
|
+
sucesso: false,
|
|
5553
|
+
diagnosticos: [{
|
|
5554
|
+
codigo: "SEM_CLI_IR_ENTRADA_AUSENTE",
|
|
5555
|
+
mensagem: "Informe o arquivo .sema para gerar a IR.",
|
|
5556
|
+
severidade: "erro",
|
|
5557
|
+
}],
|
|
5558
|
+
ir: null,
|
|
5559
|
+
}, null, 2));
|
|
5560
|
+
return 1;
|
|
5561
|
+
}
|
|
5562
|
+
const modulos = await carregarModulos(arquivo);
|
|
5563
|
+
const item = modulos[0];
|
|
5564
|
+
const resultado = item?.resultado;
|
|
4139
5565
|
console.log(JSON.stringify({
|
|
4140
5566
|
comando: "ir",
|
|
4141
|
-
caminho: path.resolve(arquivo),
|
|
4142
|
-
modulo: resultado
|
|
4143
|
-
sucesso: !temErros(resultado.diagnosticos),
|
|
4144
|
-
diagnosticos: resultado
|
|
4145
|
-
ir: resultado
|
|
5567
|
+
caminho: item?.caminho ? path.resolve(item.caminho) : path.resolve(arquivo),
|
|
5568
|
+
modulo: resultado?.modulo?.nome ?? null,
|
|
5569
|
+
sucesso: Boolean(resultado && !temErros(resultado.diagnosticos)),
|
|
5570
|
+
diagnosticos: resultado?.diagnosticos ?? [],
|
|
5571
|
+
ir: resultado?.ir ?? null,
|
|
4146
5572
|
}, null, 2));
|
|
4147
|
-
return temErros(resultado.diagnosticos) ?
|
|
5573
|
+
return resultado && !temErros(resultado.diagnosticos) ? 0 : 1;
|
|
4148
5574
|
}
|
|
4149
5575
|
async function comandoCompilar(entrada, alvo, saida, estrutura, framework, cwd = process.cwd()) {
|
|
4150
5576
|
const incompatibilidade = validarCompatibilidadeFramework(alvo, framework);
|
|
@@ -4319,7 +5745,6 @@ async function comandoAjudaIa() {
|
|
|
4319
5745
|
"Use `sema prompt-curto <arquivo> --curto --para mudanca` para colar contexto em modelo gratuito.",
|
|
4320
5746
|
"Use `sema prompt-ia`, `sema prompt-ia-ui`, `sema prompt-ia-react` e `sema prompt-ia-sema-primeiro` conforme a tarefa.",
|
|
4321
5747
|
"Use `sema exemplos-prompt-ia` para pegar modelos prontos de prompt.",
|
|
4322
|
-
"Use `sema author` para obra, capitulos, cenas, personagens, continuidade, fontes, estilo, agents e flow editorial.",
|
|
4323
5748
|
"Use `sema inspecionar` para descobrir base, codigo vivo e fontes legado.",
|
|
4324
5749
|
"Use `sema drift` para medir impls, vinculos, rotas, score e lacunas.",
|
|
4325
5750
|
"Use `sema docs-impacto --intencao <acao>` para ler ou criar docs obrigatorias antes de agir.",
|
|
@@ -4492,7 +5917,7 @@ async function comandoPromptCurto(entrada, args, emJson) {
|
|
|
4492
5917
|
const capacidade = tamanho === "micro" ? "pequena" : tamanho === "curto" ? "media" : "grande";
|
|
4493
5918
|
const prompt = `Voce esta operando Sema em modo IA-first.
|
|
4494
5919
|
|
|
4495
|
-
Isto
|
|
5920
|
+
Isto e contexto comprimido para IA operar contrato semantico antes de tocar codigo vivo.
|
|
4496
5921
|
|
|
4497
5922
|
Capacidade alvo: ${capacidade}
|
|
4498
5923
|
Modo da tarefa: ${modo}
|
|
@@ -4738,6 +6163,12 @@ async function principal() {
|
|
|
4738
6163
|
case "iniciar":
|
|
4739
6164
|
codigoSaida = await comandoIniciar(cwd, normalizarTemplateIniciar(obterOpcao(resto, "--template")));
|
|
4740
6165
|
break;
|
|
6166
|
+
case "author":
|
|
6167
|
+
codigoSaida = await comandoAuthor(posicionais, resto, possuiFlag(resto, "--json"));
|
|
6168
|
+
break;
|
|
6169
|
+
case "profile":
|
|
6170
|
+
codigoSaida = await comandoProfile(posicionais, resto, possuiFlag(resto, "--json"));
|
|
6171
|
+
break;
|
|
4741
6172
|
case "validar":
|
|
4742
6173
|
codigoSaida = possuiFlag(resto, "--json")
|
|
4743
6174
|
? await comandoValidarJson(posicionais[0])
|
|
@@ -4807,7 +6238,7 @@ async function principal() {
|
|
|
4807
6238
|
{
|
|
4808
6239
|
const fonte = normalizarFonteImportacao(posicionais[0]);
|
|
4809
6240
|
if (!fonte || !posicionais[1]) {
|
|
4810
|
-
console.error("Uso: sema importar <nestjs|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|firebase|
|
|
6241
|
+
console.error("Uso: sema importar <nestjs|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|firebase|dotnet|java|go|rust|cpp|typescript|python|dart> <diretorio> [--saida <diretorio>] [--namespace <base>] [--json]");
|
|
4811
6242
|
codigoSaida = 1;
|
|
4812
6243
|
break;
|
|
4813
6244
|
}
|
|
@@ -4832,15 +6263,15 @@ async function principal() {
|
|
|
4832
6263
|
case "instalar-exemplos":
|
|
4833
6264
|
codigoSaida = await comandoInstalarExemplos(possuiFlag(resto, "--json"));
|
|
4834
6265
|
break;
|
|
6266
|
+
case "mcp-instalar-chave":
|
|
6267
|
+
codigoSaida = await comandoMcpInstalarChave(resto, possuiFlag(resto, "--json"));
|
|
6268
|
+
break;
|
|
4835
6269
|
case "docs-impacto":
|
|
4836
6270
|
codigoSaida = await comandoDocsImpacto(posicionais, resto, possuiFlag(resto, "--json"));
|
|
4837
6271
|
break;
|
|
4838
6272
|
case "finalizar-mudanca":
|
|
4839
6273
|
codigoSaida = await comandoFinalizarMudanca(posicionais, resto, possuiFlag(resto, "--json"));
|
|
4840
6274
|
break;
|
|
4841
|
-
case "author":
|
|
4842
|
-
codigoSaida = await comandoAuthor(posicionais, resto, possuiFlag(resto, "--json"));
|
|
4843
|
-
break;
|
|
4844
6275
|
case "resumo":
|
|
4845
6276
|
codigoSaida = await comandoResumo(posicionais[0], resto, possuiFlag(resto, "--json"));
|
|
4846
6277
|
break;
|