@semacode/cli 1.5.18 → 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 +144 -104
- package/SEMA_BRIEF.curto.txt +7 -5
- package/SEMA_BRIEF.md +60 -4
- package/SEMA_BRIEF.micro.txt +6 -4
- package/SEMA_INDEX.json +917 -41
- 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 +123 -14
- package/dist/drift.js.map +1 -1
- package/dist/index.js +1889 -38
- 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 -1
- package/dist/projeto.js.map +1 -1
- package/docs/AGENT_STARTER.md +103 -97
- package/docs/cli.md +175 -106
- package/docs/como-ensinar-a-sema-para-ia.md +41 -35
- package/docs/deploy.md +231 -43
- package/docs/documentacao.md +61 -36
- package/docs/env.md +105 -56
- package/docs/extensao-vscode.md +12 -4
- package/docs/fluxo-pratico-ia-sema.md +182 -176
- package/docs/instalacao-e-primeiro-uso.md +52 -30
- package/docs/integracao-com-ia.md +108 -101
- package/docs/mcp.md +292 -51
- package/docs/pagamento-ponta-a-ponta.md +34 -28
- package/docs/persistencia-vendor-first.md +11 -5
- package/docs/prompt-base-ia-sema.md +13 -7
- package/docs/rollback.md +17 -15
- package/docs/sintaxe.md +180 -174
- package/exemplos/agendamento.sema +105 -105
- package/exemplos/assinatura.sema +133 -133
- package/exemplos/auditoria.sema +89 -89
- 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 -127
- package/exemplos/exportacao.sema +94 -94
- package/exemplos/fila.sema +130 -130
- package/exemplos/integracao_externa.sema +94 -94
- package/exemplos/multi_tenant.sema +140 -140
- package/exemplos/notificacao.sema +149 -149
- package/exemplos/operacao_estrategia.sema +633 -633
- package/exemplos/pagamento.sema +434 -434
- package/exemplos/pagamento_dominio.sema +35 -35
- package/exemplos/pedido.sema +255 -255
- 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 -160
- 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 -94
- 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/formatador/index.js +12 -4
- package/node_modules/@sema/nucleo/dist/formatador/index.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 +11 -11
package/dist/index.js
CHANGED
|
@@ -18,13 +18,13 @@ import { carregarConfiguracaoProjeto, carregarProjeto, resolverAlvoPadrao, resol
|
|
|
18
18
|
import { importarProjetoLegado, resumoImportacao } from "./importador.js";
|
|
19
19
|
import { analisarDriftLegado, assistirRenomeacaoSemantica, gerarMapaImpactoSemantico, } from "./drift.js";
|
|
20
20
|
import { resolverDocumentacaoObrigatoria, verificarDocumentacaoMudanca, } from "./docs.js";
|
|
21
|
-
const STARTER_IA = `Voce esta trabalhando com Sema, um contrato semantico IA-first para agentes operarem software vivo em backend e front consumer.
|
|
21
|
+
const STARTER_IA = `Voce esta trabalhando com Sema, um contrato semantico IA-first para agentes operarem software vivo em backend e front consumer.
|
|
22
22
|
|
|
23
23
|
Importante:
|
|
24
|
-
- a Sema se apresenta publicamente como protocolo e funciona tecnicamente como linguagem de intencao
|
|
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
|
-
- leitura humana e bonus toleravel, nao objetivo de produto
|
|
24
|
+
- a Sema se apresenta publicamente como protocolo e funciona tecnicamente como linguagem de intencao
|
|
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
|
+
- 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
30
|
- a Sema gera codigo e scaffolding real para TypeScript, Python e Dart
|
|
@@ -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
|
|
@@ -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 contrato semantico IA-first para agentes operarem software vivo.
|
|
110
|
-
|
|
111
|
-
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.
|
|
110
|
+
const PROMPT_BASE_IA = `Voce esta trabalhando com Sema, um contrato semantico IA-first para agentes operarem software vivo.
|
|
111
|
+
|
|
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,13 +122,14 @@ 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
|
-
-
|
|
127
|
-
- use
|
|
128
|
-
- use
|
|
129
|
-
- use
|
|
130
|
-
-
|
|
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
|
|
129
|
+
- use diagnosticos estruturados como contrato de correcao
|
|
130
|
+
- use a IR como fonte de verdade semantica quando houver duvida
|
|
131
|
+
- use predicados canonicos como normalizacao opcional, preservando a forma original
|
|
132
|
+
- nao conclua uma alteracao sem validar e verificar o modulo
|
|
131
133
|
- comece pelo menor artefato semantico que resolva a tarefa
|
|
132
134
|
|
|
133
135
|
Antes de editar \`.sema\`, entenda:
|
|
@@ -241,9 +243,10 @@ Se houver duvida, siga os exemplos oficiais e mantenha a separacao:
|
|
|
241
243
|
`;
|
|
242
244
|
const PROMPT_IA_SEMA_PRIMEIRO = `Quero que voce trabalhe no modo "Sema primeiro".
|
|
243
245
|
|
|
244
|
-
Regra principal:
|
|
245
|
-
- modele primeiro o dominio em arquivos \`.sema\`
|
|
246
|
-
-
|
|
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
|
|
247
250
|
|
|
248
251
|
Fluxo obrigatorio:
|
|
249
252
|
1. entender o dominio pedido
|
|
@@ -344,6 +347,9 @@ const DOCUMENTOS_SUPORTE_IA = [
|
|
|
344
347
|
"docs/sintaxe.md",
|
|
345
348
|
"docs/cli.md",
|
|
346
349
|
"docs/mcp.md",
|
|
350
|
+
"docs/profiles.md",
|
|
351
|
+
"docs/profiles-author-agents-flow.md",
|
|
352
|
+
"docs/testes.md",
|
|
347
353
|
"docs/documentacao.md",
|
|
348
354
|
"docs/deploy.md",
|
|
349
355
|
"docs/env.md",
|
|
@@ -461,7 +467,20 @@ function ajuda() {
|
|
|
461
467
|
"sema docs-impacto --intencao \"fazer deploy\" --criar-ausentes --json",
|
|
462
468
|
"sema contexto-ia <arquivo.sema> --saida ./.tmp/contexto --json",
|
|
463
469
|
"",
|
|
464
|
-
"[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",
|
|
465
484
|
"sema importar <fonte> <diretorio> --saida <diretorio> --json",
|
|
466
485
|
"sema formatar <arquivo-ou-pasta>",
|
|
467
486
|
"sema validar <arquivo-ou-pasta> --json",
|
|
@@ -486,6 +505,8 @@ function ajuda() {
|
|
|
486
505
|
"teste local: sema testar <arquivo.sema> --alvo <python|typescript|dart|lua> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
|
|
487
506
|
"verificacao final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--json]",
|
|
488
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]",
|
|
489
510
|
]),
|
|
490
511
|
"",
|
|
491
512
|
renderizarSecaoAscii("Ajuda IA-first", [
|
|
@@ -501,8 +522,14 @@ function ajuda() {
|
|
|
501
522
|
"sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--json]",
|
|
502
523
|
"sema sync-ai-entrypoints [--json]",
|
|
503
524
|
"sema instalar-exemplos [--json]",
|
|
525
|
+
"sema mcp-instalar-chave --stdin [--endpoint <url>] [--dry-run] [--json]",
|
|
504
526
|
"sema docs-impacto --intencao <acao> [--arquivo <caminho>] [--criar-ausentes] [--json]",
|
|
505
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]",
|
|
506
533
|
]),
|
|
507
534
|
"",
|
|
508
535
|
renderizarSecaoAscii("Operacional", [
|
|
@@ -558,6 +585,15 @@ const OPCOES_COM_VALOR = new Set([
|
|
|
558
585
|
"--intencao",
|
|
559
586
|
"--arquivo",
|
|
560
587
|
"--doc-lida",
|
|
588
|
+
"--texto",
|
|
589
|
+
"--texto-arquivo",
|
|
590
|
+
"--endpoint",
|
|
591
|
+
"--maturidade",
|
|
592
|
+
"--preset",
|
|
593
|
+
"--artefato",
|
|
594
|
+
"--artifact",
|
|
595
|
+
"--artefato-arquivo",
|
|
596
|
+
"--artifact-file",
|
|
561
597
|
]);
|
|
562
598
|
const ALIAS_OPCOES = {
|
|
563
599
|
"-a": "--alvo",
|
|
@@ -611,6 +647,138 @@ function comandoDisponivel(comando, argumentos = ["--version"]) {
|
|
|
611
647
|
const execucao = spawnSync(comando, argumentos, { stdio: "ignore", shell: process.platform === "win32" });
|
|
612
648
|
return (execucao.status ?? 1) === 0;
|
|
613
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
|
+
}
|
|
614
782
|
function resolverExecucaoPython() {
|
|
615
783
|
if (comandoDisponivel("python")) {
|
|
616
784
|
return { comando: "python", argumentosBase: [], rotulo: "python" };
|
|
@@ -1305,6 +1473,8 @@ function criarGuiaCapacidadeIa() {
|
|
|
1305
1473
|
}
|
|
1306
1474
|
function coletarResumoSemanticoModulo(contexto) {
|
|
1307
1475
|
const { arquivo, modulo, geradoEm, ir, briefing, drift } = contexto;
|
|
1476
|
+
const modoVerificacaoCodigo = contexto.modoVerificacaoCodigo ?? "codigo_completo";
|
|
1477
|
+
const fontesConclusao = contexto.fontesConclusao ?? ["contrato", "codigo"];
|
|
1308
1478
|
const tarefas = ir?.tasks ?? [];
|
|
1309
1479
|
const rotas = ir?.routes ?? [];
|
|
1310
1480
|
const superficies = ir?.superficies ?? [];
|
|
@@ -1328,10 +1498,17 @@ function coletarResumoSemanticoModulo(contexto) {
|
|
|
1328
1498
|
...rotas.flatMap((route) => route.resumoAgente.entidadesAfetadas),
|
|
1329
1499
|
...superficies.flatMap((superficie) => superficie.resumoAgente.entidadesAfetadas),
|
|
1330
1500
|
]);
|
|
1331
|
-
|
|
1501
|
+
const resumo = {
|
|
1332
1502
|
geradoEm,
|
|
1333
1503
|
arquivo,
|
|
1334
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,
|
|
1335
1512
|
perfilCompatibilidade: ir?.perfilCompatibilidade ?? briefing.perfilCompatibilidade,
|
|
1336
1513
|
scoreSemantico: briefing.scoreSemantico,
|
|
1337
1514
|
confiancaGeral: briefing.confiancaGeral,
|
|
@@ -1362,6 +1539,87 @@ function coletarResumoSemanticoModulo(contexto) {
|
|
|
1362
1539
|
ancoragensVinculo: limitarLista(unicosOrdenados(briefing.ancoragensVinculo ?? []), 8),
|
|
1363
1540
|
arquivosProvaveisEditar: limitarLista(unicosOrdenados(briefing.arquivosProvaveisEditar ?? briefing.oQueTocar), 8),
|
|
1364
1541
|
};
|
|
1542
|
+
return modoVerificacaoCodigo === "codigo_completo"
|
|
1543
|
+
? resumo
|
|
1544
|
+
: normalizarResumoModoContratosApenas(resumo);
|
|
1545
|
+
}
|
|
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
|
+
};
|
|
1586
|
+
}
|
|
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";
|
|
1597
|
+
}
|
|
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
|
+
});
|
|
1365
1623
|
}
|
|
1366
1624
|
function renderizarResumoModuloTexto(resumo, tamanho, modo) {
|
|
1367
1625
|
const limite = tamanho === "micro" ? 2 : tamanho === "curto" ? 4 : 6;
|
|
@@ -1370,6 +1628,9 @@ function renderizarResumoModuloTexto(resumo, tamanho, modo) {
|
|
|
1370
1628
|
`MODULO: ${resumo.modulo}`,
|
|
1371
1629
|
`FAZ: ${resumo.faz}`,
|
|
1372
1630
|
`PERFIL: ${resumo.perfilCompatibilidade}`,
|
|
1631
|
+
`MODO_CODIGO: ${resumo.modoVerificacaoCodigo}`,
|
|
1632
|
+
...(resumo.avisoVerificacaoCodigo ? [`AVISO_CODIGO: ${resumo.avisoVerificacaoCodigo}`] : []),
|
|
1633
|
+
`FONTES_CONCLUSAO: ${resumirListaTexto(resumo.fontesConclusao, limite)}`,
|
|
1373
1634
|
`CONSUMER_FRAMEWORK: ${resumo.consumerFramework ?? "nenhum"}`,
|
|
1374
1635
|
`APP_ROUTES: ${resumirListaTexto(resumo.appRoutes, limite)}`,
|
|
1375
1636
|
`CONSUMER_SURFACES: ${resumirListaTexto(resumo.consumerSurfaces, limite)}`,
|
|
@@ -1406,6 +1667,9 @@ function renderizarResumoModuloMarkdown(resumo, modo, guiaPorCapacidade) {
|
|
|
1406
1667
|
`- Gerado em: \`${resumo.geradoEm}\``,
|
|
1407
1668
|
`- Arquivo: \`${resumo.arquivo}\``,
|
|
1408
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)}`,
|
|
1409
1673
|
`- Score: \`${resumo.scoreSemantico}\``,
|
|
1410
1674
|
`- Confianca: \`${resumo.confiancaGeral}\``,
|
|
1411
1675
|
`- Risco operacional: \`${resumo.riscoOperacional}\``,
|
|
@@ -1470,6 +1734,9 @@ function criarBriefingMinimo(resumo, modo, tamanho) {
|
|
|
1470
1734
|
tamanho,
|
|
1471
1735
|
arquivo: resumo.arquivo,
|
|
1472
1736
|
modulo: resumo.modulo,
|
|
1737
|
+
modoVerificacaoCodigo: resumo.modoVerificacaoCodigo,
|
|
1738
|
+
avisoVerificacaoCodigo: resumo.avisoVerificacaoCodigo,
|
|
1739
|
+
fontesConclusao: resumo.fontesConclusao,
|
|
1473
1740
|
perfilCompatibilidade: resumo.perfilCompatibilidade,
|
|
1474
1741
|
scoreSemantico: resumo.scoreSemantico,
|
|
1475
1742
|
confiancaGeral: resumo.confiancaGeral,
|
|
@@ -1501,7 +1768,7 @@ function criarPromptCurtoModulo(resumo, modo, tamanho, capacidade) {
|
|
|
1501
1768
|
const resumoTexto = renderizarResumoModuloTexto(resumo, tamanho, modo).trim();
|
|
1502
1769
|
return `Voce esta operando Sema em modo IA-first.
|
|
1503
1770
|
|
|
1504
|
-
Esta linguagem existe para traduzir intencao operacional em contrato consumivel por IA. Humanos aprovam; agentes operam.
|
|
1771
|
+
Esta linguagem existe para traduzir intencao operacional em contrato consumivel por IA. Humanos aprovam; agentes operam.
|
|
1505
1772
|
|
|
1506
1773
|
Capacidade alvo: ${capacidade}
|
|
1507
1774
|
Modo da tarefa: ${modo}
|
|
@@ -1827,6 +2094,8 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1827
2094
|
const geradoEm = new Date().toISOString();
|
|
1828
2095
|
const guiaPorCapacidade = criarGuiaCapacidadeIa();
|
|
1829
2096
|
const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
|
|
2097
|
+
const modoVerificacaoCodigo = await detectarModoVerificacaoCodigo(contextoProjeto.baseProjeto, contextoProjeto.diretoriosCodigo);
|
|
2098
|
+
const fontesConclusao = await detectarFontesConclusaoSnapshot(contextoProjeto.baseProjeto);
|
|
1830
2099
|
const resultadoDrift = await analisarDriftLegado(contextoProjeto);
|
|
1831
2100
|
const modulos = contextoProjeto.modulosSelecionados.map((item) => {
|
|
1832
2101
|
const modulo = item.resultado.modulo?.nome ?? path.basename(item.caminho, ".sema");
|
|
@@ -1846,6 +2115,8 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1846
2115
|
resumo: driftResumo,
|
|
1847
2116
|
drift: resultadoDrift,
|
|
1848
2117
|
},
|
|
2118
|
+
modoVerificacaoCodigo,
|
|
2119
|
+
fontesConclusao,
|
|
1849
2120
|
});
|
|
1850
2121
|
});
|
|
1851
2122
|
const baseProjeto = contextoProjeto.baseProjeto;
|
|
@@ -1861,6 +2132,9 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1861
2132
|
geradoEm,
|
|
1862
2133
|
cliVersao: VERSAO_CLI,
|
|
1863
2134
|
baseProjeto,
|
|
2135
|
+
modoVerificacaoCodigo,
|
|
2136
|
+
fontesConclusao,
|
|
2137
|
+
conclusoesPorFonte: descreverFontesConclusao(fontesConclusao, modoVerificacaoCodigo),
|
|
1864
2138
|
totalModulos: modulos.length,
|
|
1865
2139
|
entradaCanonica,
|
|
1866
2140
|
guiaPorCapacidade,
|
|
@@ -1869,6 +2143,8 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1869
2143
|
const micro = [
|
|
1870
2144
|
`PROJETO: ${path.basename(baseProjeto)}`,
|
|
1871
2145
|
`MODULOS: ${modulos.length}`,
|
|
2146
|
+
`MODO_CODIGO: ${modoVerificacaoCodigo}`,
|
|
2147
|
+
`FONTES_CONCLUSAO: ${resumirListaTexto(fontesConclusao, 4)}`,
|
|
1872
2148
|
`ENTRADA_IA: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
|
|
1873
2149
|
`TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 3)}`,
|
|
1874
2150
|
`TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 3)}`,
|
|
@@ -1880,6 +2156,13 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1880
2156
|
`PROJETO: ${path.basename(baseProjeto)}`,
|
|
1881
2157
|
`BASE: ${baseProjeto}`,
|
|
1882
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)}`,
|
|
1883
2166
|
`ENTRADA_IA: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
|
|
1884
2167
|
`TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 6)}`,
|
|
1885
2168
|
`TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 6)}`,
|
|
@@ -1896,6 +2179,7 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
|
|
|
1896
2179
|
geradoEm,
|
|
1897
2180
|
baseProjeto,
|
|
1898
2181
|
pastaSaida,
|
|
2182
|
+
modoVerificacaoCodigo,
|
|
1899
2183
|
artefatos: ["SEMA_BRIEF.md", "SEMA_BRIEF.micro.txt", "SEMA_BRIEF.curto.txt", "SEMA_INDEX.json"],
|
|
1900
2184
|
modulos,
|
|
1901
2185
|
guiaPorCapacidade,
|
|
@@ -3293,6 +3577,1544 @@ async function comandoInstalarExemplos(emJson) {
|
|
|
3293
3577
|
console.log(`- Preservados: ${resultado.preservados.length}`);
|
|
3294
3578
|
return 0;
|
|
3295
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
|
+
}
|
|
3296
5118
|
function resolverEntradaDocs(posicionais, args) {
|
|
3297
5119
|
const intencao = obterOpcao(args, "--intencao") ?? posicionais[0] ?? "";
|
|
3298
5120
|
const arquivosPorFlag = obterOpcoesRepetidas(args, "--arquivo");
|
|
@@ -3712,23 +5534,43 @@ async function comandoAstJson(arquivo) {
|
|
|
3712
5534
|
return temErros(resultado.diagnosticos) ? 1 : 0;
|
|
3713
5535
|
}
|
|
3714
5536
|
async function comandoIr(arquivo) {
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
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;
|
|
3719
5545
|
}
|
|
3720
5546
|
async function comandoIrJson(arquivo) {
|
|
3721
|
-
|
|
3722
|
-
|
|
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;
|
|
3723
5565
|
console.log(JSON.stringify({
|
|
3724
5566
|
comando: "ir",
|
|
3725
|
-
caminho: path.resolve(arquivo),
|
|
3726
|
-
modulo: resultado
|
|
3727
|
-
sucesso: !temErros(resultado.diagnosticos),
|
|
3728
|
-
diagnosticos: resultado
|
|
3729
|
-
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,
|
|
3730
5572
|
}, null, 2));
|
|
3731
|
-
return temErros(resultado.diagnosticos) ?
|
|
5573
|
+
return resultado && !temErros(resultado.diagnosticos) ? 0 : 1;
|
|
3732
5574
|
}
|
|
3733
5575
|
async function comandoCompilar(entrada, alvo, saida, estrutura, framework, cwd = process.cwd()) {
|
|
3734
5576
|
const incompatibilidade = validarCompatibilidadeFramework(alvo, framework);
|
|
@@ -4075,7 +5917,7 @@ async function comandoPromptCurto(entrada, args, emJson) {
|
|
|
4075
5917
|
const capacidade = tamanho === "micro" ? "pequena" : tamanho === "curto" ? "media" : "grande";
|
|
4076
5918
|
const prompt = `Voce esta operando Sema em modo IA-first.
|
|
4077
5919
|
|
|
4078
|
-
Isto e contexto comprimido para IA operar contrato semantico antes de tocar codigo vivo.
|
|
5920
|
+
Isto e contexto comprimido para IA operar contrato semantico antes de tocar codigo vivo.
|
|
4079
5921
|
|
|
4080
5922
|
Capacidade alvo: ${capacidade}
|
|
4081
5923
|
Modo da tarefa: ${modo}
|
|
@@ -4321,6 +6163,12 @@ async function principal() {
|
|
|
4321
6163
|
case "iniciar":
|
|
4322
6164
|
codigoSaida = await comandoIniciar(cwd, normalizarTemplateIniciar(obterOpcao(resto, "--template")));
|
|
4323
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;
|
|
4324
6172
|
case "validar":
|
|
4325
6173
|
codigoSaida = possuiFlag(resto, "--json")
|
|
4326
6174
|
? await comandoValidarJson(posicionais[0])
|
|
@@ -4415,6 +6263,9 @@ async function principal() {
|
|
|
4415
6263
|
case "instalar-exemplos":
|
|
4416
6264
|
codigoSaida = await comandoInstalarExemplos(possuiFlag(resto, "--json"));
|
|
4417
6265
|
break;
|
|
6266
|
+
case "mcp-instalar-chave":
|
|
6267
|
+
codigoSaida = await comandoMcpInstalarChave(resto, possuiFlag(resto, "--json"));
|
|
6268
|
+
break;
|
|
4418
6269
|
case "docs-impacto":
|
|
4419
6270
|
codigoSaida = await comandoDocsImpacto(posicionais, resto, possuiFlag(resto, "--json"));
|
|
4420
6271
|
break;
|