@semacode/cli 1.5.17 → 1.5.19

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