@semacode/cli 1.5.18 → 1.5.25

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.
Files changed (93) hide show
  1. package/AGENTS.md +268 -260
  2. package/LICENSE +22 -22
  3. package/README.md +144 -104
  4. package/SEMA_BRIEF.curto.txt +7 -5
  5. package/SEMA_BRIEF.md +61 -5
  6. package/SEMA_BRIEF.micro.txt +6 -4
  7. package/SEMA_INDEX.json +991 -47
  8. package/dist/controleComercialSupabase.d.ts +326 -0
  9. package/dist/controleComercialSupabase.js +310 -0
  10. package/dist/controleComercialSupabase.js.map +1 -0
  11. package/dist/docs.js +48 -20
  12. package/dist/docs.js.map +1 -1
  13. package/dist/drift.d.ts +5 -3
  14. package/dist/drift.js +123 -14
  15. package/dist/drift.js.map +1 -1
  16. package/dist/index.js +1889 -38
  17. package/dist/index.js.map +1 -1
  18. package/dist/mcpRemoto.d.ts +32 -0
  19. package/dist/mcpRemoto.js +61 -0
  20. package/dist/mcpRemoto.js.map +1 -0
  21. package/dist/projeto.js +3 -1
  22. package/dist/projeto.js.map +1 -1
  23. package/docs/AGENT_STARTER.md +103 -97
  24. package/docs/cli.md +175 -106
  25. package/docs/como-ensinar-a-sema-para-ia.md +41 -35
  26. package/docs/deploy.md +231 -43
  27. package/docs/documentacao.md +61 -36
  28. package/docs/env.md +105 -56
  29. package/docs/extensao-vscode.md +12 -4
  30. package/docs/fluxo-pratico-ia-sema.md +182 -176
  31. package/docs/instalacao-e-primeiro-uso.md +52 -30
  32. package/docs/integracao-com-ia.md +108 -101
  33. package/docs/mcp.md +292 -51
  34. package/docs/pagamento-ponta-a-ponta.md +34 -28
  35. package/docs/persistencia-vendor-first.md +11 -5
  36. package/docs/prompt-base-ia-sema.md +13 -7
  37. package/docs/rollback.md +17 -15
  38. package/docs/sintaxe.md +180 -174
  39. package/exemplos/agendamento.sema +105 -105
  40. package/exemplos/assinatura.sema +133 -133
  41. package/exemplos/auditoria.sema +89 -89
  42. package/exemplos/autenticacao.sema +125 -125
  43. package/exemplos/author_obra_comum.sema +294 -0
  44. package/exemplos/author_tema_sensivel.sema +264 -0
  45. package/exemplos/automacao.sema +107 -107
  46. package/exemplos/cadastro_usuario.sema +54 -54
  47. package/exemplos/calculadora.sema +78 -78
  48. package/exemplos/crud_simples.sema +89 -89
  49. package/exemplos/estoque.sema +127 -127
  50. package/exemplos/exportacao.sema +94 -94
  51. package/exemplos/fila.sema +130 -130
  52. package/exemplos/integracao_externa.sema +94 -94
  53. package/exemplos/multi_tenant.sema +140 -140
  54. package/exemplos/notificacao.sema +149 -149
  55. package/exemplos/operacao_estrategia.sema +633 -633
  56. package/exemplos/pagamento.sema +434 -434
  57. package/exemplos/pagamento_dominio.sema +35 -35
  58. package/exemplos/pedido.sema +255 -255
  59. package/exemplos/permissao.sema +121 -121
  60. package/exemplos/persistencia_vendor_first.sema +86 -86
  61. package/exemplos/profile_game.sema +114 -0
  62. package/exemplos/profile_legal.sema +105 -0
  63. package/exemplos/profile_ops.sema +110 -0
  64. package/exemplos/profile_research.sema +104 -0
  65. package/exemplos/profile_software.sema +123 -0
  66. package/exemplos/profile_workflow_n8n.sema +99 -0
  67. package/exemplos/relatorio.sema +93 -93
  68. package/exemplos/replica_analitica_erp.sema +160 -160
  69. package/exemplos/testes_embutidos.sema +45 -45
  70. package/exemplos/tratamento_erro.sema +157 -157
  71. package/exemplos/upload_arquivo.sema +93 -93
  72. package/exemplos/webhook.sema +94 -94
  73. package/llms-full.txt +34 -34
  74. package/llms.txt +17 -17
  75. package/node_modules/@sema/gerador-css/dist/index.js +563 -563
  76. package/node_modules/@sema/gerador-css/package.json +1 -1
  77. package/node_modules/@sema/gerador-dart/package.json +1 -1
  78. package/node_modules/@sema/gerador-html/dist/index.js +90 -90
  79. package/node_modules/@sema/gerador-html/package.json +1 -1
  80. package/node_modules/@sema/gerador-javascript/dist/index.js +92 -92
  81. package/node_modules/@sema/gerador-javascript/package.json +1 -1
  82. package/node_modules/@sema/gerador-lua/dist/index.js +53 -53
  83. package/node_modules/@sema/gerador-lua/package.json +1 -1
  84. package/node_modules/@sema/gerador-python/dist/index.js +122 -96
  85. package/node_modules/@sema/gerador-python/dist/index.js.map +1 -1
  86. package/node_modules/@sema/gerador-python/package.json +1 -1
  87. package/node_modules/@sema/gerador-typescript/dist/index.js +153 -153
  88. package/node_modules/@sema/gerador-typescript/package.json +1 -1
  89. package/node_modules/@sema/nucleo/dist/formatador/index.js +12 -4
  90. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -1
  91. package/node_modules/@sema/nucleo/package.json +1 -1
  92. package/node_modules/@sema/padroes/package.json +1 -1
  93. 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
- - se a IA for pequena, nao tente abrir tudo de uma vez
47
- - use \`sema resumo\` e \`briefing.min.json\` antes de subir para o pacote completo
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
- - preserve o significado semantico
126
- - use o formatador oficial da Sema como fonte unica de estilo
127
- - use diagnosticos estruturados como contrato de correcao
128
- - use a IR como fonte de verdade semantica quando houver duvida
129
- - use predicados canonicos como normalizacao opcional, preservando a forma original
130
- - nao conclua uma alteracao sem validar e verificar o modulo
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
- - so depois proponha ou gere codigo de aplicacao derivado disso
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] Adotar Sema em projeto que ainda nao usa",
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
- return {
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
- const codigo = await lerArquivoTexto(arquivo);
3716
- const resultado = compilarCodigo(codigo, arquivo);
3717
- console.log(JSON.stringify(resultado.ir ?? null, null, 2));
3718
- return temErros(resultado.diagnosticos) ? 1 : 0;
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
- const codigo = await lerArquivoTexto(arquivo);
3722
- const resultado = compilarCodigo(codigo, arquivo);
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.modulo?.nome ?? null,
3727
- sucesso: !temErros(resultado.diagnosticos),
3728
- diagnosticos: resultado.diagnosticos,
3729
- ir: resultado.ir ?? null,
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) ? 1 : 0;
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;