@semacode/cli 1.3.2 → 1.3.5

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 (155) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +33 -27
  3. package/package.json +21 -33
  4. package/semacode-cli-1.3.1.tgz +0 -0
  5. package/src/cpp-symbols.ts +82 -0
  6. package/src/dotnet-http.ts +355 -0
  7. package/src/drift.ts +2455 -0
  8. package/src/go-http.ts +118 -0
  9. package/src/importador.ts +3448 -0
  10. package/src/index.ts +4476 -0
  11. package/src/java-http.ts +247 -0
  12. package/src/projeto.ts +810 -0
  13. package/src/python-http.ts +258 -0
  14. package/src/rust-http.ts +125 -0
  15. package/src/tipos.ts +22 -0
  16. package/src/typescript-http.ts +1076 -0
  17. package/tsconfig.json +20 -0
  18. package/AGENTS.md +0 -272
  19. package/LICENSE +0 -22
  20. package/SEMA_BRIEF.curto.txt +0 -9
  21. package/SEMA_BRIEF.md +0 -49
  22. package/SEMA_BRIEF.micro.txt +0 -7
  23. package/SEMA_INDEX.json +0 -546
  24. package/dist/cpp-symbols.d.ts +0 -10
  25. package/dist/cpp-symbols.js.map +0 -1
  26. package/dist/dotnet-http.d.ts +0 -23
  27. package/dist/dotnet-http.js.map +0 -1
  28. package/dist/drift.d.ts +0 -118
  29. package/dist/drift.js.map +0 -1
  30. package/dist/go-http.d.ts +0 -23
  31. package/dist/go-http.js.map +0 -1
  32. package/dist/importador.d.ts +0 -29
  33. package/dist/importador.js.map +0 -1
  34. package/dist/index.d.ts +0 -2
  35. package/dist/index.js.map +0 -1
  36. package/dist/java-http.d.ts +0 -23
  37. package/dist/java-http.js.map +0 -1
  38. package/dist/lua-symbols.d.ts +0 -8
  39. package/dist/lua-symbols.js.map +0 -1
  40. package/dist/projeto.d.ts +0 -48
  41. package/dist/projeto.js.map +0 -1
  42. package/dist/python-http.d.ts +0 -23
  43. package/dist/python-http.js.map +0 -1
  44. package/dist/rust-http.d.ts +0 -23
  45. package/dist/rust-http.js.map +0 -1
  46. package/dist/tipos.d.ts +0 -3
  47. package/dist/tipos.js.map +0 -1
  48. package/dist/typescript-http.d.ts +0 -35
  49. package/dist/typescript-http.js.map +0 -1
  50. package/docs/AGENT_STARTER.md +0 -102
  51. package/docs/como-ensinar-a-sema-para-ia.md +0 -149
  52. package/docs/fluxo-pratico-ia-sema.md +0 -177
  53. package/docs/instalacao-e-primeiro-uso.md +0 -196
  54. package/docs/integracao-com-ia.md +0 -228
  55. package/docs/pagamento-ponta-a-ponta.md +0 -155
  56. package/docs/prompt-base-ia-sema.md +0 -104
  57. package/docs/sintaxe.md +0 -361
  58. package/exemplos/agendamento.sema +0 -106
  59. package/exemplos/assinatura.sema +0 -136
  60. package/exemplos/auditoria.sema +0 -88
  61. package/exemplos/autenticacao.sema +0 -125
  62. package/exemplos/automacao.sema +0 -107
  63. package/exemplos/cadastro_usuario.sema +0 -54
  64. package/exemplos/calculadora.sema +0 -78
  65. package/exemplos/crud_simples.sema +0 -89
  66. package/exemplos/estoque.sema +0 -126
  67. package/exemplos/exportacao.sema +0 -94
  68. package/exemplos/fila.sema +0 -131
  69. package/exemplos/integracao_externa.sema +0 -94
  70. package/exemplos/multi_tenant.sema +0 -140
  71. package/exemplos/notificacao.sema +0 -98
  72. package/exemplos/operacao_estrategia.sema +0 -402
  73. package/exemplos/pagamento.sema +0 -222
  74. package/exemplos/pagamento_dominio.sema +0 -35
  75. package/exemplos/pedido.sema +0 -119
  76. package/exemplos/permissao.sema +0 -121
  77. package/exemplos/relatorio.sema +0 -93
  78. package/exemplos/testes_embutidos.sema +0 -45
  79. package/exemplos/tratamento_erro.sema +0 -157
  80. package/exemplos/upload_arquivo.sema +0 -93
  81. package/exemplos/webhook.sema +0 -96
  82. package/llms-full.txt +0 -34
  83. package/llms.txt +0 -17
  84. package/node_modules/@sema/gerador-css/dist/index.d.ts +0 -3
  85. package/node_modules/@sema/gerador-css/dist/index.js +0 -592
  86. package/node_modules/@sema/gerador-css/dist/index.js.map +0 -1
  87. package/node_modules/@sema/gerador-css/package.json +0 -7
  88. package/node_modules/@sema/gerador-dart/dist/index.d.ts +0 -3
  89. package/node_modules/@sema/gerador-dart/dist/index.js +0 -44
  90. package/node_modules/@sema/gerador-dart/dist/index.js.map +0 -1
  91. package/node_modules/@sema/gerador-dart/package.json +0 -7
  92. package/node_modules/@sema/gerador-html/dist/index.d.ts +0 -3
  93. package/node_modules/@sema/gerador-html/dist/index.js +0 -163
  94. package/node_modules/@sema/gerador-html/dist/index.js.map +0 -1
  95. package/node_modules/@sema/gerador-html/package.json +0 -7
  96. package/node_modules/@sema/gerador-javascript/dist/index.d.ts +0 -3
  97. package/node_modules/@sema/gerador-javascript/dist/index.js +0 -421
  98. package/node_modules/@sema/gerador-javascript/dist/index.js.map +0 -1
  99. package/node_modules/@sema/gerador-javascript/package.json +0 -7
  100. package/node_modules/@sema/gerador-lua/dist/index.d.ts +0 -3
  101. package/node_modules/@sema/gerador-lua/dist/index.js +0 -328
  102. package/node_modules/@sema/gerador-lua/dist/index.js.map +0 -1
  103. package/node_modules/@sema/gerador-lua/package.json +0 -7
  104. package/node_modules/@sema/gerador-python/dist/index.d.ts +0 -6
  105. package/node_modules/@sema/gerador-python/dist/index.js +0 -628
  106. package/node_modules/@sema/gerador-python/dist/index.js.map +0 -1
  107. package/node_modules/@sema/gerador-python/package.json +0 -7
  108. package/node_modules/@sema/gerador-typescript/dist/index.d.ts +0 -6
  109. package/node_modules/@sema/gerador-typescript/dist/index.js +0 -656
  110. package/node_modules/@sema/gerador-typescript/dist/index.js.map +0 -1
  111. package/node_modules/@sema/gerador-typescript/package.json +0 -7
  112. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +0 -122
  113. package/node_modules/@sema/nucleo/dist/ast/tipos.js +0 -2
  114. package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +0 -1
  115. package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +0 -21
  116. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +0 -12
  117. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +0 -1
  118. package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +0 -9
  119. package/node_modules/@sema/nucleo/dist/formatador/index.js +0 -445
  120. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +0 -1
  121. package/node_modules/@sema/nucleo/dist/index.d.ts +0 -34
  122. package/node_modules/@sema/nucleo/dist/index.js +0 -95
  123. package/node_modules/@sema/nucleo/dist/index.js.map +0 -1
  124. package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +0 -5
  125. package/node_modules/@sema/nucleo/dist/ir/conversor.js +0 -781
  126. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +0 -1
  127. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +0 -285
  128. package/node_modules/@sema/nucleo/dist/ir/modelos.js +0 -2
  129. package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +0 -1
  130. package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +0 -7
  131. package/node_modules/@sema/nucleo/dist/lexer/lexer.js +0 -122
  132. package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +0 -1
  133. package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +0 -8
  134. package/node_modules/@sema/nucleo/dist/lexer/tokens.js +0 -46
  135. package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +0 -1
  136. package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +0 -9
  137. package/node_modules/@sema/nucleo/dist/parser/parser.js +0 -656
  138. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +0 -1
  139. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +0 -57
  140. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +0 -1497
  141. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +0 -1
  142. package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +0 -104
  143. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +0 -445
  144. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +0 -1
  145. package/node_modules/@sema/nucleo/dist/semantico/seguranca.d.ts +0 -91
  146. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js +0 -258
  147. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +0 -1
  148. package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +0 -2
  149. package/node_modules/@sema/nucleo/dist/util/arquivos.js +0 -25
  150. package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +0 -1
  151. package/node_modules/@sema/nucleo/package.json +0 -7
  152. package/node_modules/@sema/padroes/dist/index.d.ts +0 -23
  153. package/node_modules/@sema/padroes/dist/index.js +0 -210
  154. package/node_modules/@sema/padroes/dist/index.js.map +0 -1
  155. package/node_modules/@sema/padroes/package.json +0 -7
package/src/index.ts ADDED
@@ -0,0 +1,4476 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
6
+ import pacoteCli from "../package.json" with { type: "json" };
7
+ import {
8
+ compilarCodigo,
9
+ formatarCodigo,
10
+ formatarDiagnosticos,
11
+ lerArquivoTexto,
12
+ temErros,
13
+ type IrModulo,
14
+ } from "@sema/nucleo";
15
+ import { descreverEstruturaModulo, type AlvoGeracao, type FrameworkGeracao } from "@sema/padroes";
16
+ import { gerarDart } from "@sema/gerador-dart";
17
+ import { gerarLua } from "@sema/gerador-lua";
18
+ import { gerarPython } from "@sema/gerador-python";
19
+ import { gerarTypeScript } from "@sema/gerador-typescript";
20
+ import { gerarJavaScript } from "@sema/gerador-javascript";
21
+ import { gerarHtml } from "@sema/gerador-html";
22
+ import { gerarCss } from "@sema/gerador-css";
23
+ import {
24
+ carregarConfiguracaoProjeto,
25
+ carregarProjeto,
26
+ resolverAlvoPadrao,
27
+ resolverAlvosVerificacao,
28
+ resolverEstruturaSaidaPadrao,
29
+ resolverFrameworkPadrao,
30
+ resolverSaidaPadrao,
31
+ type ContextoProjetoCarregado,
32
+ } from "./projeto.js";
33
+ import type { EstruturaSaida } from "./tipos.js";
34
+ import { importarProjetoLegado, resumoImportacao, type FonteImportacao } from "./importador.js";
35
+ import { analisarDriftLegado } from "./drift.js";
36
+
37
+ type Comando =
38
+ | "iniciar"
39
+ | "validar"
40
+ | "ast"
41
+ | "ir"
42
+ | "compilar"
43
+ | "gerar"
44
+ | "testar"
45
+ | "diagnosticos"
46
+ | "verificar"
47
+ | "inspecionar"
48
+ | "drift"
49
+ | "importar"
50
+ | "doctor"
51
+ | "formatar"
52
+ | "ajuda-ia"
53
+ | "starter-ia"
54
+ | "sync-ai-entrypoints"
55
+ | "resumo"
56
+ | "prompt-curto"
57
+ | "prompt-ia"
58
+ | "prompt-ia-ui"
59
+ | "prompt-ia-react"
60
+ | "prompt-ia-sema-primeiro"
61
+ | "exemplos-prompt-ia"
62
+ | "contexto-ia";
63
+
64
+ type TemplateIniciar =
65
+ | FrameworkGeracao
66
+ | "nextjs-api"
67
+ | "nextjs-consumer"
68
+ | "react-vite-consumer"
69
+ | "angular-consumer"
70
+ | "flutter-consumer"
71
+ | "node-firebase-worker"
72
+ | "aspnet-api"
73
+ | "springboot-api"
74
+ | "go-http-api"
75
+ | "rust-axum-api"
76
+ | "cpp-service-bridge";
77
+
78
+ interface ResultadoExecucaoTestes {
79
+ codigoSaida: number;
80
+ quantidadeTestes: number;
81
+ }
82
+
83
+ interface ResumoAlvoVerificacao {
84
+ alvo: AlvoGeracao;
85
+ arquivosGerados: number;
86
+ quantidadeTestes: number;
87
+ pastaSaida: string;
88
+ sucesso: boolean;
89
+ framework: FrameworkGeracao;
90
+ estrutura: EstruturaSaida;
91
+ testesExecutados: boolean;
92
+ }
93
+
94
+ interface ResumoModuloVerificacao {
95
+ modulo: string;
96
+ arquivoFonte: string;
97
+ alvos: ResumoAlvoVerificacao[];
98
+ }
99
+
100
+ interface SaidaTesteCapturada {
101
+ codigoSaida: number;
102
+ quantidadeTestes: number;
103
+ saidaPadrao: string;
104
+ saidaErro: string;
105
+ }
106
+
107
+ interface ResultadoFormatacaoArquivo {
108
+ caminho: string;
109
+ alterado: boolean;
110
+ sucesso: boolean;
111
+ diagnosticos: ReturnType<typeof compilarCodigo>["diagnosticos"];
112
+ }
113
+
114
+ interface ContextoIaGerado {
115
+ sucesso: boolean;
116
+ arquivo: string;
117
+ modulo: string;
118
+ pastaSaida: string;
119
+ artefatos: string[];
120
+ artefatosCompactos: string[];
121
+ geradoEm: string;
122
+ guiaPorCapacidade: Record<CapacidadeIa, GuiaCapacidadeIa>;
123
+ }
124
+
125
+ interface DescobertaDocsIa {
126
+ origemInstalacao: string;
127
+ baseDetectada: string | null;
128
+ documentos: Array<{ nome: string; caminho: string }>;
129
+ }
130
+
131
+ type TamanhoResumoIa = "micro" | "curto" | "medio";
132
+ type ModoResumoIa = "resumo" | "onboarding" | "review" | "mudanca" | "bug" | "arquitetura";
133
+ type CapacidadeIa = "pequena" | "media" | "grande";
134
+ type ResultadoDriftIa = Awaited<ReturnType<typeof analisarDriftLegado>>;
135
+ type ResumoModuloDrift = ReturnType<typeof resumirDriftPorModulo>;
136
+
137
+ interface GuiaCapacidadeIa {
138
+ descricao: string;
139
+ artefatos: string[];
140
+ ordemLeitura: string[];
141
+ evitar: string[];
142
+ }
143
+
144
+ interface PacoteContextoModuloIa {
145
+ arquivo: string;
146
+ modulo: string;
147
+ sucesso: boolean;
148
+ geradoEm: string;
149
+ diagnosticos: ReturnType<typeof compilarCodigo>["diagnosticos"];
150
+ ir: IrModulo | null;
151
+ validar: {
152
+ comando: "validar";
153
+ sucesso: boolean;
154
+ resultados: Array<{
155
+ caminho: string;
156
+ modulo: string | null;
157
+ sucesso: boolean;
158
+ diagnosticos: ReturnType<typeof compilarCodigo>["diagnosticos"];
159
+ }>;
160
+ };
161
+ diagnosticosJson: {
162
+ comando: "diagnosticos";
163
+ caminho: string;
164
+ modulo: string | null;
165
+ diagnosticos: ReturnType<typeof compilarCodigo>["diagnosticos"];
166
+ };
167
+ ast: {
168
+ comando: "ast";
169
+ caminho: string;
170
+ modulo: string | null;
171
+ sucesso: boolean;
172
+ diagnosticos: ReturnType<typeof compilarCodigo>["diagnosticos"];
173
+ ast: unknown;
174
+ };
175
+ irJson: {
176
+ comando: "ir";
177
+ caminho: string;
178
+ modulo: string | null;
179
+ sucesso: boolean;
180
+ diagnosticos: ReturnType<typeof compilarCodigo>["diagnosticos"];
181
+ ir: IrModulo | null;
182
+ };
183
+ drift: {
184
+ comando: "drift";
185
+ caminho: string;
186
+ modulo: string | null;
187
+ sucesso: boolean;
188
+ resumo: ResumoModuloDrift;
189
+ drift: ResultadoDriftIa;
190
+ };
191
+ briefing: ReturnType<typeof criarBriefingAgente>;
192
+ }
193
+
194
+ interface ResumoSemanticoModuloIa {
195
+ geradoEm: string;
196
+ arquivo: string;
197
+ modulo: string;
198
+ perfilCompatibilidade: string;
199
+ scoreSemantico: number;
200
+ confiancaGeral: string;
201
+ riscoOperacional: string;
202
+ faz: string;
203
+ tarefasPrincipais: string[];
204
+ entradasChave: string[];
205
+ saidasChave: string[];
206
+ superficiesPublicas: string[];
207
+ regrasCriticas: string[];
208
+ efeitos: string[];
209
+ erros: string[];
210
+ entidadesAfetadas: string[];
211
+ arquivosProvaveis: string[];
212
+ simbolosRelacionados: string[];
213
+ riscosPrincipais: string[];
214
+ lacunas: string[];
215
+ inferido: string[];
216
+ checksSugeridos: string[];
217
+ testesMinimos: string[];
218
+ consumerFramework: string | null;
219
+ appRoutes: string[];
220
+ consumerSurfaces: string[];
221
+ consumerBridges: string[];
222
+ arquivosProvaveisEditar: string[];
223
+ }
224
+
225
+ const STARTER_IA = `Voce esta trabalhando com Sema, um Protocolo de Governanca de Intencao para IA sobre software vivo em backend e front consumer.
226
+
227
+ Importante:
228
+ - a Sema se apresenta publicamente como protocolo e funciona tecnicamente como linguagem de intencao
229
+ - a Sema e protocolo de governanca semantica desenhado para IA, nao para ergonomia humana
230
+ - leitura humana e bonus toleravel, nao objetivo de produto
231
+ - a Sema nao e gerador magico que deveria fazer tudo
232
+ - a Sema modela contratos, estados, fluxos, erros, efeitos, garantias, vinculos e execucao
233
+ - a Sema gera codigo e scaffolding real para TypeScript, Python e Dart
234
+ - a Sema usa \`importar\` para bootstrap revisavel, nao para contrato final automatico
235
+ - a Sema usa \`impl\` para ligar task a simbolo real do runtime
236
+ - a Sema usa \`vinculos\` para ligar contrato a arquivo, simbolo, recurso e superficie real
237
+ - a Sema usa \`execucao\` para explicitar timeout, retry, compensacao e criticidade
238
+ - a Sema usa \`drift\` para medir diferenca entre contrato e codigo vivo com score, confianca e lacunas
239
+ - a Sema usa \`resumo\` e \`prompt-curto\` para IA pequena ou gratuita
240
+ - a Sema usa \`contexto-ia\` para gerar \`ast.json\`, \`ir.json\`, \`drift.json\`, \`briefing.json\` e artefatos compactos antes da edicao
241
+ - a Sema pode servir de base para interfaces graficas elegantes e coerentes
242
+ - a Sema nao gera uma interface completa sozinha no estado atual
243
+ - trate a Sema como cerebro semantico da aplicacao, nao como gerador magico de front-end pronto
244
+ - se a tarefa envolver UI, prefira pedir Sema + React + TypeScript ou Sema + arquitetura de front-end
245
+ - evite pedir HTML unico solto quando a intencao for testar a Sema de verdade
246
+
247
+ Regras:
248
+ - nao invente sintaxe fora da gramatica e dos exemplos oficiais
249
+ - se a IA for pequena, nao tente abrir tudo de uma vez
250
+ - use \`sema resumo\` e \`briefing.min.json\` antes de subir para o pacote completo
251
+ - trate \`ir --json\` como fonte de verdade semantica
252
+ - trate \`briefing.json\` como plano de intervencao antes de editar projeto vivo
253
+ - trate \`diagnosticos --json\` como fonte de correcao
254
+ - use \`sema formatar\` como fonte unica de estilo
255
+ - preserve a intencao do contrato
256
+ - nao cobre da Sema adivinhacao de negocio que nao esta no contrato nem no codigo
257
+
258
+ Comandos essenciais:
259
+ - resumo compacto por capacidade: \`sema resumo <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>]\`
260
+ - prompt curto para IA pequena: \`sema prompt-curto <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>]\`
261
+ - descoberta do projeto: \`sema inspecionar [arquivo-ou-pasta] --json\`
262
+ - auditoria do contrato vivo: \`sema drift <arquivo-ou-pasta> [--json]\`
263
+ - contexto completo do modulo: \`sema contexto-ia <arquivo.sema>\`
264
+ - estrutura sintatica: \`sema ast <arquivo.sema> --json\`
265
+ - estrutura semantica: \`sema ir <arquivo.sema> --json\`
266
+ - validacao: \`sema validar <arquivo.sema> --json\`
267
+ - diagnosticos: \`sema diagnosticos <arquivo.sema> --json\`
268
+ - formatacao: \`sema formatar <arquivo.sema>\`
269
+ - 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>\`
270
+ - geracao de codigo: \`sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart|lua> --saida <diretorio>\`
271
+ - verificacao final: \`sema verificar <arquivo-ou-pasta> [--json]\`
272
+
273
+ Antes de editar:
274
+ 1. leia README, docs de IA e um exemplo oficial parecido
275
+ 2. se a IA for pequena, rode \`sema resumo <arquivo> --micro\` e leia \`briefing.min.json\`
276
+ 3. se a IA aguentar mais, rode \`sema drift\` para medir impls, vinculos, rotas, score e lacunas
277
+ 4. se a tarefa for pesada, rode \`sema contexto-ia\` e leia \`briefing.json\`
278
+ 5. consulte AST e IR do modulo alvo so quando a capacidade realmente aguentar
279
+
280
+ Depois de editar:
281
+ 1. rode \`sema formatar\`
282
+ 2. rode \`sema validar --json\`
283
+ 3. se houver falha, use \`diagnosticos --json\`
284
+ 4. rode \`sema drift\` de novo quando mexer em codigo vivo
285
+ 5. se a tarefa pedir codigo derivado, rode \`sema compilar\`
286
+ 6. feche com \`sema verificar <arquivo-ou-pasta> --json\`
287
+
288
+ Priorize sempre:
289
+ - exemplos oficiais
290
+ - JSON da CLI
291
+ - o menor artefato que resolva a tarefa da IA atual
292
+ - score, confianca e lacunas do \`drift\`
293
+ - \`briefing.json\` como guia de mudanca
294
+ - consistencia semantica
295
+
296
+ Superficies que a IA deve enxergar como first-class:
297
+ - \`route\`
298
+ - \`worker\`
299
+ - \`evento\`
300
+ - \`fila\`
301
+ - \`cron\`
302
+ - \`webhook\`
303
+ - \`cache\`
304
+ - \`storage\`
305
+ - \`policy\`
306
+
307
+ Nao improvise quando faltar contexto.
308
+ `;
309
+
310
+ const PROMPT_BASE_IA = `Voce esta trabalhando com Sema, um Protocolo de Governanca de Intencao orientado a contrato, desenhado para operacao por IA.
311
+
312
+ 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.
313
+
314
+ Fontes de verdade, em ordem:
315
+ 1. se o projeto expuser \`SEMA_CONTEXT.md\`, comece por ele
316
+ 2. \`SEMA_BRIEF.md\`
317
+ 3. \`SEMA_INDEX.json\`
318
+ 4. README do projeto
319
+ 5. gramatica e documentacao de sintaxe da Sema
320
+ 6. especificacao semantica da linguagem
321
+ 7. exemplos oficiais, com prioridade para o vertical de pagamento
322
+ 8. \`sema resumo\` e \`briefing.min.json\` quando a IA for pequena
323
+ 9. AST, IR e diagnosticos exportados pela CLI em JSON quando a capacidade aguentar
324
+
325
+ Regras de operacao:
326
+ - preserve o significado semantico
327
+ - use o formatador oficial da Sema como fonte unica de estilo
328
+ - use diagnosticos estruturados como contrato de correcao
329
+ - use a IR como fonte de verdade semantica quando houver duvida
330
+ - nao conclua uma alteracao sem validar e verificar o modulo
331
+ - comece pelo menor artefato semantico que resolva a tarefa
332
+
333
+ Antes de editar \`.sema\`, entenda:
334
+ - o module alvo
335
+ - os contratos de task, route, error, effects, guarantees, state e flow
336
+ - os exemplos oficiais relacionados
337
+
338
+ Depois de editar \`.sema\`, execute este fluxo:
339
+ 1. formatar
340
+ 2. validar
341
+ 3. diagnosticar, se houver falha
342
+ 4. verificar
343
+
344
+ Se houver conflito entre texto livre e IR/diagnosticos, priorize a IR e os diagnosticos da CLI.
345
+
346
+ Se algo nao estiver claro, siga a forma ja usada nos exemplos oficiais. Nao improvise sem base.
347
+ `;
348
+
349
+ const PROMPT_IA_UI = `Atue como Engenheiro de Software Senior e UX/UI Designer de elite.
350
+
351
+ Quero que voce trabalhe com Sema como fonte de verdade semantica do sistema e com React + TypeScript como camada de interface.
352
+
353
+ Entregue obrigatoriamente duas partes integradas:
354
+ 1. os arquivos \`.sema\` do dominio
355
+ 2. a proposta ou implementacao da interface em React + TypeScript
356
+
357
+ Regras:
358
+ - nao entregue apenas HTML solto em arquivo unico
359
+ - nao trate a Sema como enfeite conceitual
360
+ - a interface deve nascer do contrato semantico definido em Sema
361
+ - use os exemplos oficiais da Sema como referencia de estilo e semantica
362
+ - nao invente sintaxe fora da gramatica suportada
363
+
364
+ A Sema deve modelar, quando fizer sentido:
365
+ - \`module\`
366
+ - \`use\`
367
+ - \`entity\`
368
+ - \`enum\`
369
+ - \`state\`
370
+ - \`task\`
371
+ - \`flow\`
372
+ - \`route\`
373
+ - \`effects\`
374
+ - \`error\`
375
+ - \`guarantees\`
376
+ - \`tests\`
377
+ - \`docs\`
378
+
379
+ A interface deve refletir visualmente:
380
+ - \`state\` como status e progresso observavel
381
+ - \`flow\` como etapas ou orquestracao visivel
382
+ - \`error\` como falhas tratadas com clareza
383
+ - \`effects\` como operacoes relevantes para usuario ou operacao
384
+ - \`guarantees\` como confianca, confirmacao ou consistencia final
385
+
386
+ Estruture a entrega assim:
387
+ 1. visao do produto
388
+ 2. dominio modelado em Sema
389
+ 3. arquitetura de pastas em React + TypeScript
390
+ 4. componentes principais
391
+ 5. estrategia visual
392
+ 6. codigo principal da interface
393
+ 7. explicacao curta de como a UI conversa com a semantica da Sema
394
+
395
+ Se a tarefa envolver app visual, a Sema governa o significado e o React renderiza a experiencia. Nao atropele essa separacao.
396
+ `;
397
+
398
+ const PROMPT_IA_REACT = `Crie uma solucao com Sema + React + TypeScript.
399
+
400
+ Regras principais:
401
+ - a Sema deve ser a fonte de verdade semantica do dominio
402
+ - React + TypeScript deve ser a camada de interface e experiencia
403
+ - nao entregue HTML unico solto
404
+ - nao trate a Sema como enfeite
405
+
406
+ Entregue obrigatoriamente:
407
+ 1. arquivos \`.sema\` do dominio
408
+ 2. arquitetura de pastas do frontend
409
+ 3. componentes React principais
410
+ 4. contratos e tipos derivados da semantica
411
+ 5. interface elegante e implementavel
412
+
413
+ A modelagem Sema deve cobrir, quando fizer sentido:
414
+ - \`entity\`
415
+ - \`enum\`
416
+ - \`state\`
417
+ - \`task\`
418
+ - \`flow\`
419
+ - \`route\`
420
+ - \`effects\`
421
+ - \`error\`
422
+ - \`guarantees\`
423
+ - \`tests\`
424
+
425
+ A interface React deve tornar visiveis:
426
+ - estado atual e transicoes relevantes
427
+ - fluxo operacional
428
+ - erros publicos
429
+ - efeitos operacionais importantes
430
+ - garantias ou confirmacoes finais
431
+
432
+ Estruture a entrega assim:
433
+ 1. visao do produto
434
+ 2. arquivos \`.sema\`
435
+ 3. arquitetura React + TypeScript
436
+ 4. componentes e telas
437
+ 5. codigo principal
438
+ 6. explicacao de como a UI deriva da semantica da Sema
439
+
440
+ Se houver duvida, siga os exemplos oficiais e mantenha a separacao:
441
+ - Sema governa o significado
442
+ - React governa a apresentacao
443
+ `;
444
+
445
+ const PROMPT_IA_SEMA_PRIMEIRO = `Quero que voce trabalhe no modo "Sema primeiro".
446
+
447
+ Regra principal:
448
+ - modele primeiro o dominio em arquivos \`.sema\`
449
+ - so depois proponha ou gere codigo de aplicacao derivado disso
450
+
451
+ Fluxo obrigatorio:
452
+ 1. entender o dominio pedido
453
+ 2. modelar o contrato em Sema
454
+ 3. validar coerencia entre \`task\`, \`route\`, \`state\`, \`flow\`, \`error\`, \`effects\` e \`guarantees\`
455
+ 4. so depois gerar TypeScript, Python, React ou outra camada de implementacao
456
+
457
+ Nao entregue apenas codigo de interface ou codigo imperativo direto sem antes entregar a camada semantica.
458
+
459
+ A modelagem em Sema deve:
460
+ - preservar a intencao do dominio
461
+ - explicitar entradas, saidas, erros, efeitos e garantias
462
+ - usar apenas blocos e sintaxe oficiais
463
+ - incluir testes embutidos quando fizer sentido
464
+
465
+ Se houver interface grafica:
466
+ - entregue a modelagem Sema primeiro
467
+ - depois explique como a interface deve refletir a semantica
468
+ - se gerar UI, use React + TypeScript em vez de HTML unico solto
469
+
470
+ Se houver backend:
471
+ - entregue a modelagem Sema primeiro
472
+ - depois gere a borda publica e a implementacao derivada
473
+
474
+ Nao pule a etapa semantica. A camada \`.sema\` e a ancora principal da solucao.
475
+ `;
476
+
477
+ const EXEMPLOS_PROMPT_IA = `Exemplos de prompt oficial para trabalhar com Sema
478
+
479
+ 1. Sema primeiro
480
+
481
+ Crie uma solucao seguindo a estrategia "Sema primeiro".
482
+ Entregue primeiro os arquivos \`.sema\` do dominio e so depois a implementacao derivada.
483
+ Nao entregue apenas codigo imperativo.
484
+ Use Sema como fonte de verdade para contratos, estados, erros, efeitos e garantias.
485
+
486
+ 2. Sema + React + TypeScript
487
+
488
+ Crie um projeto com Sema + React + TypeScript.
489
+ Entregue:
490
+ - os arquivos \`.sema\` do dominio
491
+ - a arquitetura de pastas do frontend
492
+ - componentes React que reflitam \`state\`, \`flow\`, \`error\`, \`effects\` e \`guarantees\`
493
+ - uma interface elegante e implementavel
494
+ - nao entregue HTML solto em arquivo unico
495
+
496
+ 3. Revisar ou corrigir um modulo Sema
497
+
498
+ Revise e corrija um modulo \`.sema\`.
499
+ Antes de editar:
500
+ - leia os exemplos oficiais parecidos
501
+ - consulte AST e IR
502
+ Depois de editar:
503
+ - rode \`sema formatar\`
504
+ - rode \`sema validar --json\`
505
+ - use \`diagnosticos --json\` se houver falha
506
+ - feche com \`sema verificar\`
507
+
508
+ 4. Caso de UI sem perder a semantica
509
+
510
+ Quero uma interface premium para este dominio, mas a solucao deve continuar ancorada em Sema.
511
+ Modele primeiro o dominio em \`.sema\`.
512
+ Depois proponha uma interface em React + TypeScript que torne visiveis:
513
+ - estado
514
+ - fluxo
515
+ - erros
516
+ - efeitos
517
+ - garantias
518
+ Nao transforme isso em um \`index.html\` solto.
519
+
520
+ Comandos uteis da CLI para esse fluxo:
521
+ - \`sema starter-ia\`
522
+ - \`sema ajuda-ia\`
523
+ - \`sema resumo <arquivo-ou-pasta>\`
524
+ - \`sema prompt-curto <arquivo-ou-pasta>\`
525
+ - \`sema prompt-ia\`
526
+ - \`sema prompt-ia-ui\`
527
+ - \`sema prompt-ia-react\`
528
+ - \`sema prompt-ia-sema-primeiro\`
529
+ - \`sema contexto-ia <arquivo.sema>\`
530
+ `;
531
+
532
+ const DIRETORIO_CLI_ATUAL = path.dirname(fileURLToPath(import.meta.url));
533
+ const VERSAO_CLI = pacoteCli.version;
534
+ const ARQUIVOS_CANONICOS_IA_RAIZ = [
535
+ "llms.txt",
536
+ "SEMA_BRIEF.md",
537
+ "SEMA_INDEX.json",
538
+ "AGENTS.md",
539
+ "README.md",
540
+ "llms-full.txt",
541
+ ] as const;
542
+ const DOCUMENTOS_SUPORTE_IA = [
543
+ "docs/AGENT_STARTER.md",
544
+ "docs/integracao-com-ia.md",
545
+ "docs/fluxo-pratico-ia-sema.md",
546
+ "docs/como-ensinar-a-sema-para-ia.md",
547
+ "docs/sintaxe.md",
548
+ "docs/cli.md",
549
+ ] as const;
550
+
551
+ function obterArgumentos(): { comando?: Comando; resto: string[] } {
552
+ const [, , comando, ...resto] = process.argv;
553
+ return { comando: comando as Comando | undefined, resto };
554
+ }
555
+
556
+ function renderizarCaixaAscii(linhas: string[]): string {
557
+ const largura = Math.max(...linhas.map((linha) => linha.length), 12);
558
+ const borda = `+${"-".repeat(largura + 2)}+`;
559
+ return [
560
+ borda,
561
+ ...linhas.map((linha) => `| ${linha.padEnd(largura, " ")} |`),
562
+ borda,
563
+ ].join("\n");
564
+ }
565
+
566
+ function renderizarSecaoAscii(titulo: string, linhas: string[]): string {
567
+ return [
568
+ titulo,
569
+ ...linhas.map((linha) => ` ${linha}`),
570
+ ].join("\n");
571
+ }
572
+
573
+ function ajuda(): string {
574
+ return [
575
+ renderizarCaixaAscii([
576
+ `Sema CLI v${VERSAO_CLI}`,
577
+ "IA-first para contrato, geracao e adocao incremental",
578
+ "novo projeto, edicao guiada e legado sem contrato inicial",
579
+ ]),
580
+ "",
581
+ renderizarSecaoAscii("Fluxos rapidos", [
582
+ "[1] Projeto novo / producao inicial",
583
+ "sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer>",
584
+ "sema validar contratos/<modulo>.sema --json",
585
+ "sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart|lua> --saida <diretorio>",
586
+ "sema verificar <arquivo-ou-pasta> --json",
587
+ "",
588
+ "[2] Editar projeto que ja usa Sema",
589
+ "sema inspecionar . --json",
590
+ "sema resumo <arquivo-ou-pasta> --micro --para mudanca",
591
+ "sema drift <arquivo-ou-pasta> --json",
592
+ "sema contexto-ia <arquivo.sema> --saida ./.tmp/contexto --json",
593
+ "",
594
+ "[3] Adotar Sema em projeto que ainda nao usa",
595
+ "sema importar <fonte> <diretorio> --saida <diretorio> --json",
596
+ "sema formatar <arquivo-ou-pasta>",
597
+ "sema validar <arquivo-ou-pasta> --json",
598
+ "sema drift <arquivo-ou-pasta> --json",
599
+ ]),
600
+ "",
601
+ renderizarSecaoAscii("IA por capacidade", [
602
+ "pequena: sema resumo --micro + briefing.min.json + prompt-curto.txt",
603
+ "media: sema resumo --curto + drift.json + briefing.min.json",
604
+ "grande: sema contexto-ia + briefing.json + ir.json + ast.json",
605
+ ]),
606
+ "",
607
+ renderizarSecaoAscii("Comandos principais", [
608
+ "descoberta: sema inspecionar [arquivo-ou-pasta] [--json]",
609
+ "auditoria: sema drift <arquivo-ou-pasta> [--json]",
610
+ "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]",
611
+ "validacao: sema validar <arquivo-ou-pasta> [--json]",
612
+ "diagnostico: sema diagnosticos <arquivo.sema> [--json]",
613
+ "geracao: sema compilar <arquivo-ou-pasta> --alvo <python|typescript|dart|lua> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
614
+ "teste local: sema testar <arquivo.sema> --alvo <python|typescript|dart|lua> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
615
+ "verificacao final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--json]",
616
+ "formatacao: sema formatar <arquivo-ou-pasta> [--check] [--json]",
617
+ ]),
618
+ "",
619
+ renderizarSecaoAscii("Ajuda IA-first", [
620
+ "sema ajuda-ia",
621
+ "sema starter-ia",
622
+ "sema resumo <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>] [--saida <diretorio>] [--raiz] [--json]",
623
+ "sema prompt-curto <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>] [--json]",
624
+ "sema prompt-ia",
625
+ "sema prompt-ia-ui",
626
+ "sema prompt-ia-react",
627
+ "sema prompt-ia-sema-primeiro",
628
+ "sema exemplos-prompt-ia",
629
+ "sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--json]",
630
+ "sema sync-ai-entrypoints [--json]",
631
+ ]),
632
+ "",
633
+ renderizarSecaoAscii("Operacional", [
634
+ "sema doctor",
635
+ "sema --versao | --version | -v",
636
+ ]),
637
+ ].join("\n");
638
+ }
639
+
640
+ async function carregarModulos(entrada: string | undefined, cwd = process.cwd()): Promise<ContextoProjetoCarregado["modulosSelecionados"]> {
641
+ return (await carregarProjeto(entrada, cwd)).modulosSelecionados;
642
+ }
643
+
644
+ async function escreverArquivos(base: string, arquivos: Array<{ caminhoRelativo: string; conteudo: string }>): Promise<void> {
645
+ await mkdir(base, { recursive: true });
646
+ for (const arquivo of arquivos) {
647
+ const destino = path.join(base, arquivo.caminhoRelativo);
648
+ await mkdir(path.dirname(destino), { recursive: true });
649
+ await writeFile(destino, arquivo.conteudo, "utf8");
650
+ }
651
+ }
652
+
653
+ function obterOpcao(args: string[], nome: string, padrao?: string): string | undefined {
654
+ const nomes = [nome, ...Object.entries(ALIAS_OPCOES).filter(([, v]) => v === nome).map(([k]) => k)];
655
+ for (const n of nomes) {
656
+ const indice = args.findIndex((arg) => arg === n);
657
+ if (indice !== -1) return args[indice + 1] ?? padrao;
658
+ }
659
+ return padrao;
660
+ }
661
+
662
+ function possuiFlag(args: string[], nome: string): boolean {
663
+ return args.includes(nome);
664
+ }
665
+
666
+ const OPCOES_COM_VALOR = new Set([
667
+ "--template",
668
+ "--alvo", "-a",
669
+ "--saida", "-s",
670
+ "--estrutura",
671
+ "--framework",
672
+ "--namespace",
673
+ "--para",
674
+ ]);
675
+
676
+ const ALIAS_OPCOES: Record<string, string> = {
677
+ "-a": "--alvo",
678
+ "-s": "--saida",
679
+ };
680
+
681
+ function obterPosicionais(args: string[]): string[] {
682
+ const posicionais: string[] = [];
683
+ for (let indice = 0; indice < args.length; indice += 1) {
684
+ const atual = args[indice]!;
685
+ if (atual.startsWith("-")) {
686
+ if (OPCOES_COM_VALOR.has(atual)) {
687
+ indice += 1;
688
+ }
689
+ continue;
690
+ }
691
+ posicionais.push(atual);
692
+ }
693
+ return posicionais;
694
+ }
695
+
696
+ function normalizarTamanhoResumo(args: string[]): TamanhoResumoIa {
697
+ const escolhas = [
698
+ possuiFlag(args, "--micro") ? "micro" : null,
699
+ possuiFlag(args, "--curto") ? "curto" : null,
700
+ possuiFlag(args, "--medio") ? "medio" : null,
701
+ ].filter((item): item is TamanhoResumoIa => item !== null);
702
+
703
+ if (escolhas.length > 1) {
704
+ throw new Error("Use apenas uma entre as flags --micro, --curto ou --medio.");
705
+ }
706
+
707
+ return escolhas[0] ?? "curto";
708
+ }
709
+
710
+ function normalizarModoResumo(valor?: string): ModoResumoIa {
711
+ if (
712
+ valor === "resumo"
713
+ || valor === "onboarding"
714
+ || valor === "review"
715
+ || valor === "mudanca"
716
+ || valor === "bug"
717
+ || valor === "arquitetura"
718
+ ) {
719
+ return valor;
720
+ }
721
+ return "resumo";
722
+ }
723
+
724
+ function comandoDisponivel(comando: string, argumentos: string[] = ["--version"]): boolean {
725
+ const execucao = spawnSync(comando, argumentos, { stdio: "ignore", shell: process.platform === "win32" });
726
+ return (execucao.status ?? 1) === 0;
727
+ }
728
+
729
+ async function comandoDoctor(): Promise<number> {
730
+ const checks = [
731
+ { nome: "node", ok: comandoDisponivel("node") },
732
+ { nome: "npm", ok: comandoDisponivel("npm") },
733
+ { nome: "python", ok: comandoDisponivel("python") || comandoDisponivel("py") },
734
+ { nome: "dotnet", ok: comandoDisponivel("dotnet") },
735
+ { nome: "go", ok: comandoDisponivel("go") },
736
+ { nome: "cargo", ok: comandoDisponivel("cargo") },
737
+ { nome: "java", ok: comandoDisponivel("java") },
738
+ { nome: "code", ok: comandoDisponivel("code", ["--version"]) },
739
+ ];
740
+
741
+ console.log(renderizarCaixaAscii([
742
+ "Sema doctor",
743
+ "checa a toolchain minima para validar, gerar e operar a CLI",
744
+ ]));
745
+ for (const check of checks) {
746
+ console.log(`- ${check.nome}: ${check.ok ? "ok" : "ausente"}`);
747
+ }
748
+
749
+ const obrigatorios = checks.filter((check) => ["node", "npm"].includes(check.nome));
750
+ return obrigatorios.every((check) => check.ok) ? 0 : 1;
751
+ }
752
+
753
+ function validarCompatibilidadeFramework(alvo: AlvoGeracao, framework: FrameworkGeracao): string | undefined {
754
+ if (framework === "base") {
755
+ return undefined;
756
+ }
757
+ if (framework === "nestjs" && alvo !== "typescript") {
758
+ return `Framework "${framework}" so pode ser usado com o alvo typescript.`;
759
+ }
760
+ if (framework === "fastapi" && alvo !== "python") {
761
+ return `Framework "${framework}" so pode ser usado com o alvo python.`;
762
+ }
763
+ if (alvo === "dart" || alvo === "lua" || alvo === "javascript" || alvo === "html" || alvo === "css") {
764
+ return `Framework "${framework}" nao e suportado para o alvo ${alvo}.`;
765
+ }
766
+ return undefined;
767
+ }
768
+
769
+ function normalizarFonteImportacao(valor: string | undefined): FonteImportacao | undefined {
770
+ if (!valor) {
771
+ return undefined;
772
+ }
773
+ if (valor === "ts") {
774
+ return "typescript";
775
+ }
776
+ if (valor === "py") {
777
+ return "python";
778
+ }
779
+ if (valor === "nest") {
780
+ return "nestjs";
781
+ }
782
+ if (valor === "api") {
783
+ return "fastapi";
784
+ }
785
+ if (valor === "next") {
786
+ return "nextjs";
787
+ }
788
+ if (valor === "next-consumer" || valor === "nextjs-consumer") {
789
+ return "nextjs-consumer";
790
+ }
791
+ if (valor === "react-vite" || valor === "react-vite-consumer" || valor === "react-consumer") {
792
+ return "react-vite-consumer";
793
+ }
794
+ if (valor === "angular" || valor === "angular-consumer") {
795
+ return "angular-consumer";
796
+ }
797
+ if (valor === "flutter" || valor === "flutter-consumer") {
798
+ return "flutter-consumer";
799
+ }
800
+ if (valor === "fb") {
801
+ return "firebase";
802
+ }
803
+ if (valor === "csharp" || valor === "cs" || valor === "dotnet") {
804
+ return "dotnet";
805
+ }
806
+ if (valor === "java") {
807
+ return "java";
808
+ }
809
+ if (valor === "go" || valor === "golang") {
810
+ return "go";
811
+ }
812
+ if (valor === "rust" || valor === "rs") {
813
+ return "rust";
814
+ }
815
+ if (valor === "cpp" || valor === "cxx" || valor === "cc" || valor === "c++") {
816
+ return "cpp";
817
+ }
818
+ if (
819
+ valor === "nestjs"
820
+ || valor === "fastapi"
821
+ || valor === "flask"
822
+ || valor === "nextjs"
823
+ || valor === "nextjs-consumer"
824
+ || valor === "react-vite-consumer"
825
+ || valor === "angular-consumer"
826
+ || valor === "flutter-consumer"
827
+ || valor === "firebase"
828
+ || valor === "dotnet"
829
+ || valor === "java"
830
+ || valor === "go"
831
+ || valor === "rust"
832
+ || valor === "cpp"
833
+ || valor === "typescript"
834
+ || valor === "python"
835
+ || valor === "dart"
836
+ ) {
837
+ return valor;
838
+ }
839
+ return undefined;
840
+ }
841
+
842
+ function normalizarTemplateIniciar(valor?: string): TemplateIniciar {
843
+ if (
844
+ valor === "nestjs"
845
+ || valor === "fastapi"
846
+ || valor === "nextjs-api"
847
+ || valor === "nextjs-consumer"
848
+ || valor === "react-vite-consumer"
849
+ || valor === "angular-consumer"
850
+ || valor === "flutter-consumer"
851
+ || valor === "node-firebase-worker"
852
+ || valor === "aspnet-api"
853
+ || valor === "springboot-api"
854
+ || valor === "go-http-api"
855
+ || valor === "rust-axum-api"
856
+ || valor === "cpp-service-bridge"
857
+ ) {
858
+ return valor;
859
+ }
860
+ return "base";
861
+ }
862
+
863
+ function garantirIr(resultado: ReturnType<typeof compilarCodigo>, caminho: string): IrModulo {
864
+ if (!resultado.ir) {
865
+ throw new Error(`Nao foi possivel gerar IR para ${caminho}.\n${formatarDiagnosticos(resultado.diagnosticos)}`);
866
+ }
867
+ return resultado.ir;
868
+ }
869
+
870
+ function gerarArquivosPorAlvo(ir: IrModulo, alvo: AlvoGeracao, framework: FrameworkGeracao) {
871
+ if (alvo === "python") {
872
+ return gerarPython(ir, { framework });
873
+ }
874
+ if (alvo === "dart") {
875
+ return gerarDart(ir);
876
+ }
877
+ if (alvo === "lua") {
878
+ return gerarLua(ir);
879
+ }
880
+ if (alvo === "javascript") {
881
+ return gerarJavaScript(ir);
882
+ }
883
+ if (alvo === "html") {
884
+ return gerarHtml(ir);
885
+ }
886
+ if (alvo === "css") {
887
+ return gerarCss(ir);
888
+ }
889
+ return gerarTypeScript(ir, { framework });
890
+ }
891
+
892
+ function aplicarEstruturaSaida(
893
+ arquivos: Array<{ caminhoRelativo: string; conteudo: string }>,
894
+ ir: IrModulo,
895
+ estrutura: EstruturaSaida,
896
+ ): Array<{ caminhoRelativo: string; conteudo: string }> {
897
+ if (estrutura === "flat" || estrutura === "backend") {
898
+ return arquivos;
899
+ }
900
+
901
+ const estruturaModulo = descreverEstruturaModulo(ir.nome);
902
+ const pastaModulo = estruturaModulo.contextoSegmentos.join(path.sep);
903
+ const nomeArquivo = estruturaModulo.nomeArquivo;
904
+ const nomeBaseAntigo = estruturaModulo.nomeBase;
905
+
906
+ return arquivos.map((arquivo) => {
907
+ const basename = path.basename(arquivo.caminhoRelativo);
908
+ let novoBasename = basename;
909
+ let conteudo = arquivo.conteudo;
910
+
911
+ if (basename === `${nomeBaseAntigo}.ts`) {
912
+ novoBasename = `${nomeArquivo}.ts`;
913
+ } else if (basename === `${nomeBaseAntigo}.test.ts`) {
914
+ novoBasename = `${nomeArquivo}.test.ts`;
915
+ conteudo = conteudo.replace(`./${nomeBaseAntigo}.ts`, `./${nomeArquivo}.ts`);
916
+ } else if (basename === `${nomeBaseAntigo}.py`) {
917
+ novoBasename = `${nomeArquivo}.py`;
918
+ } else if (basename === `test_${nomeBaseAntigo}.py`) {
919
+ novoBasename = `test_${nomeArquivo}.py`;
920
+ conteudo = conteudo.replace(`from ${nomeBaseAntigo} import *`, `from ${nomeArquivo} import *`);
921
+ } else if (basename === `${nomeBaseAntigo}.dart`) {
922
+ novoBasename = `${nomeArquivo}.dart`;
923
+ } else if (basename === `${nomeBaseAntigo}.lua`) {
924
+ novoBasename = `${nomeArquivo}.lua`;
925
+ } else if (basename === `test_${nomeBaseAntigo}.lua`) {
926
+ novoBasename = `test_${nomeArquivo}.lua`;
927
+ conteudo = conteudo.replace(`${nomeBaseAntigo}.lua`, `${nomeArquivo}.lua`);
928
+ } else if (basename === `${nomeBaseAntigo}.js`) {
929
+ novoBasename = `${nomeArquivo}.js`;
930
+ } else if (basename === `${nomeBaseAntigo}.test.js`) {
931
+ novoBasename = `${nomeArquivo}.test.js`;
932
+ conteudo = conteudo.replace(`./${nomeBaseAntigo}.js`, `./${nomeArquivo}.js`);
933
+ } else if (basename === `${nomeBaseAntigo}.html`) {
934
+ novoBasename = `${nomeArquivo}.html`;
935
+ } else if (basename === `${nomeBaseAntigo}.css`) {
936
+ novoBasename = `${nomeArquivo}.css`;
937
+ }
938
+
939
+ return {
940
+ caminhoRelativo: pastaModulo ? path.join(pastaModulo, novoBasename) : novoBasename,
941
+ conteudo,
942
+ };
943
+ });
944
+ }
945
+
946
+ function contarCasosDeTesteGerados(alvo: AlvoGeracao, arquivos: Array<{ caminhoRelativo: string; conteudo: string }>): number {
947
+ if (alvo === "dart" || alvo === "html" || alvo === "css") {
948
+ return 0;
949
+ }
950
+ if (alvo === "lua") {
951
+ const arquivoTeste = arquivos.find((item) => path.basename(item.caminhoRelativo).startsWith("test_") && item.caminhoRelativo.endsWith(".lua"));
952
+ if (!arquivoTeste) {
953
+ return 0;
954
+ }
955
+ return (arquivoTeste.conteudo.match(/\blocal function test_/g) ?? []).length;
956
+ }
957
+
958
+ if (alvo === "typescript") {
959
+ const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.ts"));
960
+ if (!arquivoTeste) {
961
+ return 0;
962
+ }
963
+ return (arquivoTeste.conteudo.match(/\btest\(/g) ?? []).length;
964
+ }
965
+
966
+ if (alvo === "javascript") {
967
+ const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.js"));
968
+ if (!arquivoTeste) {
969
+ return 0;
970
+ }
971
+ return (arquivoTeste.conteudo.match(/\btest\(/g) ?? []).length;
972
+ }
973
+
974
+ const arquivoTeste = arquivos.find((item) => path.basename(item.caminhoRelativo).startsWith("test_"));
975
+ if (!arquivoTeste) {
976
+ return 0;
977
+ }
978
+ return (arquivoTeste.conteudo.match(/\bdef test_/g) ?? []).length;
979
+ }
980
+
981
+ function executarTestesGerados(
982
+ alvo: AlvoGeracao,
983
+ baseSaida: string,
984
+ arquivos: Array<{ caminhoRelativo: string; conteudo: string }>,
985
+ silencioso = false,
986
+ ): SaidaTesteCapturada {
987
+ const quantidadeTestes = contarCasosDeTesteGerados(alvo, arquivos);
988
+ if (quantidadeTestes === 0) {
989
+ if (!silencioso) {
990
+ const nomesAlvo: Record<AlvoGeracao, string> = { typescript: "TypeScript", python: "Python", lua: "Lua", dart: "Dart", javascript: "JavaScript", html: "HTML", css: "CSS" };
991
+ console.log(`Nenhum teste ${nomesAlvo[alvo] ?? alvo} foi gerado.`);
992
+ }
993
+ return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
994
+ }
995
+
996
+ if (alvo === "html" || alvo === "css") {
997
+ return { codigoSaida: 0, quantidadeTestes: 0, saidaPadrao: "", saidaErro: "" };
998
+ }
999
+
1000
+ if (alvo === "typescript") {
1001
+ const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.ts"))?.caminhoRelativo;
1002
+ if (!arquivoTeste) {
1003
+ if (!silencioso) {
1004
+ console.log("Nenhum teste TypeScript foi gerado.");
1005
+ }
1006
+ return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
1007
+ }
1008
+ const execucao = spawnSync("node", ["--import", "tsx", path.join(baseSaida, arquivoTeste)], {
1009
+ stdio: silencioso ? "pipe" : "inherit",
1010
+ encoding: silencioso ? "utf8" : undefined,
1011
+ });
1012
+ return {
1013
+ codigoSaida: execucao.status ?? 1,
1014
+ quantidadeTestes,
1015
+ saidaPadrao: typeof execucao.stdout === "string" ? execucao.stdout : "",
1016
+ saidaErro: typeof execucao.stderr === "string" ? execucao.stderr : "",
1017
+ };
1018
+ }
1019
+
1020
+ if (alvo === "javascript") {
1021
+ const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.js"))?.caminhoRelativo;
1022
+ if (!arquivoTeste) {
1023
+ if (!silencioso) {
1024
+ console.log("Nenhum teste JavaScript foi gerado.");
1025
+ }
1026
+ return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
1027
+ }
1028
+ const execucao = spawnSync("node", ["--test", path.join(baseSaida, arquivoTeste)], {
1029
+ stdio: silencioso ? "pipe" : "inherit",
1030
+ encoding: silencioso ? "utf8" : undefined,
1031
+ });
1032
+ return {
1033
+ codigoSaida: execucao.status ?? 1,
1034
+ quantidadeTestes,
1035
+ saidaPadrao: typeof execucao.stdout === "string" ? execucao.stdout : "",
1036
+ saidaErro: typeof execucao.stderr === "string" ? execucao.stderr : "",
1037
+ };
1038
+ }
1039
+
1040
+ if (alvo === "lua") {
1041
+ const arquivoTeste = arquivos.find((item) => path.basename(item.caminhoRelativo).startsWith("test_") && item.caminhoRelativo.endsWith(".lua"))?.caminhoRelativo;
1042
+ if (!arquivoTeste) {
1043
+ if (!silencioso) {
1044
+ console.log("Nenhum teste Lua foi gerado.");
1045
+ }
1046
+ return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
1047
+ }
1048
+ const execucao = spawnSync("lua", [arquivoTeste], {
1049
+ stdio: silencioso ? "pipe" : "inherit",
1050
+ cwd: baseSaida,
1051
+ encoding: silencioso ? "utf8" : undefined,
1052
+ shell: process.platform === "win32",
1053
+ });
1054
+ return {
1055
+ codigoSaida: execucao.status ?? 1,
1056
+ quantidadeTestes,
1057
+ saidaPadrao: typeof execucao.stdout === "string" ? execucao.stdout : "",
1058
+ saidaErro: typeof execucao.stderr === "string" ? execucao.stderr : "",
1059
+ };
1060
+ }
1061
+
1062
+ const arquivoTeste = arquivos.find((item) => path.basename(item.caminhoRelativo).startsWith("test_"))?.caminhoRelativo;
1063
+ if (!arquivoTeste) {
1064
+ if (!silencioso) {
1065
+ console.log("Nenhum teste Python foi gerado.");
1066
+ }
1067
+ return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
1068
+ }
1069
+ const execucao = spawnSync("pytest", [arquivoTeste], {
1070
+ stdio: silencioso ? "pipe" : "inherit",
1071
+ cwd: baseSaida,
1072
+ encoding: silencioso ? "utf8" : undefined,
1073
+ });
1074
+ return {
1075
+ codigoSaida: execucao.status ?? 1,
1076
+ quantidadeTestes,
1077
+ saidaPadrao: typeof execucao.stdout === "string" ? execucao.stdout : "",
1078
+ saidaErro: typeof execucao.stderr === "string" ? execucao.stderr : "",
1079
+ };
1080
+ }
1081
+
1082
+ function resolverConfiguracaoVerificacaoPorAlvo(
1083
+ alvo: AlvoGeracao,
1084
+ configCarregada?: Awaited<ReturnType<typeof carregarConfiguracaoProjeto>>,
1085
+ ): {
1086
+ framework: FrameworkGeracao;
1087
+ estrutura: EstruturaSaida;
1088
+ incompatibilidade?: string;
1089
+ } {
1090
+ const framework = resolverFrameworkPadrao(undefined, configCarregada);
1091
+ const incompatibilidade = validarCompatibilidadeFramework(alvo, framework);
1092
+ const estrutura = resolverEstruturaSaidaPadrao(undefined, framework, configCarregada);
1093
+
1094
+ return {
1095
+ framework,
1096
+ estrutura,
1097
+ incompatibilidade,
1098
+ };
1099
+ }
1100
+
1101
+ function executarTestesParaVerificacao(
1102
+ alvo: AlvoGeracao,
1103
+ baseSaida: string,
1104
+ arquivos: Array<{ caminhoRelativo: string; conteudo: string }>,
1105
+ framework: FrameworkGeracao,
1106
+ silencioso = false,
1107
+ ): {
1108
+ execucao: SaidaTesteCapturada;
1109
+ testesExecutados: boolean;
1110
+ } {
1111
+ if (framework !== "base") {
1112
+ return {
1113
+ execucao: {
1114
+ codigoSaida: 0,
1115
+ quantidadeTestes: 0,
1116
+ saidaPadrao: "",
1117
+ saidaErro: "",
1118
+ },
1119
+ testesExecutados: false,
1120
+ };
1121
+ }
1122
+
1123
+ return {
1124
+ execucao: executarTestesGerados(alvo, baseSaida, arquivos, silencioso),
1125
+ testesExecutados: true,
1126
+ };
1127
+ }
1128
+
1129
+ function nomeSubpastaModulo(caminhoArquivo: string): string {
1130
+ return path.basename(caminhoArquivo, ".sema");
1131
+ }
1132
+
1133
+ async function caminhoExiste(caminhoAlvo: string): Promise<boolean> {
1134
+ try {
1135
+ await stat(caminhoAlvo);
1136
+ return true;
1137
+ } catch {
1138
+ return false;
1139
+ }
1140
+ }
1141
+
1142
+ async function descobrirDocsIa(): Promise<DescobertaDocsIa> {
1143
+ const candidatosBase = [];
1144
+ let atual = DIRETORIO_CLI_ATUAL;
1145
+
1146
+ for (let tentativas = 0; tentativas < 8; tentativas += 1) {
1147
+ candidatosBase.push(atual);
1148
+ const proximo = path.dirname(atual);
1149
+ if (proximo === atual) {
1150
+ break;
1151
+ }
1152
+ atual = proximo;
1153
+ }
1154
+
1155
+ const nomesDocs = [
1156
+ "AGENT_STARTER.md",
1157
+ "como-ensinar-a-sema-para-ia.md",
1158
+ "prompt-base-ia-sema.md",
1159
+ "fluxo-pratico-ia-sema.md",
1160
+ ];
1161
+
1162
+ for (const base of candidatosBase) {
1163
+ const documentos = [];
1164
+ let encontrouTodos = true;
1165
+
1166
+ for (const nome of nomesDocs) {
1167
+ const caminhoDoc = path.join(base, "docs", nome);
1168
+ if (!(await caminhoExiste(caminhoDoc))) {
1169
+ encontrouTodos = false;
1170
+ break;
1171
+ }
1172
+ documentos.push({ nome, caminho: caminhoDoc });
1173
+ }
1174
+
1175
+ if (encontrouTodos) {
1176
+ return {
1177
+ origemInstalacao: DIRETORIO_CLI_ATUAL,
1178
+ baseDetectada: base,
1179
+ documentos,
1180
+ };
1181
+ }
1182
+ }
1183
+
1184
+ return {
1185
+ origemInstalacao: DIRETORIO_CLI_ATUAL,
1186
+ baseDetectada: null,
1187
+ documentos: [],
1188
+ };
1189
+ }
1190
+
1191
+ function renderizarCabecalhoDocsIa(descoberta: DescobertaDocsIa): string {
1192
+ const documentos = descoberta.documentos.map((documento) => `\`${documento.nome}\``);
1193
+ const linhas = [
1194
+ "Modo IA-first da instalacao atual",
1195
+ "- Use `sema` como interface publica principal.",
1196
+ "- A Sema entra em projeto novo, projeto ja semantizado e adocao incremental em legado sem contrato inicial.",
1197
+ "- Nao assuma monorepo, `node pacotes/cli/dist/index.js`, `npm run project:check` ou uma pasta `exemplos` externa ao projeto atual.",
1198
+ "- Se a IA tiver contexto curto, comece por `sema resumo` e `sema prompt-curto`.",
1199
+ "- Se a IA aguentar mais contexto, suba para `sema drift --json` e `sema contexto-ia`.",
1200
+ "- So leia `ast.json` e `ir.json` completos quando a capacidade da IA realmente aguentar esse volume.",
1201
+ ];
1202
+
1203
+ if (documentos.length > 0) {
1204
+ linhas.push(`- Documentos locais empacotados: ${documentos.join(", ")}.`);
1205
+ } else {
1206
+ linhas.push("- Documentos locais empacotados: nenhum extra detectado. Siga a CLI, o contrato atual e os artefatos JSON.");
1207
+ }
1208
+
1209
+ return linhas.join("\n");
1210
+ }
1211
+
1212
+ function unicos<T>(itens: T[]): T[] {
1213
+ return [...new Set(itens)];
1214
+ }
1215
+
1216
+ function unicosOrdenados(itens: string[]): string[] {
1217
+ return unicos(itens).sort((a, b) => a.localeCompare(b, "pt-BR"));
1218
+ }
1219
+
1220
+ function limitarLista(itens: string[], limite: number): string[] {
1221
+ return itens.slice(0, limite);
1222
+ }
1223
+
1224
+ function resumirListaTexto(itens: string[], limite: number, padrao = "nenhum"): string {
1225
+ if (itens.length === 0) {
1226
+ return padrao;
1227
+ }
1228
+ const visiveis = itens.slice(0, limite);
1229
+ const restante = itens.length - visiveis.length;
1230
+ return restante > 0 ? `${visiveis.join(", ")} (+${restante})` : visiveis.join(", ");
1231
+ }
1232
+
1233
+ function normalizarIdentificadorResumo(valor: string): string {
1234
+ return valor.replace(/[._]/g, " ").replace(/\s+/g, " ").trim();
1235
+ }
1236
+
1237
+ function resumirCamposTask(
1238
+ task: { nome: string; input?: Array<{ nome: string }>; output?: Array<{ nome: string }> },
1239
+ campo: "input" | "output",
1240
+ limiteCampos: number,
1241
+ ): string {
1242
+ const campos = (task[campo] ?? []).map((item) => item.nome).slice(0, limiteCampos);
1243
+ if (campos.length === 0) {
1244
+ return `${task.nome}(-)`;
1245
+ }
1246
+ return `${task.nome}(${campos.join(", ")})`;
1247
+ }
1248
+
1249
+ function formatarEfeitoSemanticoResumido(
1250
+ efeito: { categoria: string; alvo: string; criticidade?: string; detalhe?: string; textoOriginal?: string },
1251
+ ): string {
1252
+ if (efeito.textoOriginal) {
1253
+ return efeito.textoOriginal;
1254
+ }
1255
+ const partes = [`${efeito.categoria} ${efeito.alvo}`];
1256
+ if (efeito.criticidade) {
1257
+ partes.push(`criticidade=${efeito.criticidade}`);
1258
+ }
1259
+ if (efeito.detalhe) {
1260
+ partes.push(efeito.detalhe);
1261
+ }
1262
+ return partes.join(" ");
1263
+ }
1264
+
1265
+ function calcularRiscoOperacionalResumo(resumoDrift: ResumoModuloDrift): string {
1266
+ if (resumoDrift.tasks.some((task) => task.riscoOperacional === "alto")) {
1267
+ return "alto";
1268
+ }
1269
+ if (resumoDrift.tasks.some((task) => task.riscoOperacional === "medio")) {
1270
+ return "medio";
1271
+ }
1272
+ return "baixo";
1273
+ }
1274
+
1275
+ function descreverFazModulo(ir: IrModulo | null, modulo: string): string {
1276
+ if (!ir) {
1277
+ return `governa o modulo ${normalizarIdentificadorResumo(modulo)}`;
1278
+ }
1279
+
1280
+ const partes: string[] = [];
1281
+ if (ir.routes.length > 0) {
1282
+ partes.push(`${ir.routes.length} rota(s)`);
1283
+ }
1284
+ if (ir.superficies.length > 0) {
1285
+ partes.push(`${ir.superficies.length} superficie(s)`);
1286
+ }
1287
+ if (ir.tasks.length > 0) {
1288
+ partes.push(`${ir.tasks.length} task(s)`);
1289
+ }
1290
+
1291
+ const foco = ir.routes[0]?.nome ?? ir.superficies[0]?.nome ?? ir.tasks[0]?.nome ?? modulo;
1292
+ return partes.length > 0
1293
+ ? `governa ${partes.join(", ")} com foco em ${normalizarIdentificadorResumo(foco)}`
1294
+ : `governa o modulo ${normalizarIdentificadorResumo(modulo)}`;
1295
+ }
1296
+
1297
+ function criarGuiaCapacidadeIa(): Record<CapacidadeIa, GuiaCapacidadeIa> {
1298
+ return {
1299
+ pequena: {
1300
+ descricao: "IA gratuita ou com contexto curto. Leia so o cartao semantico e o briefing minimo.",
1301
+ artefatos: ["resumo.micro.txt", "briefing.min.json", "prompt-curto.txt"],
1302
+ ordemLeitura: ["resumo.micro.txt", "briefing.min.json", "resumo.curto.txt"],
1303
+ evitar: ["ast.json", "ir.json", "diagnosticos.json"],
1304
+ },
1305
+ media: {
1306
+ descricao: "IA com contexto medio. Aguenta resumo expandido, briefing minimo e drift.",
1307
+ artefatos: ["resumo.curto.txt", "briefing.min.json", "drift.json", "prompt-curto.txt"],
1308
+ ordemLeitura: ["resumo.curto.txt", "briefing.min.json", "drift.json", "resumo.md"],
1309
+ evitar: ["ast.json"],
1310
+ },
1311
+ grande: {
1312
+ descricao: "IA com contexto grande ou tool use. Pode consumir o pacote completo.",
1313
+ artefatos: ["README.md", "resumo.md", "briefing.json", "drift.json", "ir.json", "ast.json"],
1314
+ ordemLeitura: ["README.md", "resumo.md", "briefing.json", "drift.json", "ir.json", "ast.json"],
1315
+ evitar: [],
1316
+ },
1317
+ };
1318
+ }
1319
+
1320
+ function coletarResumoSemanticoModulo(
1321
+ contexto: Pick<PacoteContextoModuloIa, "arquivo" | "modulo" | "geradoEm" | "ir" | "briefing" | "drift">,
1322
+ ): ResumoSemanticoModuloIa {
1323
+ const { arquivo, modulo, geradoEm, ir, briefing, drift } = contexto;
1324
+ const tarefas = ir?.tasks ?? [];
1325
+ const rotas = ir?.routes ?? [];
1326
+ const superficies = ir?.superficies ?? [];
1327
+ const regrasCriticas = unicosOrdenados([
1328
+ ...tarefas.flatMap((task) => task.rules),
1329
+ ...tarefas.flatMap((task) => task.guarantees),
1330
+ ]);
1331
+ const efeitos = unicosOrdenados([
1332
+ ...tarefas.flatMap((task) => task.effects),
1333
+ ...rotas.flatMap((route) => route.efeitosPublicos.map((efeito) => formatarEfeitoSemanticoResumido(efeito))),
1334
+ ...superficies.flatMap((superficie) => superficie.effects.map((efeito) => formatarEfeitoSemanticoResumido(efeito))),
1335
+ ]);
1336
+ const erros = unicosOrdenados([
1337
+ ...tarefas.flatMap((task) => Object.keys(task.errors)),
1338
+ ...tarefas.flatMap((task) => task.errosDetalhados.map((erro) => erro.codigo)),
1339
+ ...rotas.flatMap((route) => route.errosPublicos.map((erro) => erro.codigo)),
1340
+ ]);
1341
+ const entidadesAfetadas = unicosOrdenados([
1342
+ ...(ir?.resumoAgente.entidadesAfetadas ?? []),
1343
+ ...tarefas.flatMap((task) => task.resumoAgente.entidadesAfetadas),
1344
+ ...rotas.flatMap((route) => route.resumoAgente.entidadesAfetadas),
1345
+ ...superficies.flatMap((superficie) => superficie.resumoAgente.entidadesAfetadas),
1346
+ ]);
1347
+
1348
+ return {
1349
+ geradoEm,
1350
+ arquivo,
1351
+ modulo,
1352
+ perfilCompatibilidade: ir?.perfilCompatibilidade ?? briefing.perfilCompatibilidade,
1353
+ scoreSemantico: briefing.scoreSemantico,
1354
+ confiancaGeral: briefing.confiancaGeral,
1355
+ riscoOperacional: calcularRiscoOperacionalResumo(drift.resumo),
1356
+ faz: descreverFazModulo(ir, modulo),
1357
+ tarefasPrincipais: limitarLista(tarefas.map((task) => task.nome), 6),
1358
+ entradasChave: limitarLista(tarefas.map((task) => resumirCamposTask(task, "input", 4)), 4),
1359
+ saidasChave: limitarLista(tarefas.map((task) => resumirCamposTask(task, "output", 4)), 4),
1360
+ superficiesPublicas: limitarLista(unicosOrdenados([
1361
+ ...briefing.superficiesImpactadas,
1362
+ ...rotas.map((route) => `${route.metodo ?? "?"} ${route.caminho ?? route.nome}`),
1363
+ ]), 8),
1364
+ regrasCriticas: limitarLista(regrasCriticas, 8),
1365
+ efeitos: limitarLista(efeitos, 8),
1366
+ erros: limitarLista(erros, 8),
1367
+ entidadesAfetadas: limitarLista(entidadesAfetadas, 8),
1368
+ arquivosProvaveis: limitarLista(unicosOrdenados(briefing.oQueTocar), 8),
1369
+ simbolosRelacionados: limitarLista(unicosOrdenados(briefing.simbolosRelacionados), 8),
1370
+ riscosPrincipais: limitarLista(unicosOrdenados(briefing.riscosPrincipais), 6),
1371
+ lacunas: limitarLista(unicosOrdenados(briefing.oQueEstaFrouxo), 6),
1372
+ inferido: limitarLista(unicosOrdenados(briefing.oQueFoiInferido), 6),
1373
+ checksSugeridos: limitarLista(unicosOrdenados(briefing.oQueValidar), 6),
1374
+ testesMinimos: limitarLista(unicosOrdenados(briefing.testesMinimos), 6),
1375
+ consumerFramework: briefing.consumerFramework ?? drift.drift.consumerFramework ?? null,
1376
+ appRoutes: limitarLista(unicosOrdenados(briefing.appRoutes ?? drift.drift.appRoutes ?? []), 8),
1377
+ consumerSurfaces: limitarLista(unicosOrdenados(briefing.consumerSurfaces ?? []), 8),
1378
+ consumerBridges: limitarLista(unicosOrdenados(briefing.consumerBridges ?? []), 8),
1379
+ arquivosProvaveisEditar: limitarLista(unicosOrdenados(briefing.arquivosProvaveisEditar ?? briefing.oQueTocar), 8),
1380
+ };
1381
+ }
1382
+
1383
+ function renderizarResumoModuloTexto(
1384
+ resumo: ResumoSemanticoModuloIa,
1385
+ tamanho: TamanhoResumoIa,
1386
+ modo: ModoResumoIa,
1387
+ ): string {
1388
+ const limite = tamanho === "micro" ? 2 : tamanho === "curto" ? 4 : 6;
1389
+ const linhas = [
1390
+ `MODO: ${modo}`,
1391
+ `MODULO: ${resumo.modulo}`,
1392
+ `FAZ: ${resumo.faz}`,
1393
+ `PERFIL: ${resumo.perfilCompatibilidade}`,
1394
+ `CONSUMER_FRAMEWORK: ${resumo.consumerFramework ?? "nenhum"}`,
1395
+ `APP_ROUTES: ${resumirListaTexto(resumo.appRoutes, limite)}`,
1396
+ `CONSUMER_SURFACES: ${resumirListaTexto(resumo.consumerSurfaces, limite)}`,
1397
+ `CONSUMER_BRIDGES: ${resumirListaTexto(resumo.consumerBridges, limite)}`,
1398
+ `PUBLICO: ${resumirListaTexto(resumo.superficiesPublicas, limite)}`,
1399
+ `TAREFAS: ${resumirListaTexto(resumo.tarefasPrincipais, limite)}`,
1400
+ `ENTRADAS: ${resumirListaTexto(resumo.entradasChave, limite)}`,
1401
+ `SAIDAS: ${resumirListaTexto(resumo.saidasChave, limite)}`,
1402
+ `REGRAS: ${resumirListaTexto(resumo.regrasCriticas, limite)}`,
1403
+ `EFEITOS: ${resumirListaTexto(resumo.efeitos, limite)}`,
1404
+ `ERROS: ${resumirListaTexto(resumo.erros, limite)}`,
1405
+ `TOCAR: ${resumirListaTexto(resumo.arquivosProvaveis, limite)}`,
1406
+ `VALIDAR: ${resumirListaTexto(resumo.checksSugeridos, limite)}`,
1407
+ `TESTES: ${resumirListaTexto(resumo.testesMinimos, limite)}`,
1408
+ `RISCOS: ${resumirListaTexto(resumo.riscosPrincipais, limite)}`,
1409
+ `LACUNAS: ${resumirListaTexto(resumo.lacunas, limite)}`,
1410
+ `INFERIDO: ${resumirListaTexto(resumo.inferido, limite)}`,
1411
+ `CONFIANCA: ${resumo.confiancaGeral}`,
1412
+ `RISCO_OPERACIONAL: ${resumo.riscoOperacional}`,
1413
+ `SCORE: ${resumo.scoreSemantico}`,
1414
+ `GERADO_EM: ${resumo.geradoEm}`,
1415
+ ];
1416
+
1417
+ if (tamanho === "micro") {
1418
+ return `${linhas.slice(0, 12).join("\n")}\n`;
1419
+ }
1420
+
1421
+ return `${linhas.join("\n")}\n`;
1422
+ }
1423
+
1424
+ function renderizarResumoModuloMarkdown(
1425
+ resumo: ResumoSemanticoModuloIa,
1426
+ modo: ModoResumoIa,
1427
+ guiaPorCapacidade: Record<CapacidadeIa, GuiaCapacidadeIa>,
1428
+ ): string {
1429
+ const linhas = [
1430
+ `# Resumo Sema para ${resumo.modulo}`,
1431
+ "",
1432
+ `- Modo: \`${modo}\``,
1433
+ `- Gerado em: \`${resumo.geradoEm}\``,
1434
+ `- Arquivo: \`${resumo.arquivo}\``,
1435
+ `- Perfil: \`${resumo.perfilCompatibilidade}\``,
1436
+ `- Score: \`${resumo.scoreSemantico}\``,
1437
+ `- Confianca: \`${resumo.confiancaGeral}\``,
1438
+ `- Risco operacional: \`${resumo.riscoOperacional}\``,
1439
+ "",
1440
+ "## O que este modulo faz",
1441
+ "",
1442
+ `- ${resumo.faz}`,
1443
+ `- Superficies publicas: ${resumirListaTexto(resumo.superficiesPublicas, 8)}`,
1444
+ `- Tarefas principais: ${resumirListaTexto(resumo.tarefasPrincipais, 8)}`,
1445
+ "",
1446
+ "## Contrato util para IA",
1447
+ "",
1448
+ `- Entradas chave: ${resumirListaTexto(resumo.entradasChave, 6)}`,
1449
+ `- Saidas chave: ${resumirListaTexto(resumo.saidasChave, 6)}`,
1450
+ `- Regras criticas: ${resumirListaTexto(resumo.regrasCriticas, 6)}`,
1451
+ `- Efeitos: ${resumirListaTexto(resumo.efeitos, 6)}`,
1452
+ `- Erros: ${resumirListaTexto(resumo.erros, 6)}`,
1453
+ `- Entidades afetadas: ${resumirListaTexto(resumo.entidadesAfetadas, 6)}`,
1454
+ "",
1455
+ ...(resumo.consumerFramework
1456
+ ? [
1457
+ "## Consumer IA-first",
1458
+ "",
1459
+ `- Framework consumer: ${resumo.consumerFramework}`,
1460
+ `- Rotas de app: ${resumirListaTexto(resumo.appRoutes, 6)}`,
1461
+ `- Superficies consumer: ${resumirListaTexto(resumo.consumerSurfaces, 6)}`,
1462
+ `- Bridges consumer: ${resumirListaTexto(resumo.consumerBridges, 6)}`,
1463
+ "",
1464
+ ]
1465
+ : []),
1466
+ "## Intervencao segura",
1467
+ "",
1468
+ `- Arquivos provaveis: ${resumirListaTexto(resumo.arquivosProvaveis, 6)}`,
1469
+ `- Simbolos relacionados: ${resumirListaTexto(resumo.simbolosRelacionados, 6)}`,
1470
+ `- Riscos principais: ${resumirListaTexto(resumo.riscosPrincipais, 6)}`,
1471
+ `- Lacunas: ${resumirListaTexto(resumo.lacunas, 6)}`,
1472
+ `- O que foi inferido: ${resumirListaTexto(resumo.inferido, 6)}`,
1473
+ `- Checks sugeridos: ${resumirListaTexto(resumo.checksSugeridos, 6)}`,
1474
+ `- Testes minimos: ${resumirListaTexto(resumo.testesMinimos, 6)}`,
1475
+ "",
1476
+ "## Guia por capacidade de IA",
1477
+ "",
1478
+ ];
1479
+
1480
+ for (const capacidade of ["pequena", "media", "grande"] as const) {
1481
+ const guia = guiaPorCapacidade[capacidade];
1482
+ linhas.push(`### ${capacidade}`);
1483
+ linhas.push("");
1484
+ linhas.push(`- ${guia.descricao}`);
1485
+ linhas.push(`- Artefatos: ${guia.artefatos.map((item) => `\`${item}\``).join(", ")}`);
1486
+ linhas.push(`- Ordem de leitura: ${guia.ordemLeitura.map((item) => `\`${item}\``).join(" -> ")}`);
1487
+ linhas.push(`- Evitar: ${guia.evitar.length > 0 ? guia.evitar.map((item) => `\`${item}\``).join(", ") : "nada obrigatorio"}`);
1488
+ linhas.push("");
1489
+ }
1490
+
1491
+ return `${linhas.join("\n").trim()}\n`;
1492
+ }
1493
+
1494
+ function criarBriefingMinimo(
1495
+ resumo: ResumoSemanticoModuloIa,
1496
+ modo: ModoResumoIa,
1497
+ tamanho: TamanhoResumoIa,
1498
+ ): Record<string, unknown> {
1499
+ return {
1500
+ comando: "briefing-minimo",
1501
+ geradoEm: resumo.geradoEm,
1502
+ cliVersao: VERSAO_CLI,
1503
+ modo,
1504
+ tamanho,
1505
+ arquivo: resumo.arquivo,
1506
+ modulo: resumo.modulo,
1507
+ perfilCompatibilidade: resumo.perfilCompatibilidade,
1508
+ scoreSemantico: resumo.scoreSemantico,
1509
+ confiancaGeral: resumo.confiancaGeral,
1510
+ riscoOperacional: resumo.riscoOperacional,
1511
+ faz: resumo.faz,
1512
+ publico: resumo.superficiesPublicas,
1513
+ tarefasPrincipais: resumo.tarefasPrincipais,
1514
+ entradasChave: resumo.entradasChave,
1515
+ saidasChave: resumo.saidasChave,
1516
+ regrasCriticas: resumo.regrasCriticas,
1517
+ efeitos: resumo.efeitos,
1518
+ erros: resumo.erros,
1519
+ arquivosProvaveis: resumo.arquivosProvaveis,
1520
+ arquivosProvaveisEditar: resumo.arquivosProvaveisEditar,
1521
+ simbolosRelacionados: resumo.simbolosRelacionados,
1522
+ riscosPrincipais: resumo.riscosPrincipais,
1523
+ lacunas: resumo.lacunas,
1524
+ inferido: resumo.inferido,
1525
+ checksSugeridos: resumo.checksSugeridos,
1526
+ testesMinimos: resumo.testesMinimos,
1527
+ consumerFramework: resumo.consumerFramework,
1528
+ appRoutes: resumo.appRoutes,
1529
+ consumerSurfaces: resumo.consumerSurfaces,
1530
+ consumerBridges: resumo.consumerBridges,
1531
+ };
1532
+ }
1533
+
1534
+ function criarPromptCurtoModulo(
1535
+ resumo: ResumoSemanticoModuloIa,
1536
+ modo: ModoResumoIa,
1537
+ tamanho: TamanhoResumoIa,
1538
+ capacidade: CapacidadeIa,
1539
+ ): string {
1540
+ const resumoTexto = renderizarResumoModuloTexto(resumo, tamanho, modo).trim();
1541
+ return `Voce esta operando Sema em modo IA-first.
1542
+
1543
+ Esta linguagem nao foi desenhada para agradar humano; ela existe para reduzir ambiguidade para IA.
1544
+
1545
+ Capacidade alvo: ${capacidade}
1546
+ Modo da tarefa: ${modo}
1547
+
1548
+ Regras:
1549
+ - nao invente sintaxe nem bloco fora da gramatica oficial
1550
+ - preserve a intencao do contrato
1551
+ - use este resumo como fonte compacta inicial
1552
+ - se a tarefa pedir mais contexto, suba para \`briefing.min.json\`, \`drift.json\` e depois \`ir.json\`
1553
+ - nao saia editando software vivo sem olhar risco, lacuna e checks sugeridos
1554
+ ${resumo.consumerFramework ? "- se for tarefa visual consumer, priorize `appRoutes`, `consumerSurfaces` e `consumerBridges` antes de abrir arquivos aleatorios" : ""}
1555
+
1556
+ Contexto compacto:
1557
+ ${resumoTexto}
1558
+ `;
1559
+ }
1560
+
1561
+ function renderizarResumoProjetoMarkdown(
1562
+ geradoEm: string,
1563
+ modulos: ResumoSemanticoModuloIa[],
1564
+ guiaPorCapacidade: Record<CapacidadeIa, GuiaCapacidadeIa>,
1565
+ ): string {
1566
+ const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
1567
+ const linhas = [
1568
+ "# SEMA_BRIEF",
1569
+ "",
1570
+ "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.",
1571
+ "",
1572
+ `- Gerado em: \`${geradoEm}\``,
1573
+ `- Modulos: \`${modulos.length}\``,
1574
+ "",
1575
+ "## Entrada canonica para IA",
1576
+ "",
1577
+ `- Ordem minima: ${entradaCanonica.ordemLeitura.join(" -> ")}`,
1578
+ `- IA pequena: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
1579
+ `- IA media: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
1580
+ `- IA grande: ${entradaCanonica.porCapacidade.grande.join(" -> ")}`,
1581
+ "",
1582
+ "## Guia por capacidade",
1583
+ "",
1584
+ ];
1585
+
1586
+ for (const capacidade of ["pequena", "media", "grande"] as const) {
1587
+ const guia = guiaPorCapacidade[capacidade];
1588
+ linhas.push(`- ${capacidade}: ${guia.descricao} Artefatos: ${guia.artefatos.join(", ")}.`);
1589
+ }
1590
+
1591
+ linhas.push("");
1592
+ linhas.push("## Modulos");
1593
+ linhas.push("");
1594
+
1595
+ for (const modulo of modulos) {
1596
+ linhas.push(`### ${modulo.modulo}`);
1597
+ linhas.push(`- Faz: ${modulo.faz}`);
1598
+ linhas.push(`- Publico: ${resumirListaTexto(modulo.superficiesPublicas, 4)}`);
1599
+ linhas.push(`- Tocar: ${resumirListaTexto(modulo.arquivosProvaveis, 4)}`);
1600
+ linhas.push(`- Score: ${modulo.scoreSemantico} | Confianca: ${modulo.confiancaGeral} | Risco: ${modulo.riscoOperacional}`);
1601
+ linhas.push(`- Lacunas: ${resumirListaTexto(modulo.lacunas, 4)}`);
1602
+ linhas.push("");
1603
+ }
1604
+
1605
+ return `${linhas.join("\n").trim()}\n`;
1606
+ }
1607
+
1608
+ function criarEntradaCanonicaProjeto(guiaPorCapacidade: Record<CapacidadeIa, GuiaCapacidadeIa>) {
1609
+ return {
1610
+ 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.",
1611
+ ordemLeitura: [...ARQUIVOS_CANONICOS_IA_RAIZ],
1612
+ porCapacidade: {
1613
+ pequena: ["llms.txt", "SEMA_BRIEF.micro.txt", "SEMA_INDEX.json", "AGENTS.md"],
1614
+ media: ["llms.txt", "SEMA_BRIEF.curto.txt", "SEMA_INDEX.json", "AGENTS.md", "README.md"],
1615
+ grande: ["llms-full.txt", "SEMA_BRIEF.md", "SEMA_INDEX.json", "AGENTS.md", "README.md"],
1616
+ },
1617
+ docsSuporte: [...DOCUMENTOS_SUPORTE_IA],
1618
+ guiaPorCapacidade,
1619
+ };
1620
+ }
1621
+
1622
+ function falharContextoIa(mensagem: string): never {
1623
+ throw new Error(mensagem);
1624
+ }
1625
+
1626
+ function garantirArquivoSema(caminhoArquivo: string): void {
1627
+ if (!caminhoArquivo.toLowerCase().endsWith(".sema")) {
1628
+ falharContextoIa("O caminho informado precisa apontar para um arquivo .sema.");
1629
+ }
1630
+ }
1631
+
1632
+ function resumirDriftPorModulo(
1633
+ modulo: string | null,
1634
+ caminho: string,
1635
+ resultadoDrift: Awaited<ReturnType<typeof analisarDriftLegado>>,
1636
+ ) {
1637
+ const tasks = modulo
1638
+ ? resultadoDrift.tasks.filter((task) => task.modulo === modulo)
1639
+ : [];
1640
+ const implsValidos = modulo
1641
+ ? resultadoDrift.impls_validos.filter((impl) => impl.modulo === modulo)
1642
+ : [];
1643
+ const implsQuebrados = modulo
1644
+ ? resultadoDrift.impls_quebrados.filter((impl) => impl.modulo === modulo)
1645
+ : [];
1646
+ const vinculosValidos = modulo
1647
+ ? resultadoDrift.vinculos_validos.filter((vinculo) => vinculo.modulo === modulo)
1648
+ : [];
1649
+ const vinculosQuebrados = modulo
1650
+ ? resultadoDrift.vinculos_quebrados.filter((vinculo) => vinculo.modulo === modulo)
1651
+ : [];
1652
+ const rotasDivergentes = modulo
1653
+ ? resultadoDrift.rotas_divergentes.filter((rota) => rota.modulo === modulo)
1654
+ : [];
1655
+ const recursosValidos = modulo
1656
+ ? resultadoDrift.recursos_validos.filter((recurso) => recurso.modulo === modulo)
1657
+ : [];
1658
+ const recursosDivergentes = modulo
1659
+ ? resultadoDrift.recursos_divergentes.filter((recurso) => recurso.modulo === modulo)
1660
+ : [];
1661
+ const vinculosModulo = modulo
1662
+ ? [
1663
+ ...resultadoDrift.vinculos_validos.filter((vinculo) => vinculo.modulo === modulo),
1664
+ ...resultadoDrift.vinculos_quebrados.filter((vinculo) => vinculo.modulo === modulo),
1665
+ ]
1666
+ : [];
1667
+ const rotasConsumerModulo = new Set(
1668
+ vinculosModulo
1669
+ .filter((vinculo) => vinculo.tipo === "superficie")
1670
+ .map((vinculo) => vinculo.valor),
1671
+ );
1672
+ const arquivosRelacionados = [...new Set([
1673
+ ...tasks.flatMap((task) => task.arquivosReferenciados),
1674
+ ...tasks.flatMap((task) => task.arquivosProvaveisEditar),
1675
+ ...implsValidos.map((impl) => impl.arquivo).filter((item): item is string => Boolean(item)),
1676
+ ...implsQuebrados.flatMap((impl) => impl.candidatos?.map((candidato) => candidato.arquivo) ?? []),
1677
+ ...vinculosValidos.map((vinculo) => vinculo.arquivo).filter((item): item is string => Boolean(item)),
1678
+ ...recursosValidos.map((recurso) => recurso.arquivo).filter(Boolean),
1679
+ ...recursosDivergentes.map((recurso) => recurso.arquivo).filter(Boolean),
1680
+ ])].sort((a, b) => a.localeCompare(b, "pt-BR"));
1681
+ const consumerSurfaces = resultadoDrift.consumerSurfaces
1682
+ .filter((surface) =>
1683
+ arquivosRelacionados.includes(surface.arquivo)
1684
+ || rotasConsumerModulo.has(surface.rota))
1685
+ .map((surface) => `${surface.tipoArquivo}:${surface.rota} -> ${surface.arquivo}`)
1686
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1687
+ const consumerBridges = resultadoDrift.consumerBridges
1688
+ .filter((bridge) => arquivosRelacionados.includes(bridge.arquivo))
1689
+ .map((bridge) => bridge.caminho)
1690
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1691
+ const appRoutes = [...new Set(resultadoDrift.consumerSurfaces
1692
+ .filter((surface) =>
1693
+ arquivosRelacionados.includes(surface.arquivo)
1694
+ || rotasConsumerModulo.has(surface.rota))
1695
+ .map((surface) => surface.rota))]
1696
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1697
+ const consumerFramework = appRoutes.length > 0 || consumerBridges.length > 0
1698
+ ? resultadoDrift.consumerFramework
1699
+ : null;
1700
+
1701
+ return {
1702
+ caminho,
1703
+ modulo,
1704
+ implsValidos: implsValidos.length,
1705
+ implsQuebrados: implsQuebrados.length,
1706
+ vinculosValidos: vinculosValidos.length,
1707
+ vinculosQuebrados: vinculosQuebrados.length,
1708
+ recursosValidos: recursosValidos.length,
1709
+ recursosDivergentesCount: recursosDivergentes.length,
1710
+ tasksSemImplementacao: tasks.filter((task) => task.semImplementacao).length,
1711
+ scoreMedio: tasks.length > 0 ? Math.round(tasks.reduce((total, task) => total + task.scoreSemantico, 0) / tasks.length) : 0,
1712
+ confiancaGeral: tasks.some((task) => task.confiancaVinculo === "alta")
1713
+ ? "alta"
1714
+ : tasks.some((task) => task.confiancaVinculo === "media")
1715
+ ? "media"
1716
+ : "baixa",
1717
+ arquivosRelacionados,
1718
+ arquivosProvaveisEditar: arquivosRelacionados,
1719
+ consumerFramework,
1720
+ appRoutes,
1721
+ consumerSurfaces,
1722
+ consumerBridges,
1723
+ checksSugeridos: [...new Set(tasks.flatMap((task) => task.checksSugeridos))],
1724
+ lacunas: [...new Set(tasks.flatMap((task) => task.lacunas))],
1725
+ tasks,
1726
+ rotasDivergentes,
1727
+ recursosDivergentes,
1728
+ vinculosQuebradosDetalhes: vinculosQuebrados,
1729
+ };
1730
+ }
1731
+
1732
+ function criarBriefingAgente(
1733
+ arquivo: string,
1734
+ modulo: string,
1735
+ ir: IrModulo | null,
1736
+ resumoDrift: ReturnType<typeof resumirDriftPorModulo>,
1737
+ resultadoDrift: Awaited<ReturnType<typeof analisarDriftLegado>>,
1738
+ ) {
1739
+ const tarefasModulo = resultadoDrift.tasks.filter((task) => task.modulo === modulo);
1740
+ return {
1741
+ arquivo,
1742
+ modulo,
1743
+ perfilCompatibilidade: ir?.perfilCompatibilidade ?? "interno",
1744
+ scoreSemantico: resumoDrift.scoreMedio,
1745
+ confiancaGeral: resumoDrift.confiancaGeral,
1746
+ riscosPrincipais: [...new Set([
1747
+ ...resultadoDrift.resumo_operacional.riscosPrincipais.filter((item) => item.startsWith(`${modulo}:`) || tarefasModulo.some((task) => item.startsWith(`${task.task}:`))),
1748
+ ...(ir?.resumoAgente.riscos ?? []),
1749
+ ])],
1750
+ oQueTocar: resumoDrift.arquivosRelacionados,
1751
+ arquivosProvaveisEditar: resumoDrift.arquivosProvaveisEditar,
1752
+ oQueValidar: [...new Set([
1753
+ ...resumoDrift.checksSugeridos,
1754
+ ...resultadoDrift.resumo_operacional.oQueValidar,
1755
+ ])],
1756
+ oQueEstaFrouxo: [...new Set([
1757
+ ...resumoDrift.lacunas,
1758
+ ...resultadoDrift.resumo_operacional.oQueEstaFrouxo,
1759
+ ])],
1760
+ oQueFoiInferido: [...new Set([
1761
+ ...resultadoDrift.impls_quebrados
1762
+ .filter((impl) => impl.modulo === modulo)
1763
+ .flatMap((impl) => impl.candidatos?.map((candidato) => candidato.caminho) ?? []),
1764
+ ...resultadoDrift.vinculos_validos
1765
+ .filter((vinculo) => vinculo.modulo === modulo && vinculo.status === "parcial")
1766
+ .map((vinculo) => `${vinculo.dono}:${vinculo.valor}`),
1767
+ ])],
1768
+ simbolosRelacionados: [...new Set([
1769
+ ...tarefasModulo.flatMap((task) => task.simbolosReferenciados),
1770
+ ...resultadoDrift.vinculos_validos
1771
+ .filter((vinculo) => vinculo.modulo === modulo)
1772
+ .map((vinculo) => vinculo.simbolo)
1773
+ .filter((item): item is string => Boolean(item)),
1774
+ ])],
1775
+ superficiesImpactadas: [
1776
+ ...(ir?.routes.map((route) => `${route.metodo ?? "?"} ${route.caminho ?? route.nome}`) ?? []),
1777
+ ...(ir?.superficies.map((superficie) => `${superficie.tipo}:${superficie.nome}`) ?? []),
1778
+ ],
1779
+ consumerFramework: resumoDrift.consumerFramework,
1780
+ appRoutes: resumoDrift.appRoutes,
1781
+ consumerSurfaces: resumoDrift.consumerSurfaces,
1782
+ consumerBridges: resumoDrift.consumerBridges,
1783
+ testesMinimos: [
1784
+ "sema validar <arquivo> --json",
1785
+ "sema drift <arquivo> --json",
1786
+ "sema verificar <arquivo-ou-pasta> --json",
1787
+ ],
1788
+ };
1789
+ }
1790
+
1791
+ async function carregarContextoModuloIa(arquivoEntrada: string): Promise<PacoteContextoModuloIa> {
1792
+ const arquivo = path.resolve(arquivoEntrada);
1793
+ garantirArquivoSema(arquivo);
1794
+ const contextoProjeto = await carregarProjeto(arquivo, process.cwd());
1795
+ const resultadoModulo = contextoProjeto.modulosSelecionados.find((item) => path.resolve(item.caminho) === arquivo)?.resultado;
1796
+
1797
+ if (!resultadoModulo) {
1798
+ falharContextoIa(`Nao foi possivel encontrar o modulo correspondente ao arquivo ${arquivo}.`);
1799
+ }
1800
+
1801
+ const sucesso = !temErros(resultadoModulo.diagnosticos);
1802
+ const modulo = resultadoModulo.modulo?.nome ?? path.basename(arquivo, ".sema");
1803
+ const geradoEm = new Date().toISOString();
1804
+ const resultadoDrift = await analisarDriftLegado(contextoProjeto);
1805
+ const drift = {
1806
+ comando: "drift" as const,
1807
+ caminho: arquivo,
1808
+ modulo: resultadoModulo.modulo?.nome ?? null,
1809
+ sucesso: resultadoDrift.sucesso,
1810
+ resumo: resumirDriftPorModulo(resultadoModulo.modulo?.nome ?? null, arquivo, resultadoDrift),
1811
+ drift: resultadoDrift,
1812
+ };
1813
+
1814
+ const validar = {
1815
+ comando: "validar" as const,
1816
+ sucesso,
1817
+ resultados: [
1818
+ {
1819
+ caminho: arquivo,
1820
+ modulo: resultadoModulo.modulo?.nome ?? null,
1821
+ sucesso,
1822
+ diagnosticos: resultadoModulo.diagnosticos,
1823
+ },
1824
+ ],
1825
+ };
1826
+
1827
+ const diagnosticos = {
1828
+ comando: "diagnosticos" as const,
1829
+ caminho: arquivo,
1830
+ modulo: resultadoModulo.modulo?.nome ?? null,
1831
+ diagnosticos: resultadoModulo.diagnosticos,
1832
+ };
1833
+
1834
+ const ast = {
1835
+ comando: "ast" as const,
1836
+ caminho: arquivo,
1837
+ modulo: resultadoModulo.modulo?.nome ?? null,
1838
+ sucesso,
1839
+ diagnosticos: resultadoModulo.diagnosticos,
1840
+ ast: resultadoModulo.modulo ?? null,
1841
+ };
1842
+
1843
+ const ir = {
1844
+ comando: "ir" as const,
1845
+ caminho: arquivo,
1846
+ modulo: resultadoModulo.modulo?.nome ?? null,
1847
+ sucesso,
1848
+ diagnosticos: resultadoModulo.diagnosticos,
1849
+ ir: resultadoModulo.ir ?? null,
1850
+ };
1851
+ const briefing = criarBriefingAgente(
1852
+ arquivo,
1853
+ modulo,
1854
+ resultadoModulo.ir ?? null,
1855
+ drift.resumo,
1856
+ resultadoDrift,
1857
+ );
1858
+
1859
+ return {
1860
+ arquivo,
1861
+ modulo,
1862
+ sucesso,
1863
+ geradoEm,
1864
+ diagnosticos: resultadoModulo.diagnosticos,
1865
+ ir: resultadoModulo.ir ?? null,
1866
+ validar,
1867
+ diagnosticosJson: diagnosticos,
1868
+ ast,
1869
+ irJson: ir,
1870
+ drift,
1871
+ briefing,
1872
+ };
1873
+ }
1874
+
1875
+ async function gerarArquivosResumoModuloIa(
1876
+ contexto: PacoteContextoModuloIa,
1877
+ pastaBase: string,
1878
+ ): Promise<{
1879
+ artefatosCompactos: string[];
1880
+ guiaPorCapacidade: Record<CapacidadeIa, GuiaCapacidadeIa>;
1881
+ }> {
1882
+ const guiaPorCapacidade = criarGuiaCapacidadeIa();
1883
+ const resumoSemantico = coletarResumoSemanticoModulo(contexto);
1884
+ const resumoMicro = renderizarResumoModuloTexto(resumoSemantico, "micro", "resumo");
1885
+ const resumoCurto = renderizarResumoModuloTexto(resumoSemantico, "curto", "resumo");
1886
+ const resumoMarkdown = renderizarResumoModuloMarkdown(resumoSemantico, "resumo", guiaPorCapacidade);
1887
+ const briefingMinimo = criarBriefingMinimo(resumoSemantico, "resumo", "curto");
1888
+ const promptCurto = criarPromptCurtoModulo(resumoSemantico, "mudanca", "curto", "pequena");
1889
+
1890
+ await writeFile(path.join(pastaBase, "resumo.micro.txt"), resumoMicro, "utf8");
1891
+ await writeFile(path.join(pastaBase, "resumo.curto.txt"), resumoCurto, "utf8");
1892
+ await writeFile(path.join(pastaBase, "resumo.md"), resumoMarkdown, "utf8");
1893
+ await writeFile(path.join(pastaBase, "briefing.min.json"), `${JSON.stringify(briefingMinimo, null, 2)}\n`, "utf8");
1894
+ await writeFile(path.join(pastaBase, "prompt-curto.txt"), promptCurto, "utf8");
1895
+
1896
+ return {
1897
+ artefatosCompactos: ["resumo.micro.txt", "resumo.curto.txt", "resumo.md", "briefing.min.json", "prompt-curto.txt"],
1898
+ guiaPorCapacidade,
1899
+ };
1900
+ }
1901
+
1902
+ async function gerarResumoProjetoIa(
1903
+ entrada: string | undefined,
1904
+ pastaSaidaOpcional?: string,
1905
+ escreverNaRaiz = false,
1906
+ ): Promise<{
1907
+ geradoEm: string;
1908
+ baseProjeto: string;
1909
+ pastaSaida: string;
1910
+ artefatos: string[];
1911
+ modulos: ResumoSemanticoModuloIa[];
1912
+ guiaPorCapacidade: Record<CapacidadeIa, GuiaCapacidadeIa>;
1913
+ }> {
1914
+ const contextoProjeto = await carregarProjeto(entrada, process.cwd());
1915
+ const geradoEm = new Date().toISOString();
1916
+ const guiaPorCapacidade = criarGuiaCapacidadeIa();
1917
+ const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
1918
+ const resultadoDrift = await analisarDriftLegado(contextoProjeto);
1919
+ const modulos = contextoProjeto.modulosSelecionados.map((item) => {
1920
+ const modulo = item.resultado.modulo?.nome ?? path.basename(item.caminho, ".sema");
1921
+ const driftResumo = resumirDriftPorModulo(modulo, item.caminho, resultadoDrift);
1922
+ const briefing = criarBriefingAgente(item.caminho, modulo, item.resultado.ir ?? null, driftResumo, resultadoDrift);
1923
+ return coletarResumoSemanticoModulo({
1924
+ arquivo: item.caminho,
1925
+ modulo,
1926
+ geradoEm,
1927
+ ir: item.resultado.ir ?? null,
1928
+ briefing,
1929
+ drift: {
1930
+ comando: "drift",
1931
+ caminho: item.caminho,
1932
+ modulo,
1933
+ sucesso: resultadoDrift.sucesso,
1934
+ resumo: driftResumo,
1935
+ drift: resultadoDrift,
1936
+ },
1937
+ });
1938
+ });
1939
+
1940
+ const baseProjeto = contextoProjeto.baseProjeto;
1941
+ const pastaSaida = escreverNaRaiz
1942
+ ? baseProjeto
1943
+ : pastaSaidaOpcional
1944
+ ? path.resolve(pastaSaidaOpcional)
1945
+ : path.resolve(baseProjeto, ".tmp", "sema-resumo");
1946
+
1947
+ await mkdir(pastaSaida, { recursive: true });
1948
+
1949
+ const semaBrief = renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade);
1950
+ const indexJson = {
1951
+ comando: "resumo-projeto",
1952
+ geradoEm,
1953
+ cliVersao: VERSAO_CLI,
1954
+ baseProjeto,
1955
+ totalModulos: modulos.length,
1956
+ entradaCanonica,
1957
+ guiaPorCapacidade,
1958
+ modulos,
1959
+ };
1960
+ const micro = [
1961
+ `PROJETO: ${path.basename(baseProjeto)}`,
1962
+ `MODULOS: ${modulos.length}`,
1963
+ `ENTRADA_IA: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
1964
+ `TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 3)}`,
1965
+ `TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 3)}`,
1966
+ `TOP_LACUNAS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.lacunas)), 3)}`,
1967
+ `GERADO_EM: ${geradoEm}`,
1968
+ "",
1969
+ ].join("\n");
1970
+ const curto = [
1971
+ `PROJETO: ${path.basename(baseProjeto)}`,
1972
+ `BASE: ${baseProjeto}`,
1973
+ `MODULOS: ${modulos.length}`,
1974
+ `ENTRADA_IA: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
1975
+ `TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 6)}`,
1976
+ `TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 6)}`,
1977
+ `TOP_LACUNAS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.lacunas)), 6)}`,
1978
+ `TOP_ARQUIVOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.arquivosProvaveis)), 6)}`,
1979
+ `GERADO_EM: ${geradoEm}`,
1980
+ "",
1981
+ ].join("\n");
1982
+
1983
+ await writeFile(path.join(pastaSaida, "SEMA_BRIEF.md"), semaBrief, "utf8");
1984
+ await writeFile(path.join(pastaSaida, "SEMA_BRIEF.micro.txt"), micro, "utf8");
1985
+ await writeFile(path.join(pastaSaida, "SEMA_BRIEF.curto.txt"), curto, "utf8");
1986
+ await writeFile(path.join(pastaSaida, "SEMA_INDEX.json"), `${JSON.stringify(indexJson, null, 2)}\n`, "utf8");
1987
+
1988
+ return {
1989
+ geradoEm,
1990
+ baseProjeto,
1991
+ pastaSaida,
1992
+ artefatos: ["SEMA_BRIEF.md", "SEMA_BRIEF.micro.txt", "SEMA_BRIEF.curto.txt", "SEMA_INDEX.json"],
1993
+ modulos,
1994
+ guiaPorCapacidade,
1995
+ };
1996
+ }
1997
+
1998
+ async function gerarContextoIa(arquivoEntrada: string, pastaSaidaOpcional?: string): Promise<ContextoIaGerado> {
1999
+ const contexto = await carregarContextoModuloIa(arquivoEntrada);
2000
+ const pastaBase = pastaSaidaOpcional
2001
+ ? path.resolve(pastaSaidaOpcional)
2002
+ : path.resolve(process.cwd(), ".tmp", "contexto-ia", path.basename(contexto.arquivo, ".sema"));
2003
+
2004
+ await mkdir(pastaBase, { recursive: true });
2005
+
2006
+ await writeFile(path.join(pastaBase, "validar.json"), `${JSON.stringify(contexto.validar, null, 2)}\n`, "utf8");
2007
+ await writeFile(path.join(pastaBase, "diagnosticos.json"), `${JSON.stringify(contexto.diagnosticosJson, null, 2)}\n`, "utf8");
2008
+ await writeFile(path.join(pastaBase, "ast.json"), `${JSON.stringify(contexto.ast, null, 2)}\n`, "utf8");
2009
+ await writeFile(path.join(pastaBase, "ir.json"), `${JSON.stringify(contexto.irJson, null, 2)}\n`, "utf8");
2010
+ await writeFile(path.join(pastaBase, "drift.json"), `${JSON.stringify(contexto.drift, null, 2)}\n`, "utf8");
2011
+ await writeFile(path.join(pastaBase, "briefing.json"), `${JSON.stringify(contexto.briefing, null, 2)}\n`, "utf8");
2012
+ const resumoGerado = await gerarArquivosResumoModuloIa(contexto, pastaBase);
2013
+
2014
+ const resumo = `# Contexto de IA para ${contexto.modulo}
2015
+
2016
+ - Arquivo alvo: \`${contexto.arquivo}\`
2017
+ - Modulo: \`${contexto.modulo}\`
2018
+ - Sucesso em validar: \`${contexto.sucesso}\`
2019
+ - Quantidade de diagnosticos: \`${contexto.diagnosticos.length}\`
2020
+ - Gerado em: \`${contexto.geradoEm}\`
2021
+
2022
+ ## Arquivos gerados neste pacote
2023
+
2024
+ - \`resumo.micro.txt\`
2025
+ - \`resumo.curto.txt\`
2026
+ - \`resumo.md\`
2027
+ - \`briefing.min.json\`
2028
+ - \`prompt-curto.txt\`
2029
+ - \`validar.json\`
2030
+ - \`diagnosticos.json\`
2031
+ - \`ast.json\`
2032
+ - \`ir.json\`
2033
+ - \`drift.json\`
2034
+ - \`briefing.json\`
2035
+
2036
+ ## Fluxo recomendado para o agente
2037
+
2038
+ ### IA pequena ou gratuita
2039
+
2040
+ 1. Ler \`resumo.micro.txt\`.
2041
+ 2. Ler \`briefing.min.json\`.
2042
+ 3. Se ainda couber contexto, ler \`resumo.curto.txt\`.
2043
+
2044
+ ### IA media
2045
+
2046
+ 1. Ler \`resumo.curto.txt\`.
2047
+ 2. Ler \`briefing.min.json\`.
2048
+ 3. Ler \`drift.json\`.
2049
+ 4. Se precisar, subir para \`resumo.md\`.
2050
+
2051
+ ### IA grande ou com tool use
2052
+
2053
+ 1. Ler \`README.md\`.
2054
+ 2. Ler \`resumo.md\`.
2055
+ 3. Ler \`briefing.json\`.
2056
+ 4. Ler \`drift.json\`.
2057
+ 5. So depois abrir \`ir.json\` e \`ast.json\`.
2058
+
2059
+ ## Fechamento
2060
+
2061
+ 1. Editar o arquivo \`.sema\`.
2062
+ 2. Rodar \`sema formatar "${contexto.arquivo}"\`.
2063
+ 3. Rodar \`sema validar "${contexto.arquivo}" --json\`.
2064
+ 4. Rodar \`sema drift "${contexto.arquivo}" --json\`.
2065
+ 5. Fechar com \`sema verificar <arquivo-ou-pasta> --json --saida ./.tmp/verificacao-ia\`.
2066
+
2067
+ ## Textos base para onboarding do agente
2068
+
2069
+ - \`sema starter-ia\`
2070
+ - \`sema resumo "${contexto.arquivo}" --micro --para onboarding\`
2071
+ - \`sema prompt-curto "${contexto.arquivo}" --para mudanca\`
2072
+ - \`sema prompt-ia\`
2073
+ `;
2074
+
2075
+ await writeFile(path.join(pastaBase, "README.md"), resumo, "utf8");
2076
+
2077
+ return {
2078
+ sucesso: contexto.sucesso,
2079
+ arquivo: contexto.arquivo,
2080
+ modulo: contexto.modulo,
2081
+ pastaSaida: pastaBase,
2082
+ artefatos: [
2083
+ "validar.json",
2084
+ "diagnosticos.json",
2085
+ "ast.json",
2086
+ "ir.json",
2087
+ "drift.json",
2088
+ "briefing.json",
2089
+ "README.md",
2090
+ ...resumoGerado.artefatosCompactos,
2091
+ ],
2092
+ artefatosCompactos: resumoGerado.artefatosCompactos,
2093
+ geradoEm: contexto.geradoEm,
2094
+ guiaPorCapacidade: resumoGerado.guiaPorCapacidade,
2095
+ };
2096
+ }
2097
+
2098
+ async function comandoIniciar(cwd: string, template: TemplateIniciar): Promise<number> {
2099
+ const arquivosBase = [
2100
+ {
2101
+ caminhoRelativo: "contratos/pedidos.sema",
2102
+ conteudo: `module app.pedidos {
2103
+ entity Pedido {
2104
+ fields {
2105
+ id: Id
2106
+ status: Texto
2107
+ total: Decimal
2108
+ }
2109
+ }
2110
+
2111
+ task criar_pedido {
2112
+ input {
2113
+ cliente_id: Id required
2114
+ total: Decimal required
2115
+ }
2116
+ output {
2117
+ pedido_id: Id
2118
+ status: Texto
2119
+ }
2120
+ rules {
2121
+ total > 0
2122
+ }
2123
+ effects {
2124
+ persistencia Pedido criticidade=alta
2125
+ auditoria pedidos
2126
+ }
2127
+ guarantees {
2128
+ pedido_id existe
2129
+ status existe
2130
+ }
2131
+ tests {
2132
+ caso "pedido valido" {
2133
+ given {
2134
+ cliente_id: "cli-1"
2135
+ total: 10
2136
+ }
2137
+ expect {
2138
+ sucesso: verdadeiro
2139
+ }
2140
+ }
2141
+ }
2142
+ }
2143
+
2144
+ route criar_pedido_publico {
2145
+ metodo: POST
2146
+ caminho: /pedidos
2147
+ task: criar_pedido
2148
+ }
2149
+ }
2150
+ `,
2151
+ },
2152
+ ];
2153
+
2154
+ let arquivos = arquivosBase;
2155
+ if (template === "nestjs") {
2156
+ arquivos = [
2157
+ {
2158
+ caminhoRelativo: "sema.config.json",
2159
+ conteudo: `{
2160
+ "origens": ["./contratos"],
2161
+ "saida": "./generated/nestjs",
2162
+ "alvos": ["typescript"],
2163
+ "alvoPadrao": "typescript",
2164
+ "estruturaSaida": "backend",
2165
+ "framework": "nestjs",
2166
+ "modoEstrito": true,
2167
+ "diretoriosSaidaPorAlvo": {
2168
+ "typescript": "./generated/nestjs"
2169
+ },
2170
+ "convencoesGeracaoPorProjeto": "backend"
2171
+ }
2172
+ `,
2173
+ },
2174
+ { caminhoRelativo: "src/.gitkeep", conteudo: "" },
2175
+ { caminhoRelativo: "test/.gitkeep", conteudo: "" },
2176
+ ...arquivosBase,
2177
+ ];
2178
+ } else if (template === "fastapi") {
2179
+ arquivos = [
2180
+ {
2181
+ caminhoRelativo: "sema.config.json",
2182
+ conteudo: `{
2183
+ "origens": ["./contratos"],
2184
+ "saida": "./generated/fastapi",
2185
+ "alvos": ["python"],
2186
+ "alvoPadrao": "python",
2187
+ "estruturaSaida": "backend",
2188
+ "framework": "fastapi",
2189
+ "modoEstrito": true,
2190
+ "diretoriosSaidaPorAlvo": {
2191
+ "python": "./generated/fastapi"
2192
+ },
2193
+ "convencoesGeracaoPorProjeto": "backend"
2194
+ }
2195
+ `,
2196
+ },
2197
+ { caminhoRelativo: "app/.gitkeep", conteudo: "" },
2198
+ { caminhoRelativo: "tests/.gitkeep", conteudo: "" },
2199
+ ...arquivosBase,
2200
+ ];
2201
+ } else if (template === "nextjs-api") {
2202
+ arquivos = [
2203
+ {
2204
+ caminhoRelativo: "sema.config.json",
2205
+ conteudo: `{
2206
+ "origens": ["./contratos"],
2207
+ "saida": "./generated",
2208
+ "alvos": ["typescript"],
2209
+ "alvoPadrao": "typescript",
2210
+ "estruturaSaida": "modulos",
2211
+ "framework": "base",
2212
+ "modoEstrito": true,
2213
+ "diretoriosCodigo": ["./src"],
2214
+ "fontesLegado": ["nextjs", "typescript"],
2215
+ "diretoriosSaidaPorAlvo": {
2216
+ "typescript": "./generated/typescript"
2217
+ },
2218
+ "convencoesGeracaoPorProjeto": "base"
2219
+ }
2220
+ `,
2221
+ },
2222
+ {
2223
+ caminhoRelativo: "contratos/health.sema",
2224
+ conteudo: `module app.health {
2225
+ task get_api_health {
2226
+ output {
2227
+ status: Texto
2228
+ runtime: Texto
2229
+ }
2230
+ impl {
2231
+ ts: src.app.api.health.route.GET
2232
+ }
2233
+ guarantees {
2234
+ status existe
2235
+ runtime existe
2236
+ }
2237
+ }
2238
+
2239
+ route get_api_health_publico {
2240
+ metodo: GET
2241
+ caminho: /api/health
2242
+ task: get_api_health
2243
+ }
2244
+ }
2245
+ `,
2246
+ },
2247
+ {
2248
+ caminhoRelativo: "src/app/api/health/route.ts",
2249
+ conteudo: `export async function GET() {
2250
+ return Response.json({
2251
+ status: "ok",
2252
+ runtime: "nextjs",
2253
+ });
2254
+ }
2255
+ `,
2256
+ },
2257
+ {
2258
+ caminhoRelativo: "README.md",
2259
+ conteudo: `# Starter Next.js API + Sema
2260
+
2261
+ - Contratos em \`contratos/\`
2262
+ - Handlers App Router em \`src/app/api/\`
2263
+ - Rota de exemplo validada por \`drift\`
2264
+ `,
2265
+ },
2266
+ ];
2267
+ } else if (template === "nextjs-consumer") {
2268
+ arquivos = [
2269
+ {
2270
+ caminhoRelativo: "sema.config.json",
2271
+ conteudo: `{
2272
+ "origens": ["./contratos"],
2273
+ "saida": "./generated",
2274
+ "alvos": ["typescript"],
2275
+ "alvoPadrao": "typescript",
2276
+ "estruturaSaida": "modulos",
2277
+ "framework": "base",
2278
+ "modoEstrito": true,
2279
+ "diretoriosCodigo": ["./src"],
2280
+ "fontesLegado": ["nextjs-consumer", "typescript"],
2281
+ "diretoriosSaidaPorAlvo": {
2282
+ "typescript": "./generated/typescript"
2283
+ },
2284
+ "convencoesGeracaoPorProjeto": "base"
2285
+ }
2286
+ `,
2287
+ },
2288
+ {
2289
+ caminhoRelativo: "contratos/showroom_consumer.sema",
2290
+ conteudo: `module showroom.consumer {
2291
+ task fetch_showroom_ranking {
2292
+ input {
2293
+ }
2294
+ output {
2295
+ ranking: Json
2296
+ }
2297
+ impl {
2298
+ ts: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
2299
+ }
2300
+ vinculos {
2301
+ arquivo: "src/lib/sema_consumer_bridge.ts"
2302
+ simbolo: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
2303
+ superficie: "/ranking"
2304
+ arquivo: "src/app/ranking/page.tsx"
2305
+ arquivo: "src/app/ranking/loading.tsx"
2306
+ arquivo: "src/app/ranking/error.tsx"
2307
+ }
2308
+ guarantees {
2309
+ ranking existe
2310
+ }
2311
+ }
2312
+ }
2313
+ `,
2314
+ },
2315
+ {
2316
+ caminhoRelativo: "src/lib/sema_consumer_bridge.ts",
2317
+ conteudo: `export async function semaFetchShowroomRanking() {
2318
+ return {
2319
+ ranking: [
2320
+ { clube: "Tigres do Norte", pontos: 33 },
2321
+ { clube: "Porto Azul", pontos: 31 },
2322
+ { clube: "Galo de Ouro", pontos: 28 },
2323
+ ],
2324
+ };
2325
+ }
2326
+ `,
2327
+ },
2328
+ {
2329
+ caminhoRelativo: "src/app/ranking/page.tsx",
2330
+ conteudo: `import { semaFetchShowroomRanking } from "../../lib/sema_consumer_bridge";
2331
+
2332
+ export default async function RankingPage() {
2333
+ const { ranking } = await semaFetchShowroomRanking();
2334
+
2335
+ return (
2336
+ <main>
2337
+ <h1>Ranking showroom</h1>
2338
+ <ul>
2339
+ {ranking.map((item) => (
2340
+ <li key={item.clube}>
2341
+ {item.clube} - {item.pontos} pts
2342
+ </li>
2343
+ ))}
2344
+ </ul>
2345
+ </main>
2346
+ );
2347
+ }
2348
+ `,
2349
+ },
2350
+ {
2351
+ caminhoRelativo: "src/app/ranking/loading.tsx",
2352
+ conteudo: `export default function Loading() {
2353
+ return <p>Carregando ranking...</p>;
2354
+ }
2355
+ `,
2356
+ },
2357
+ {
2358
+ caminhoRelativo: "src/app/ranking/error.tsx",
2359
+ conteudo: `"use client";
2360
+
2361
+ export default function Error({
2362
+ error,
2363
+ reset,
2364
+ }: {
2365
+ error: Error;
2366
+ reset: () => void;
2367
+ }) {
2368
+ return (
2369
+ <main>
2370
+ <h1>Falha ao carregar ranking</h1>
2371
+ <p>{error.message}</p>
2372
+ <button type="button" onClick={reset}>Tentar novamente</button>
2373
+ </main>
2374
+ );
2375
+ }
2376
+ `,
2377
+ },
2378
+ {
2379
+ caminhoRelativo: "README.md",
2380
+ conteudo: `# Starter Next.js Consumer + Sema
2381
+
2382
+ - Contratos em \`contratos/\`
2383
+ - Bridge consumer canonico em \`src/lib/sema_consumer_bridge.ts\`
2384
+ - Superficies App Router em \`src/app/\`
2385
+ - O slice oficial desta fase e \`consumer bridge + App Router surfaces\`
2386
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual drift
2387
+ `,
2388
+ },
2389
+ ];
2390
+ } else if (template === "react-vite-consumer") {
2391
+ arquivos = [
2392
+ {
2393
+ caminhoRelativo: "sema.config.json",
2394
+ conteudo: `{
2395
+ "origens": ["./contratos"],
2396
+ "saida": "./generated",
2397
+ "alvos": ["typescript"],
2398
+ "alvoPadrao": "typescript",
2399
+ "estruturaSaida": "modulos",
2400
+ "framework": "base",
2401
+ "modoEstrito": true,
2402
+ "diretoriosCodigo": ["./src"],
2403
+ "fontesLegado": ["react-vite-consumer", "typescript"],
2404
+ "diretoriosSaidaPorAlvo": {
2405
+ "typescript": "./generated/typescript"
2406
+ },
2407
+ "convencoesGeracaoPorProjeto": "base"
2408
+ }
2409
+ `,
2410
+ },
2411
+ {
2412
+ caminhoRelativo: "contratos/showroom_consumer.sema",
2413
+ conteudo: `module showroom.consumer {
2414
+ task fetch_showroom_ranking {
2415
+ input {
2416
+ }
2417
+ output {
2418
+ ranking: Json
2419
+ }
2420
+ impl {
2421
+ ts: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
2422
+ }
2423
+ vinculos {
2424
+ arquivo: "src/lib/sema_consumer_bridge.ts"
2425
+ simbolo: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
2426
+ superficie: "/ranking"
2427
+ arquivo: "src/router.tsx"
2428
+ arquivo: "src/pages/ranking.tsx"
2429
+ }
2430
+ guarantees {
2431
+ ranking existe
2432
+ }
2433
+ }
2434
+ }
2435
+ `,
2436
+ },
2437
+ {
2438
+ caminhoRelativo: "src/lib/sema_consumer_bridge.ts",
2439
+ conteudo: `export async function semaFetchShowroomRanking() {
2440
+ return {
2441
+ ranking: [
2442
+ { clube: "Tigres do Norte", pontos: 33 },
2443
+ { clube: "Porto Azul", pontos: 31 },
2444
+ { clube: "Galo de Ouro", pontos: 28 },
2445
+ ],
2446
+ };
2447
+ }
2448
+ `,
2449
+ },
2450
+ {
2451
+ caminhoRelativo: "src/pages/ranking.tsx",
2452
+ conteudo: `import { useEffect, useState } from "react";
2453
+ import { semaFetchShowroomRanking } from "../lib/sema_consumer_bridge";
2454
+
2455
+ export function RankingPage() {
2456
+ const [ranking, setRanking] = useState<Array<{ clube: string; pontos: number }>>([]);
2457
+
2458
+ useEffect(() => {
2459
+ void semaFetchShowroomRanking().then((payload) => setRanking(payload.ranking ?? []));
2460
+ }, []);
2461
+
2462
+ return (
2463
+ <main>
2464
+ <h1>Ranking showroom</h1>
2465
+ <ul>
2466
+ {ranking.map((item) => (
2467
+ <li key={item.clube}>
2468
+ {item.clube} - {item.pontos} pts
2469
+ </li>
2470
+ ))}
2471
+ </ul>
2472
+ </main>
2473
+ );
2474
+ }
2475
+ `,
2476
+ },
2477
+ {
2478
+ caminhoRelativo: "src/router.tsx",
2479
+ conteudo: `import { createBrowserRouter } from "react-router-dom";
2480
+ import { RankingPage } from "./pages/ranking";
2481
+
2482
+ export const appRouter = createBrowserRouter([
2483
+ {
2484
+ path: "/ranking",
2485
+ Component: RankingPage,
2486
+ },
2487
+ ]);
2488
+ `,
2489
+ },
2490
+ {
2491
+ caminhoRelativo: "src/App.tsx",
2492
+ conteudo: `import { RouterProvider } from "react-router-dom";
2493
+ import { appRouter } from "./router";
2494
+
2495
+ export default function App() {
2496
+ return <RouterProvider router={appRouter} />;
2497
+ }
2498
+ `,
2499
+ },
2500
+ {
2501
+ caminhoRelativo: "src/main.tsx",
2502
+ conteudo: `import React from "react";
2503
+ import ReactDOM from "react-dom/client";
2504
+ import App from "./App";
2505
+
2506
+ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
2507
+ <React.StrictMode>
2508
+ <App />
2509
+ </React.StrictMode>,
2510
+ );
2511
+ `,
2512
+ },
2513
+ {
2514
+ caminhoRelativo: "README.md",
2515
+ conteudo: `# Starter React Vite Consumer + Sema
2516
+
2517
+ - Contratos em \`contratos/\`
2518
+ - Bridge consumer canonico em \`src/lib/sema_consumer_bridge.ts\`
2519
+ - Rotas explicitas em \`src/router.tsx\`
2520
+ - Superficies consumer em \`src/pages/\`
2521
+ - O slice oficial desta fase e \`consumer bridge + react-router surfaces\`
2522
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual drift
2523
+ `,
2524
+ },
2525
+ ];
2526
+ } else if (template === "angular-consumer") {
2527
+ arquivos = [
2528
+ {
2529
+ caminhoRelativo: "sema.config.json",
2530
+ conteudo: `{
2531
+ "origens": ["./contratos"],
2532
+ "saida": "./generated",
2533
+ "alvos": ["typescript"],
2534
+ "alvoPadrao": "typescript",
2535
+ "estruturaSaida": "modulos",
2536
+ "framework": "base",
2537
+ "modoEstrito": true,
2538
+ "diretoriosCodigo": ["./src"],
2539
+ "fontesLegado": ["angular-consumer", "typescript"],
2540
+ "diretoriosSaidaPorAlvo": {
2541
+ "typescript": "./generated/typescript"
2542
+ },
2543
+ "convencoesGeracaoPorProjeto": "base"
2544
+ }
2545
+ `,
2546
+ },
2547
+ {
2548
+ caminhoRelativo: "contratos/showroom_consumer.sema",
2549
+ conteudo: `module showroom.consumer {
2550
+ task fetch_showroom_ranking {
2551
+ input {
2552
+ }
2553
+ output {
2554
+ ranking: Json
2555
+ }
2556
+ impl {
2557
+ ts: src.app.sema_consumer_bridge.semaFetchShowroomRanking
2558
+ }
2559
+ vinculos {
2560
+ arquivo: "src/app/sema_consumer_bridge.ts"
2561
+ simbolo: src.app.sema_consumer_bridge.semaFetchShowroomRanking
2562
+ superficie: "/ranking"
2563
+ arquivo: "src/app/app.routes.ts"
2564
+ arquivo: "src/app/features/ranking/ranking.routes.ts"
2565
+ arquivo: "src/app/features/ranking/ranking-page.component.ts"
2566
+ }
2567
+ guarantees {
2568
+ ranking existe
2569
+ }
2570
+ }
2571
+ }
2572
+ `,
2573
+ },
2574
+ {
2575
+ caminhoRelativo: "src/app/sema_consumer_bridge.ts",
2576
+ conteudo: `export async function semaFetchShowroomRanking() {
2577
+ return {
2578
+ ranking: [
2579
+ { clube: "Tigres do Norte", pontos: 33 },
2580
+ { clube: "Porto Azul", pontos: 31 },
2581
+ { clube: "Galo de Ouro", pontos: 28 },
2582
+ ],
2583
+ };
2584
+ }
2585
+ `,
2586
+ },
2587
+ {
2588
+ caminhoRelativo: "src/app/app.routes.ts",
2589
+ conteudo: `import { Routes } from "@angular/router";
2590
+
2591
+ export const routes: Routes = [
2592
+ {
2593
+ path: "ranking",
2594
+ loadChildren: () => import("./features/ranking/ranking.routes").then((m) => m.RANKING_ROUTES),
2595
+ },
2596
+ ];
2597
+ `,
2598
+ },
2599
+ {
2600
+ caminhoRelativo: "src/app/features/ranking/ranking.routes.ts",
2601
+ conteudo: `import { Routes } from "@angular/router";
2602
+
2603
+ export const RANKING_ROUTES: Routes = [
2604
+ {
2605
+ path: "",
2606
+ loadComponent: () => import("./ranking-page.component").then((m) => m.RankingPageComponent),
2607
+ },
2608
+ ];
2609
+ `,
2610
+ },
2611
+ {
2612
+ caminhoRelativo: "src/app/features/ranking/ranking-page.component.ts",
2613
+ conteudo: `import { Component, OnInit } from "@angular/core";
2614
+ import { CommonModule } from "@angular/common";
2615
+ import { semaFetchShowroomRanking } from "../../sema_consumer_bridge";
2616
+
2617
+ @Component({
2618
+ selector: "app-ranking-page",
2619
+ standalone: true,
2620
+ imports: [CommonModule],
2621
+ template: \`
2622
+ <main>
2623
+ <h1>Ranking showroom</h1>
2624
+ <ul>
2625
+ <li *ngFor="let item of ranking">
2626
+ {{ item.clube }} - {{ item.pontos }} pts
2627
+ </li>
2628
+ </ul>
2629
+ </main>
2630
+ \`,
2631
+ })
2632
+ export class RankingPageComponent implements OnInit {
2633
+ ranking: Array<{ clube: string; pontos: number }> = [];
2634
+
2635
+ async ngOnInit() {
2636
+ const payload = await semaFetchShowroomRanking();
2637
+ this.ranking = payload.ranking ?? [];
2638
+ }
2639
+ }
2640
+ `,
2641
+ },
2642
+ {
2643
+ caminhoRelativo: "src/app/app.component.ts",
2644
+ conteudo: `import { Component } from "@angular/core";
2645
+ import { RouterOutlet } from "@angular/router";
2646
+
2647
+ @Component({
2648
+ selector: "app-root",
2649
+ standalone: true,
2650
+ imports: [RouterOutlet],
2651
+ template: "<router-outlet />",
2652
+ })
2653
+ export class AppComponent {}
2654
+ `,
2655
+ },
2656
+ {
2657
+ caminhoRelativo: "src/main.ts",
2658
+ conteudo: `import { bootstrapApplication } from "@angular/platform-browser";
2659
+ import { provideRouter } from "@angular/router";
2660
+ import { AppComponent } from "./app/app.component";
2661
+ import { routes } from "./app/app.routes";
2662
+
2663
+ void bootstrapApplication(AppComponent, {
2664
+ providers: [provideRouter(routes)],
2665
+ });
2666
+ `,
2667
+ },
2668
+ {
2669
+ caminhoRelativo: "README.md",
2670
+ conteudo: `# Starter Angular Consumer + Sema
2671
+
2672
+ - Contratos em \`contratos/\`
2673
+ - Bridge consumer canonico em \`src/app/sema_consumer_bridge.ts\`
2674
+ - Rotas lazy em \`src/app/app.routes.ts\`
2675
+ - Feature folders em \`src/app/features/\`
2676
+ - O slice oficial desta fase e \`consumer bridge + route config surfaces\`
2677
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual drift
2678
+ `,
2679
+ },
2680
+ ];
2681
+ } else if (template === "flutter-consumer") {
2682
+ arquivos = [
2683
+ {
2684
+ caminhoRelativo: "sema.config.json",
2685
+ conteudo: `{
2686
+ "origens": ["./contratos"],
2687
+ "saida": "./generated",
2688
+ "alvos": ["dart"],
2689
+ "alvoPadrao": "dart",
2690
+ "estruturaSaida": "modulos",
2691
+ "framework": "base",
2692
+ "modoEstrito": true,
2693
+ "diretoriosCodigo": ["./lib"],
2694
+ "fontesLegado": ["flutter-consumer", "dart"],
2695
+ "diretoriosSaidaPorAlvo": {
2696
+ "dart": "./generated/dart"
2697
+ },
2698
+ "convencoesGeracaoPorProjeto": "base"
2699
+ }
2700
+ `,
2701
+ },
2702
+ {
2703
+ caminhoRelativo: "pubspec.yaml",
2704
+ conteudo: `name: sema_flutter_consumer
2705
+ description: Starter Flutter consumer IA-first com Sema
2706
+ publish_to: "none"
2707
+
2708
+ environment:
2709
+ sdk: ">=3.3.0 <4.0.0"
2710
+
2711
+ dependencies:
2712
+ flutter:
2713
+ sdk: flutter
2714
+ go_router: ^14.0.0
2715
+ `,
2716
+ },
2717
+ {
2718
+ caminhoRelativo: "contratos/showroom_consumer.sema",
2719
+ conteudo: `module showroom.consumer {
2720
+ task fetch_showroom_ranking {
2721
+ input {
2722
+ }
2723
+ output {
2724
+ resultado: Json
2725
+ }
2726
+ impl {
2727
+ dart: lib.sema_consumer_bridge.semaFetchShowroomRanking
2728
+ }
2729
+ vinculos {
2730
+ arquivo: "lib/sema_consumer_bridge.dart"
2731
+ simbolo: lib.sema_consumer_bridge.semaFetchShowroomRanking
2732
+ superficie: "/ranking"
2733
+ arquivo: "lib/router.dart"
2734
+ arquivo: "lib/screens/ranking_screen.dart"
2735
+ }
2736
+ guarantees {
2737
+ resultado existe
2738
+ }
2739
+ }
2740
+ }
2741
+ `,
2742
+ },
2743
+ {
2744
+ caminhoRelativo: "lib/sema_consumer_bridge.dart",
2745
+ conteudo: `Future<Map<String, dynamic>> semaFetchShowroomRanking() async {
2746
+ return {
2747
+ "ranking": [
2748
+ {"clube": "Tigres do Norte", "pontos": 33},
2749
+ {"clube": "Porto Azul", "pontos": 31},
2750
+ {"clube": "Galo de Ouro", "pontos": 28},
2751
+ ],
2752
+ };
2753
+ }
2754
+ `,
2755
+ },
2756
+ {
2757
+ caminhoRelativo: "lib/router.dart",
2758
+ conteudo: `import "package:go_router/go_router.dart";
2759
+ import "package:flutter/widgets.dart";
2760
+ import "screens/ranking_screen.dart";
2761
+
2762
+ final appRouter = GoRouter(
2763
+ routes: [
2764
+ GoRoute(
2765
+ path: "/ranking",
2766
+ builder: (BuildContext context, GoRouterState state) => const RankingScreen(),
2767
+ ),
2768
+ ],
2769
+ );
2770
+ `,
2771
+ },
2772
+ {
2773
+ caminhoRelativo: "lib/screens/ranking_screen.dart",
2774
+ conteudo: `import "package:flutter/widgets.dart";
2775
+ import "../sema_consumer_bridge.dart";
2776
+
2777
+ class RankingScreen extends StatefulWidget {
2778
+ const RankingScreen({super.key});
2779
+
2780
+ @override
2781
+ State<RankingScreen> createState() => _RankingScreenState();
2782
+ }
2783
+
2784
+ class _RankingScreenState extends State<RankingScreen> {
2785
+ List<Map<String, dynamic>> ranking = const [];
2786
+
2787
+ @override
2788
+ void initState() {
2789
+ super.initState();
2790
+ semaFetchShowroomRanking().then((payload) {
2791
+ final itens = (payload["ranking"] as List<dynamic>? ?? const [])
2792
+ .whereType<Map<String, dynamic>>()
2793
+ .toList();
2794
+ if (!mounted) return;
2795
+ setState(() {
2796
+ ranking = itens;
2797
+ });
2798
+ });
2799
+ }
2800
+
2801
+ @override
2802
+ Widget build(BuildContext context) {
2803
+ return ListView(
2804
+ children: [
2805
+ const Padding(
2806
+ padding: EdgeInsets.all(16),
2807
+ child: Text("Ranking showroom"),
2808
+ ),
2809
+ ...ranking.map((item) => Padding(
2810
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
2811
+ child: Text("\${item["clube"]} - \${item["pontos"]} pts"),
2812
+ )),
2813
+ ],
2814
+ );
2815
+ }
2816
+ }
2817
+ `,
2818
+ },
2819
+ {
2820
+ caminhoRelativo: "lib/main.dart",
2821
+ conteudo: `import "package:flutter/material.dart";
2822
+ import "router.dart";
2823
+
2824
+ void main() {
2825
+ runApp(const ShowroomApp());
2826
+ }
2827
+
2828
+ class ShowroomApp extends StatelessWidget {
2829
+ const ShowroomApp({super.key});
2830
+
2831
+ @override
2832
+ Widget build(BuildContext context) {
2833
+ return MaterialApp.router(
2834
+ routerConfig: appRouter,
2835
+ );
2836
+ }
2837
+ }
2838
+ `,
2839
+ },
2840
+ {
2841
+ caminhoRelativo: "README.md",
2842
+ conteudo: `# Starter Flutter Consumer + Sema
2843
+
2844
+ - Contratos em \`contratos/\`
2845
+ - Bridge consumer canonico em \`lib/sema_consumer_bridge.dart\`
2846
+ - Rotas consumer em \`lib/router.dart\`
2847
+ - Superficies consumer em \`lib/screens/\`
2848
+ - O slice oficial desta fase e \`consumer bridge + router/screen surfaces\`
2849
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual diff
2850
+ `,
2851
+ },
2852
+ ];
2853
+ } else if (template === "node-firebase-worker") {
2854
+ arquivos = [
2855
+ {
2856
+ caminhoRelativo: "sema.config.json",
2857
+ conteudo: `{
2858
+ "origens": ["./contratos"],
2859
+ "saida": "./generated",
2860
+ "alvos": ["typescript"],
2861
+ "alvoPadrao": "typescript",
2862
+ "estruturaSaida": "modulos",
2863
+ "framework": "base",
2864
+ "modoEstrito": true,
2865
+ "diretoriosCodigo": ["./src"],
2866
+ "fontesLegado": ["firebase", "typescript"],
2867
+ "diretoriosSaidaPorAlvo": {
2868
+ "typescript": "./generated/typescript"
2869
+ },
2870
+ "convencoesGeracaoPorProjeto": "base"
2871
+ }
2872
+ `,
2873
+ },
2874
+ {
2875
+ caminhoRelativo: "contratos/worker_runtime.sema",
2876
+ conteudo: `module worker.runtime {
2877
+ task publicar_payload_health {
2878
+ output {
2879
+ status: Texto
2880
+ timestamp: Texto
2881
+ }
2882
+ effects {
2883
+ evento payload_health criticidade = alta
2884
+ }
2885
+ impl {
2886
+ ts: src.sema_contract_bridge.semaWorkerHealthPayload
2887
+ }
2888
+ guarantees {
2889
+ status existe
2890
+ timestamp existe
2891
+ }
2892
+ }
2893
+
2894
+ task inventariar_colecoes {
2895
+ output {
2896
+ collections: Json
2897
+ }
2898
+ effects {
2899
+ consulta runtime criticidade = baixa
2900
+ }
2901
+ impl {
2902
+ ts: src.sema_contract_bridge.semaCollectionNames
2903
+ }
2904
+ guarantees {
2905
+ collections existe
2906
+ }
2907
+ }
2908
+
2909
+ route get_health_worker {
2910
+ metodo: GET
2911
+ caminho: /health
2912
+ task: publicar_payload_health
2913
+ }
2914
+ }
2915
+ `,
2916
+ },
2917
+ {
2918
+ caminhoRelativo: "src/config/collections.ts",
2919
+ conteudo: `export const COLLECTIONS = {
2920
+ worker_status: "worker_status",
2921
+ audit_log: "audit_log",
2922
+ } as const;
2923
+ `,
2924
+ },
2925
+ {
2926
+ caminhoRelativo: "src/services/health-check.ts",
2927
+ conteudo: `import http from "node:http";
2928
+
2929
+ export type HealthStatus = {
2930
+ status: "healthy" | "degraded" | "unhealthy" | "initializing";
2931
+ timestamp: string;
2932
+ };
2933
+
2934
+ export type HealthProvider = () => HealthStatus;
2935
+
2936
+ export function startHealthCheckServer(port: number, provider: HealthProvider) {
2937
+ const server = http.createServer((req, res) => {
2938
+ if (req.url === "/health" && req.method === "GET") {
2939
+ res.writeHead(200, { "Content-Type": "application/json" });
2940
+ res.end(JSON.stringify(provider()));
2941
+ return;
2942
+ }
2943
+ res.writeHead(404);
2944
+ res.end();
2945
+ });
2946
+
2947
+ server.listen(port);
2948
+ return server;
2949
+ }
2950
+ `,
2951
+ },
2952
+ {
2953
+ caminhoRelativo: "src/sema_contract_bridge.ts",
2954
+ conteudo: `import { COLLECTIONS } from "./config/collections";
2955
+ import { startHealthCheckServer, type HealthProvider, type HealthStatus } from "./services/health-check";
2956
+
2957
+ export function semaStartWorkerHealthServer(port: number, provider: HealthProvider) {
2958
+ return startHealthCheckServer(port, provider);
2959
+ }
2960
+
2961
+ export function semaWorkerHealthPayload(payload: HealthStatus): HealthStatus {
2962
+ return payload;
2963
+ }
2964
+
2965
+ export function semaCollectionNames() {
2966
+ return COLLECTIONS;
2967
+ }
2968
+ `,
2969
+ },
2970
+ {
2971
+ caminhoRelativo: "README.md",
2972
+ conteudo: `# Starter Node Firebase Worker + Sema
2973
+
2974
+ - Contratos em \`contratos/\`
2975
+ - Worker e bridges em \`src/\`
2976
+ - \`drift\` valida impl, endpoint de health e recursos Firestore declarados
2977
+ `,
2978
+ },
2979
+ ];
2980
+ } else if (template === "aspnet-api") {
2981
+ arquivos = [
2982
+ {
2983
+ caminhoRelativo: "sema.config.json",
2984
+ conteudo: `{
2985
+ "origens": ["./contratos"],
2986
+ "saida": "./generated",
2987
+ "alvos": ["typescript"],
2988
+ "alvoPadrao": "typescript",
2989
+ "estruturaSaida": "modulos",
2990
+ "framework": "base",
2991
+ "modoEstrito": true,
2992
+ "diretoriosCodigo": ["./src"],
2993
+ "fontesLegado": ["dotnet"],
2994
+ "diretoriosSaidaPorAlvo": {
2995
+ "typescript": "./generated/typescript"
2996
+ },
2997
+ "convencoesGeracaoPorProjeto": "base"
2998
+ }
2999
+ `,
3000
+ },
3001
+ {
3002
+ caminhoRelativo: "contratos/health.sema",
3003
+ conteudo: `module app.health {
3004
+ task get_health {
3005
+ output {
3006
+ status: Texto
3007
+ runtime: Texto
3008
+ }
3009
+ impl {
3010
+ cs: src.Controllers.HealthController.Get
3011
+ }
3012
+ guarantees {
3013
+ status existe
3014
+ runtime existe
3015
+ }
3016
+ }
3017
+
3018
+ route get_health_publico {
3019
+ metodo: GET
3020
+ caminho: /api/health
3021
+ task: get_health
3022
+ }
3023
+ }
3024
+ `,
3025
+ },
3026
+ {
3027
+ caminhoRelativo: "src/Controllers/HealthController.cs",
3028
+ conteudo: `using Microsoft.AspNetCore.Mvc;
3029
+
3030
+ [ApiController]
3031
+ [Route("api/health")]
3032
+ public class HealthController : ControllerBase
3033
+ {
3034
+ [HttpGet]
3035
+ public object Get()
3036
+ {
3037
+ return new { status = "ok", runtime = "aspnet" };
3038
+ }
3039
+ }
3040
+ `,
3041
+ },
3042
+ {
3043
+ caminhoRelativo: "README.md",
3044
+ conteudo: `# Starter ASP.NET Core API + Sema
3045
+
3046
+ - Contratos em \`contratos/\`
3047
+ - Controllers/Minimal API em \`src/\`
3048
+ - \`drift\` valida impl e rota publica
3049
+ `,
3050
+ },
3051
+ ];
3052
+ } else if (template === "springboot-api") {
3053
+ arquivos = [
3054
+ {
3055
+ caminhoRelativo: "sema.config.json",
3056
+ conteudo: `{
3057
+ "origens": ["./contratos"],
3058
+ "saida": "./generated",
3059
+ "alvos": ["typescript"],
3060
+ "alvoPadrao": "typescript",
3061
+ "estruturaSaida": "modulos",
3062
+ "framework": "base",
3063
+ "modoEstrito": true,
3064
+ "diretoriosCodigo": ["./src"],
3065
+ "fontesLegado": ["java"],
3066
+ "diretoriosSaidaPorAlvo": {
3067
+ "typescript": "./generated/typescript"
3068
+ },
3069
+ "convencoesGeracaoPorProjeto": "base"
3070
+ }
3071
+ `,
3072
+ },
3073
+ {
3074
+ caminhoRelativo: "contratos/health.sema",
3075
+ conteudo: `module app.health {
3076
+ task get_health {
3077
+ output {
3078
+ status: Texto
3079
+ runtime: Texto
3080
+ }
3081
+ impl {
3082
+ java: src.main.java.com.acme.health.HealthController.health
3083
+ }
3084
+ guarantees {
3085
+ status existe
3086
+ runtime existe
3087
+ }
3088
+ }
3089
+
3090
+ route get_health_publico {
3091
+ metodo: GET
3092
+ caminho: /api/health
3093
+ task: get_health
3094
+ }
3095
+ }
3096
+ `,
3097
+ },
3098
+ {
3099
+ caminhoRelativo: "src/main/java/com/acme/health/HealthController.java",
3100
+ conteudo: `package com.acme.health;
3101
+
3102
+ import java.util.Map;
3103
+ import org.springframework.web.bind.annotation.GetMapping;
3104
+ import org.springframework.web.bind.annotation.RequestMapping;
3105
+ import org.springframework.web.bind.annotation.RestController;
3106
+
3107
+ @RestController
3108
+ @RequestMapping("/api/health")
3109
+ public class HealthController {
3110
+ @GetMapping
3111
+ public Map<String, String> health() {
3112
+ return Map.of("status", "ok", "runtime", "spring");
3113
+ }
3114
+ }
3115
+ `,
3116
+ },
3117
+ {
3118
+ caminhoRelativo: "README.md",
3119
+ conteudo: `# Starter Spring Boot API + Sema
3120
+
3121
+ - Contratos em \`contratos/\`
3122
+ - Controllers REST em \`src/main/java/\`
3123
+ - \`drift\` valida impl e rota publica
3124
+ `,
3125
+ },
3126
+ ];
3127
+ } else if (template === "go-http-api") {
3128
+ arquivos = [
3129
+ {
3130
+ caminhoRelativo: "sema.config.json",
3131
+ conteudo: `{
3132
+ "origens": ["./contratos"],
3133
+ "saida": "./generated",
3134
+ "alvos": ["typescript"],
3135
+ "alvoPadrao": "typescript",
3136
+ "estruturaSaida": "modulos",
3137
+ "framework": "base",
3138
+ "modoEstrito": true,
3139
+ "diretoriosCodigo": ["./internal"],
3140
+ "fontesLegado": ["go"],
3141
+ "diretoriosSaidaPorAlvo": {
3142
+ "typescript": "./generated/typescript"
3143
+ },
3144
+ "convencoesGeracaoPorProjeto": "base"
3145
+ }
3146
+ `,
3147
+ },
3148
+ {
3149
+ caminhoRelativo: "contratos/health.sema",
3150
+ conteudo: `module app.health {
3151
+ task get_health {
3152
+ output {
3153
+ resultado: Json
3154
+ }
3155
+ impl {
3156
+ go: internal.health.getHealth
3157
+ }
3158
+ guarantees {
3159
+ resultado existe
3160
+ }
3161
+ }
3162
+
3163
+ route get_health_publico {
3164
+ metodo: GET
3165
+ caminho: /health
3166
+ task: get_health
3167
+ }
3168
+ }
3169
+ `,
3170
+ },
3171
+ {
3172
+ caminhoRelativo: "internal/health.go",
3173
+ conteudo: `package internal
3174
+
3175
+ import "github.com/gin-gonic/gin"
3176
+
3177
+ func registerRoutes(router *gin.Engine) {
3178
+ router.GET("/health", getHealth)
3179
+ }
3180
+
3181
+ func getHealth(ctx *gin.Context) {
3182
+ ctx.JSON(200, gin.H{"status": "ok", "runtime": "go"})
3183
+ }
3184
+ `,
3185
+ },
3186
+ {
3187
+ caminhoRelativo: "README.md",
3188
+ conteudo: `# Starter Go HTTP API + Sema
3189
+
3190
+ - Contratos em \`contratos/\`
3191
+ - Handlers em \`internal/\`
3192
+ - \`drift\` valida impl e rota publica
3193
+ `,
3194
+ },
3195
+ ];
3196
+ } else if (template === "rust-axum-api") {
3197
+ arquivos = [
3198
+ {
3199
+ caminhoRelativo: "sema.config.json",
3200
+ conteudo: `{
3201
+ "origens": ["./contratos"],
3202
+ "saida": "./generated",
3203
+ "alvos": ["typescript"],
3204
+ "alvoPadrao": "typescript",
3205
+ "estruturaSaida": "modulos",
3206
+ "framework": "base",
3207
+ "modoEstrito": true,
3208
+ "diretoriosCodigo": ["./src"],
3209
+ "fontesLegado": ["rust"],
3210
+ "diretoriosSaidaPorAlvo": {
3211
+ "typescript": "./generated/typescript"
3212
+ },
3213
+ "convencoesGeracaoPorProjeto": "base"
3214
+ }
3215
+ `,
3216
+ },
3217
+ {
3218
+ caminhoRelativo: "contratos/health.sema",
3219
+ conteudo: `module app.health {
3220
+ task get_health {
3221
+ output {
3222
+ resultado: Json
3223
+ }
3224
+ impl {
3225
+ rust: src.handlers.health
3226
+ }
3227
+ guarantees {
3228
+ resultado existe
3229
+ }
3230
+ }
3231
+
3232
+ route get_health_publico {
3233
+ metodo: GET
3234
+ caminho: /health
3235
+ task: get_health
3236
+ }
3237
+ }
3238
+ `,
3239
+ },
3240
+ {
3241
+ caminhoRelativo: "src/main.rs",
3242
+ conteudo: `use axum::{routing::get, Router};
3243
+
3244
+ mod handlers;
3245
+
3246
+ fn app() -> Router {
3247
+ Router::new().route("/health", get(handlers::health))
3248
+ }
3249
+ `,
3250
+ },
3251
+ {
3252
+ caminhoRelativo: "src/handlers.rs",
3253
+ conteudo: `pub async fn health() -> &'static str {
3254
+ "ok"
3255
+ }
3256
+ `,
3257
+ },
3258
+ {
3259
+ caminhoRelativo: "README.md",
3260
+ conteudo: `# Starter Rust Axum API + Sema
3261
+
3262
+ - Contratos em \`contratos/\`
3263
+ - Handlers em \`src/\`
3264
+ - \`drift\` valida impl e rota publica
3265
+ `,
3266
+ },
3267
+ ];
3268
+ } else if (template === "cpp-service-bridge") {
3269
+ arquivos = [
3270
+ {
3271
+ caminhoRelativo: "sema.config.json",
3272
+ conteudo: `{
3273
+ "origens": ["./contratos"],
3274
+ "saida": "./generated",
3275
+ "alvos": ["typescript"],
3276
+ "alvoPadrao": "typescript",
3277
+ "estruturaSaida": "modulos",
3278
+ "framework": "base",
3279
+ "modoEstrito": true,
3280
+ "diretoriosCodigo": ["./src"],
3281
+ "fontesLegado": ["cpp"],
3282
+ "diretoriosSaidaPorAlvo": {
3283
+ "typescript": "./generated/typescript"
3284
+ },
3285
+ "convencoesGeracaoPorProjeto": "base"
3286
+ }
3287
+ `,
3288
+ },
3289
+ {
3290
+ caminhoRelativo: "contratos/runtime_bridge.sema",
3291
+ conteudo: `module app.runtime_bridge {
3292
+ task processar_snapshot {
3293
+ input {
3294
+ payload: Json required
3295
+ }
3296
+ output {
3297
+ resultado: Json
3298
+ }
3299
+ impl {
3300
+ cpp: src.runtime.RuntimeBridge.processSnapshot
3301
+ }
3302
+ guarantees {
3303
+ resultado existe
3304
+ }
3305
+ }
3306
+ }
3307
+ `,
3308
+ },
3309
+ {
3310
+ caminhoRelativo: "src/runtime.cpp",
3311
+ conteudo: `class RuntimeBridge {
3312
+ public:
3313
+ int processSnapshot(int payload) {
3314
+ return payload;
3315
+ }
3316
+ };
3317
+ `,
3318
+ },
3319
+ {
3320
+ caminhoRelativo: "README.md",
3321
+ conteudo: `# Starter C++ Service Bridge + Sema
3322
+
3323
+ - Contratos em \`contratos/\`
3324
+ - Symbols e bridges em \`src/\`
3325
+ - \`drift\` valida impl de simbolos, sem prometer rota HTTP
3326
+ `,
3327
+ },
3328
+ ];
3329
+ } else {
3330
+ arquivos = [
3331
+ {
3332
+ caminhoRelativo: "sema.config.json",
3333
+ conteudo: `{
3334
+ "origens": ["./contratos"],
3335
+ "saida": "./generated",
3336
+ "alvos": ["typescript", "python", "dart"],
3337
+ "alvoPadrao": "typescript",
3338
+ "estruturaSaida": "modulos",
3339
+ "framework": "base",
3340
+ "modoEstrito": true,
3341
+ "diretoriosSaidaPorAlvo": {
3342
+ "typescript": "./generated/typescript",
3343
+ "python": "./generated/python",
3344
+ "dart": "./generated/dart"
3345
+ },
3346
+ "convencoesGeracaoPorProjeto": "base"
3347
+ }
3348
+ `,
3349
+ },
3350
+ ...arquivosBase,
3351
+ ];
3352
+ }
3353
+
3354
+ await escreverArquivos(cwd, arquivos);
3355
+ console.log(`Projeto Sema inicializado com template ${template}.`);
3356
+ return 0;
3357
+ }
3358
+
3359
+ async function comandoValidar(entrada?: string): Promise<number> {
3360
+ const modulos = await carregarModulos(entrada);
3361
+ const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
3362
+ console.log(formatarDiagnosticos(diagnosticos));
3363
+ return temErros(diagnosticos) ? 1 : 0;
3364
+ }
3365
+
3366
+ async function comandoValidarJson(entrada?: string): Promise<number> {
3367
+ const modulos = await carregarModulos(entrada);
3368
+ const resultados = modulos.map((item) => ({
3369
+ caminho: item.caminho,
3370
+ modulo: item.resultado.modulo?.nome ?? null,
3371
+ sucesso: !temErros(item.resultado.diagnosticos),
3372
+ diagnosticos: item.resultado.diagnosticos,
3373
+ }));
3374
+ console.log(JSON.stringify({
3375
+ comando: "validar",
3376
+ sucesso: resultados.every((resultado) => resultado.sucesso),
3377
+ resultados,
3378
+ }, null, 2));
3379
+ return resultados.every((resultado) => resultado.sucesso) ? 0 : 1;
3380
+ }
3381
+
3382
+ async function comandoInspecionar(entrada: string | undefined, emJson: boolean, cwd = process.cwd()): Promise<number> {
3383
+ const contextoProjeto = await carregarProjeto(entrada, cwd);
3384
+ const resultadoDrift = await analisarDriftLegado(contextoProjeto);
3385
+ const framework = resolverFrameworkPadrao(undefined, contextoProjeto.configCarregada);
3386
+ const estruturaSaida = resolverEstruturaSaidaPadrao(undefined, framework, contextoProjeto.configCarregada);
3387
+ const alvos = resolverAlvosVerificacao(contextoProjeto.configCarregada);
3388
+ const saidas = Object.fromEntries(alvos.map((alvo) => [alvo, resolverSaidaPadrao(undefined, alvo, contextoProjeto.configCarregada)]));
3389
+ const payload = {
3390
+ comando: "inspecionar",
3391
+ entrada: contextoProjeto.entradaResolvida,
3392
+ configuracao: {
3393
+ caminho: contextoProjeto.configCarregada?.caminho ?? null,
3394
+ baseProjeto: contextoProjeto.baseProjeto,
3395
+ framework,
3396
+ estruturaSaida,
3397
+ alvos,
3398
+ saidas,
3399
+ origens: contextoProjeto.origensProjeto,
3400
+ diretoriosCodigo: contextoProjeto.diretoriosCodigo,
3401
+ fontesLegado: contextoProjeto.fontesLegado,
3402
+ modoAdocao: contextoProjeto.modoAdocao,
3403
+ scoreDrift: resultadoDrift.resumo_operacional.scoreMedio,
3404
+ confiancaGeral: resultadoDrift.resumo_operacional.confiancaGeral,
3405
+ consumerFramework: resultadoDrift.consumerFramework,
3406
+ appRoutes: resultadoDrift.appRoutes,
3407
+ consumerSurfaces: resultadoDrift.consumerSurfaces,
3408
+ consumerBridges: resultadoDrift.consumerBridges,
3409
+ },
3410
+ projeto: {
3411
+ arquivos: contextoProjeto.arquivosProjeto,
3412
+ modulos: contextoProjeto.modulosSelecionados.map((item) => ({
3413
+ caminho: item.caminho,
3414
+ modulo: item.resultado.modulo?.nome ?? null,
3415
+ sucesso: !temErros(item.resultado.diagnosticos),
3416
+ diagnosticos: item.resultado.diagnosticos.length,
3417
+ superficies: item.resultado.ir?.superficies.map((superficie) => `${superficie.tipo}:${superficie.nome}`) ?? [],
3418
+ implementacao: resumirDriftPorModulo(item.resultado.modulo?.nome ?? null, item.caminho, resultadoDrift),
3419
+ })),
3420
+ },
3421
+ };
3422
+
3423
+ if (emJson) {
3424
+ console.log(JSON.stringify(payload, null, 2));
3425
+ return 0;
3426
+ }
3427
+
3428
+ console.log("Inspecao de projeto Sema");
3429
+ console.log(`- Entrada: ${payload.entrada}`);
3430
+ console.log(`- Configuracao: ${payload.configuracao.caminho ?? "nenhuma"}`);
3431
+ console.log(`- Base do projeto: ${payload.configuracao.baseProjeto}`);
3432
+ console.log(`- Framework: ${payload.configuracao.framework}`);
3433
+ console.log(`- Estrutura de saida: ${payload.configuracao.estruturaSaida}`);
3434
+ console.log(`- Alvos: ${payload.configuracao.alvos.join(", ")}`);
3435
+ console.log(`- Modo de adocao: ${payload.configuracao.modoAdocao}`);
3436
+ console.log(`- Score medio de drift: ${payload.configuracao.scoreDrift}`);
3437
+ console.log(`- Confianca geral: ${payload.configuracao.confiancaGeral}`);
3438
+ console.log("- Saidas por alvo:");
3439
+ for (const [alvo, saida] of Object.entries(payload.configuracao.saidas)) {
3440
+ console.log(` - ${alvo}: ${saida}`);
3441
+ }
3442
+ console.log("- Origens do projeto:");
3443
+ for (const origem of payload.configuracao.origens) {
3444
+ console.log(` - ${origem}`);
3445
+ }
3446
+ console.log("- Diretorios de codigo:");
3447
+ for (const diretorio of payload.configuracao.diretoriosCodigo) {
3448
+ console.log(` - ${diretorio}`);
3449
+ }
3450
+ console.log(`- Fontes de legado detectadas: ${payload.configuracao.fontesLegado.join(", ") || "nenhuma"}`);
3451
+ console.log("- Modulos selecionados:");
3452
+ for (const modulo of payload.projeto.modulos) {
3453
+ console.log(` - ${modulo.modulo ?? "(sem modulo)"} :: ${modulo.caminho} :: diagnosticos=${modulo.diagnosticos}`);
3454
+ console.log(` impls validos=${modulo.implementacao.implsValidos} quebrados=${modulo.implementacao.implsQuebrados} recursos divergentes=${modulo.implementacao.recursosDivergentesCount} sem_impl=${modulo.implementacao.tasksSemImplementacao}`);
3455
+ for (const arquivoRelacionado of modulo.implementacao.arquivosRelacionados.slice(0, 5)) {
3456
+ console.log(` arquivo relacionado: ${arquivoRelacionado}`);
3457
+ }
3458
+ }
3459
+ return 0;
3460
+ }
3461
+
3462
+ async function comandoDrift(entrada: string | undefined, emJson: boolean, cwd = process.cwd()): Promise<number> {
3463
+ const contextoProjeto = await carregarProjeto(entrada, cwd);
3464
+ const resultado = await analisarDriftLegado(contextoProjeto);
3465
+
3466
+ if (emJson) {
3467
+ console.log(JSON.stringify(resultado, null, 2));
3468
+ return resultado.sucesso ? 0 : 1;
3469
+ }
3470
+
3471
+ console.log("Drift entre Sema e codigo legado");
3472
+ console.log(`- Modulos analisados: ${resultado.modulos.length}`);
3473
+ console.log(`- Tasks analisadas: ${resultado.tasks.length}`);
3474
+ console.log(`- Impl validos: ${resultado.impls_validos.length}`);
3475
+ console.log(`- Impl quebrados: ${resultado.impls_quebrados.length}`);
3476
+ console.log(`- Vinculos validos: ${resultado.vinculos_validos.length}`);
3477
+ console.log(`- Vinculos quebrados: ${resultado.vinculos_quebrados.length}`);
3478
+ console.log(`- Rotas divergentes: ${resultado.rotas_divergentes.length}`);
3479
+ console.log(`- Recursos vivos validos: ${resultado.recursos_validos.length}`);
3480
+ console.log(`- Recursos vivos divergentes: ${resultado.recursos_divergentes.length}`);
3481
+ console.log(`- Score medio: ${resultado.resumo_operacional.scoreMedio}`);
3482
+ console.log(`- Confianca geral: ${resultado.resumo_operacional.confiancaGeral}`);
3483
+
3484
+ if (resultado.impls_quebrados.length > 0) {
3485
+ console.log("- Impl quebrados:");
3486
+ for (const impl of resultado.impls_quebrados) {
3487
+ console.log(` - ${impl.modulo}.${impl.task} :: ${impl.origem}:${impl.caminho}`);
3488
+ if (impl.candidatos && impl.candidatos.length > 0) {
3489
+ console.log(" candidatos provaveis:");
3490
+ for (const candidato of impl.candidatos) {
3491
+ console.log(` - [${candidato.confianca}] ${candidato.caminho} :: ${candidato.arquivo} :: ${candidato.simbolo}`);
3492
+ }
3493
+ }
3494
+ }
3495
+ }
3496
+
3497
+ if (resultado.rotas_divergentes.length > 0) {
3498
+ console.log("- Rotas divergentes:");
3499
+ for (const rota of resultado.rotas_divergentes) {
3500
+ console.log(` - ${rota.modulo}.${rota.route} :: ${rota.metodo ?? "?"} ${rota.caminho ?? "?"}`);
3501
+ }
3502
+ }
3503
+
3504
+ if (resultado.recursos_divergentes.length > 0) {
3505
+ console.log("- Recursos divergentes:");
3506
+ for (const recurso of resultado.recursos_divergentes) {
3507
+ console.log(` - ${recurso.modulo}.${recurso.task} :: ${recurso.categoria} ${recurso.alvo}`);
3508
+ }
3509
+ }
3510
+
3511
+ const semImpl = resultado.tasks.filter((task) => task.semImplementacao);
3512
+ if (semImpl.length > 0) {
3513
+ console.log("- Tasks sem implementacao vinculada:");
3514
+ for (const task of semImpl) {
3515
+ console.log(` - ${task.modulo}.${task.task}`);
3516
+ if (task.candidatosImpl.length > 0) {
3517
+ console.log(" candidatos provaveis:");
3518
+ for (const candidato of task.candidatosImpl) {
3519
+ console.log(` - [${candidato.confianca}] ${candidato.caminho} :: ${candidato.arquivo} :: ${candidato.simbolo}`);
3520
+ }
3521
+ }
3522
+ }
3523
+ }
3524
+
3525
+ if (resultado.vinculos_quebrados.length > 0) {
3526
+ console.log("- Vinculos quebrados:");
3527
+ for (const vinculo of resultado.vinculos_quebrados) {
3528
+ console.log(` - ${vinculo.modulo}.${vinculo.dono} :: ${vinculo.tipo}=${vinculo.valor}`);
3529
+ }
3530
+ }
3531
+
3532
+ if (resultado.resumo_operacional.oQueTocar.length > 0) {
3533
+ console.log("- O que tocar primeiro:");
3534
+ for (const alvo of resultado.resumo_operacional.oQueTocar.slice(0, 8)) {
3535
+ console.log(` - ${alvo}`);
3536
+ }
3537
+ }
3538
+
3539
+ if (resultado.resumo_operacional.oQueValidar.length > 0) {
3540
+ console.log("- O que validar:");
3541
+ for (const check of resultado.resumo_operacional.oQueValidar.slice(0, 8)) {
3542
+ console.log(` - ${check}`);
3543
+ }
3544
+ }
3545
+
3546
+ if (resultado.diagnosticos.length === 0) {
3547
+ console.log("Nenhum drift relevante encontrado.");
3548
+ }
3549
+
3550
+ return resultado.sucesso ? 0 : 1;
3551
+ }
3552
+
3553
+ async function comandoImportar(
3554
+ fonte: FonteImportacao,
3555
+ diretorio: string,
3556
+ saida: string,
3557
+ namespaceBase: string | undefined,
3558
+ emJson: boolean,
3559
+ ): Promise<number> {
3560
+ const resultado = await importarProjetoLegado(fonte, diretorio, namespaceBase);
3561
+ const resumo = resumoImportacao(resultado);
3562
+
3563
+ if (!resumo.sucesso) {
3564
+ const payloadErro = {
3565
+ comando: "importar",
3566
+ fonte,
3567
+ diretorio: path.resolve(diretorio),
3568
+ namespaceBase: resultado.namespaceBase,
3569
+ resumo,
3570
+ arquivos: resultado.arquivos.map((arquivo) => ({
3571
+ caminho: path.join(path.resolve(saida), arquivo.caminhoRelativo),
3572
+ modulo: arquivo.modulo,
3573
+ tarefas: arquivo.tarefas,
3574
+ rotas: arquivo.rotas,
3575
+ entidades: arquivo.entidades,
3576
+ enums: arquivo.enums,
3577
+ })),
3578
+ diagnosticos: resultado.diagnosticos,
3579
+ };
3580
+
3581
+ if (emJson) {
3582
+ console.log(JSON.stringify(payloadErro, null, 2));
3583
+ } else {
3584
+ console.error("Falha na importacao assistida. O rascunho gerado ainda nao ficou semanticamente valido.");
3585
+ console.error(formatarDiagnosticos(resultado.diagnosticos));
3586
+ }
3587
+ return 1;
3588
+ }
3589
+
3590
+ await escreverArquivos(saida, resultado.arquivos.map((arquivo) => ({
3591
+ caminhoRelativo: arquivo.caminhoRelativo,
3592
+ conteudo: arquivo.conteudo,
3593
+ })));
3594
+
3595
+ const payload = {
3596
+ comando: "importar",
3597
+ fonte,
3598
+ diretorio: path.resolve(diretorio),
3599
+ saida: path.resolve(saida),
3600
+ namespaceBase: resultado.namespaceBase,
3601
+ resumo,
3602
+ arquivos: resultado.arquivos.map((arquivo) => ({
3603
+ caminho: path.join(path.resolve(saida), arquivo.caminhoRelativo),
3604
+ modulo: arquivo.modulo,
3605
+ tarefas: arquivo.tarefas,
3606
+ rotas: arquivo.rotas,
3607
+ entidades: arquivo.entidades,
3608
+ enums: arquivo.enums,
3609
+ })),
3610
+ };
3611
+
3612
+ if (emJson) {
3613
+ console.log(JSON.stringify(payload, null, 2));
3614
+ return 0;
3615
+ }
3616
+
3617
+ console.log("Importacao assistida para Sema concluida.");
3618
+ console.log(`- Fonte: ${fonte}`);
3619
+ console.log(`- Diretorio analisado: ${payload.diretorio}`);
3620
+ console.log(`- Namespace base: ${payload.namespaceBase}`);
3621
+ console.log(`- Saida: ${payload.saida}`);
3622
+ console.log(`- Modulos: ${resumo.modulos}`);
3623
+ console.log(`- Tarefas: ${resumo.tarefas}`);
3624
+ console.log(`- Rotas: ${resumo.rotas}`);
3625
+ console.log(`- Entidades: ${resumo.entidades}`);
3626
+ console.log(`- Enums: ${resumo.enums}`);
3627
+ console.log("- Arquivos gerados:");
3628
+ for (const arquivo of payload.arquivos) {
3629
+ console.log(` - ${arquivo.caminho} :: modulo=${arquivo.modulo} tarefas=${arquivo.tarefas} rotas=${arquivo.rotas}`);
3630
+ }
3631
+ console.log("Ajuste os rascunhos .sema, rode `sema formatar`, `sema validar --json` e depois `sema compilar`.");
3632
+ return 0;
3633
+ }
3634
+
3635
+ async function comandoAst(arquivo: string): Promise<number> {
3636
+ const codigo = await lerArquivoTexto(arquivo);
3637
+ const resultado = compilarCodigo(codigo, arquivo);
3638
+ console.log(JSON.stringify(resultado.modulo ?? null, null, 2));
3639
+ return temErros(resultado.diagnosticos) ? 1 : 0;
3640
+ }
3641
+
3642
+ async function comandoAstJson(arquivo: string): Promise<number> {
3643
+ const codigo = await lerArquivoTexto(arquivo);
3644
+ const resultado = compilarCodigo(codigo, arquivo);
3645
+ console.log(JSON.stringify({
3646
+ comando: "ast",
3647
+ caminho: path.resolve(arquivo),
3648
+ modulo: resultado.modulo?.nome ?? null,
3649
+ sucesso: !temErros(resultado.diagnosticos),
3650
+ diagnosticos: resultado.diagnosticos,
3651
+ ast: resultado.modulo ?? null,
3652
+ }, null, 2));
3653
+ return temErros(resultado.diagnosticos) ? 1 : 0;
3654
+ }
3655
+
3656
+ async function comandoIr(arquivo: string): Promise<number> {
3657
+ const codigo = await lerArquivoTexto(arquivo);
3658
+ const resultado = compilarCodigo(codigo, arquivo);
3659
+ console.log(JSON.stringify(resultado.ir ?? null, null, 2));
3660
+ return temErros(resultado.diagnosticos) ? 1 : 0;
3661
+ }
3662
+
3663
+ async function comandoIrJson(arquivo: string): Promise<number> {
3664
+ const codigo = await lerArquivoTexto(arquivo);
3665
+ const resultado = compilarCodigo(codigo, arquivo);
3666
+ console.log(JSON.stringify({
3667
+ comando: "ir",
3668
+ caminho: path.resolve(arquivo),
3669
+ modulo: resultado.modulo?.nome ?? null,
3670
+ sucesso: !temErros(resultado.diagnosticos),
3671
+ diagnosticos: resultado.diagnosticos,
3672
+ ir: resultado.ir ?? null,
3673
+ }, null, 2));
3674
+ return temErros(resultado.diagnosticos) ? 1 : 0;
3675
+ }
3676
+
3677
+ async function comandoCompilar(
3678
+ entrada: string | undefined,
3679
+ alvo: AlvoGeracao,
3680
+ saida: string,
3681
+ estrutura: EstruturaSaida,
3682
+ framework: FrameworkGeracao,
3683
+ cwd = process.cwd(),
3684
+ ): Promise<number> {
3685
+ const incompatibilidade = validarCompatibilidadeFramework(alvo, framework);
3686
+ if (incompatibilidade) {
3687
+ console.error(incompatibilidade);
3688
+ return 1;
3689
+ }
3690
+
3691
+ const modulos = await carregarModulos(entrada, cwd);
3692
+ const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
3693
+ if (temErros(diagnosticos)) {
3694
+ console.error(formatarDiagnosticos(diagnosticos));
3695
+ return 1;
3696
+ }
3697
+
3698
+ for (const modulo of modulos) {
3699
+ const ir = garantirIr(modulo.resultado, modulo.caminho);
3700
+ const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
3701
+ await escreverArquivos(saida, arquivos);
3702
+ }
3703
+ console.log(`Compilacao concluida para o alvo ${alvo} com estrutura ${estrutura} e framework ${framework}.`);
3704
+ return 0;
3705
+ }
3706
+
3707
+ async function comandoDiagnosticos(arquivo: string, emJson: boolean): Promise<number> {
3708
+ const codigo = await lerArquivoTexto(arquivo);
3709
+ const resultado = compilarCodigo(codigo, arquivo);
3710
+ if (emJson) {
3711
+ console.log(JSON.stringify(resultado.diagnosticos, null, 2));
3712
+ } else {
3713
+ console.log(formatarDiagnosticos(resultado.diagnosticos));
3714
+ }
3715
+ return temErros(resultado.diagnosticos) ? 1 : 0;
3716
+ }
3717
+
3718
+ async function comandoFormatar(entrada: string | undefined, verificarApenas: boolean, emJson: boolean): Promise<number> {
3719
+ const contextoProjeto = await carregarProjeto(entrada, process.cwd());
3720
+ const entradaResolvida = contextoProjeto.entradaResolvida;
3721
+ const estatisticas = await stat(entradaResolvida);
3722
+ const arquivos = estatisticas.isFile()
3723
+ ? [entradaResolvida]
3724
+ : contextoProjeto.arquivosProjeto.filter((arquivo) => arquivo.startsWith(path.resolve(entradaResolvida)));
3725
+ const resultados: ResultadoFormatacaoArquivo[] = [];
3726
+
3727
+ for (const arquivo of arquivos) {
3728
+ const codigo = await lerArquivoTexto(arquivo);
3729
+ const resultado = formatarCodigo(codigo, arquivo);
3730
+ const sucesso = !temErros(resultado.diagnosticos) && Boolean(resultado.codigoFormatado);
3731
+ resultados.push({
3732
+ caminho: arquivo,
3733
+ alterado: resultado.alterado,
3734
+ sucesso,
3735
+ diagnosticos: resultado.diagnosticos,
3736
+ });
3737
+
3738
+ if (sucesso && !verificarApenas && resultado.alterado && resultado.codigoFormatado) {
3739
+ await writeFile(arquivo, resultado.codigoFormatado, "utf8");
3740
+ }
3741
+ }
3742
+
3743
+ const possuiErros = resultados.some((resultado) => !resultado.sucesso);
3744
+ const possuiDiferencas = resultados.some((resultado) => resultado.alterado);
3745
+ const codigoSaida = possuiErros ? 1 : verificarApenas && possuiDiferencas ? 1 : 0;
3746
+
3747
+ if (emJson) {
3748
+ console.log(JSON.stringify({
3749
+ comando: "formatar",
3750
+ sucesso: codigoSaida === 0,
3751
+ modo: verificarApenas ? "check" : "write",
3752
+ arquivos: resultados,
3753
+ totais: {
3754
+ arquivos: resultados.length,
3755
+ alterados: resultados.filter((resultado) => resultado.alterado).length,
3756
+ erros: resultados.filter((resultado) => !resultado.sucesso).length,
3757
+ },
3758
+ }, null, 2));
3759
+ return codigoSaida;
3760
+ }
3761
+
3762
+ if (possuiErros) {
3763
+ console.error(formatarDiagnosticos(resultados.flatMap((resultado) => resultado.diagnosticos)));
3764
+ return 1;
3765
+ }
3766
+
3767
+ if (verificarApenas) {
3768
+ if (possuiDiferencas) {
3769
+ console.error("Arquivos fora do formato canonico:");
3770
+ for (const resultado of resultados.filter((item) => item.alterado)) {
3771
+ console.error(`- ${resultado.caminho}`);
3772
+ }
3773
+ return 1;
3774
+ }
3775
+ console.log("Todos os arquivos ja estao no formato canonico.");
3776
+ return 0;
3777
+ }
3778
+
3779
+ console.log(`Formatacao concluida. Arquivos verificados=${resultados.length} alterados=${resultados.filter((resultado) => resultado.alterado).length}`);
3780
+ return 0;
3781
+ }
3782
+
3783
+ async function comandoStarterIa(): Promise<number> {
3784
+ const descoberta = await descobrirDocsIa();
3785
+ console.log("Starter de IA da Sema");
3786
+ console.log("");
3787
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3788
+ console.log("");
3789
+ console.log(STARTER_IA);
3790
+ return 0;
3791
+ }
3792
+
3793
+ async function comandoSyncAiEntrypoints(emJson: boolean): Promise<number> {
3794
+ const resumoProjeto = await gerarResumoProjetoIa(process.cwd(), undefined, true);
3795
+ const indexJson = JSON.parse(await readFile(path.join(resumoProjeto.pastaSaida, "SEMA_INDEX.json"), "utf8"));
3796
+ const artefatos = [...new Set([
3797
+ ...ARQUIVOS_CANONICOS_IA_RAIZ,
3798
+ ...resumoProjeto.artefatos,
3799
+ ])];
3800
+
3801
+ if (emJson) {
3802
+ console.log(JSON.stringify({
3803
+ comando: "sync-ai-entrypoints",
3804
+ sucesso: true,
3805
+ baseProjeto: resumoProjeto.baseProjeto,
3806
+ pastaSaida: resumoProjeto.pastaSaida,
3807
+ artefatos,
3808
+ entradaCanonica: indexJson.entradaCanonica,
3809
+ }, null, 2));
3810
+ return 0;
3811
+ }
3812
+
3813
+ console.log("Entrypoints IA-first sincronizados");
3814
+ console.log("");
3815
+ console.log(`Base do projeto: ${resumoProjeto.baseProjeto}`);
3816
+ console.log(`Ordem canonica: ${indexJson.entradaCanonica.ordemLeitura.join(" -> ")}`);
3817
+ console.log(`IA pequena: ${indexJson.entradaCanonica.porCapacidade.pequena.join(" -> ")}`);
3818
+ console.log(`IA media: ${indexJson.entradaCanonica.porCapacidade.media.join(" -> ")}`);
3819
+ console.log(`IA grande: ${indexJson.entradaCanonica.porCapacidade.grande.join(" -> ")}`);
3820
+ return 0;
3821
+ }
3822
+
3823
+ async function comandoAjudaIa(): Promise<number> {
3824
+ const descoberta = await descobrirDocsIa();
3825
+ console.log("Ajuda de IA da Sema");
3826
+ console.log("");
3827
+ console.log(renderizarCaixaAscii([
3828
+ "IA-first para greenfield, edicao guiada e legado sem contrato inicial",
3829
+ "use o menor artefato semantico que resolva a tarefa",
3830
+ ]));
3831
+ console.log("");
3832
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3833
+ console.log("");
3834
+ console.log(renderizarSecaoAscii("Tres jeitos de usar a Sema", [
3835
+ "[1] Producao inicial: modele, valide, compile e verifique antes de subir codigo derivado.",
3836
+ "[2] Edicao em projeto com Sema: inspecione, leia resumo, rode drift e gere contexto antes de editar codigo vivo.",
3837
+ "[3] Projeto sem Sema ainda: importe, revise o rascunho, formate, valide e use drift como juiz da adocao incremental.",
3838
+ ]));
3839
+ console.log("");
3840
+ console.log(renderizarSecaoAscii("Capacidade de IA", [
3841
+ "pequena: `sema resumo --micro`, `briefing.min.json`, `prompt-curto.txt`",
3842
+ "media: `sema resumo --curto`, `drift.json`, `briefing.min.json`",
3843
+ "grande: `sema contexto-ia`, `briefing.json`, `ir.json`, `ast.json`",
3844
+ ]));
3845
+ console.log("");
3846
+ console.log(renderizarSecaoAscii("Fluxo recomendado", [
3847
+ "Use `sema starter-ia` para um texto curto de onboarding.",
3848
+ "Use `sema sync-ai-entrypoints` para regenerar `SEMA_BRIEF.*` e `SEMA_INDEX.json` na raiz.",
3849
+ "Use `sema resumo <arquivo> --micro --para onboarding` para IA pequena.",
3850
+ "Use `sema prompt-curto <arquivo> --curto --para mudanca` para colar contexto em modelo gratuito.",
3851
+ "Use `sema prompt-ia`, `sema prompt-ia-ui`, `sema prompt-ia-react` e `sema prompt-ia-sema-primeiro` conforme a tarefa.",
3852
+ "Use `sema exemplos-prompt-ia` para pegar modelos prontos de prompt.",
3853
+ "Use `sema inspecionar` para descobrir base, codigo vivo e fontes legado.",
3854
+ "Use `sema drift` para medir impls, vinculos, rotas, score e lacunas.",
3855
+ "Use `sema contexto-ia <arquivo.sema>` para gerar AST, IR, drift, `briefing.json` e `briefing.min.json`.",
3856
+ "Use `sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart|lua> --saida <diretorio>` quando a tarefa pedir codigo derivado.",
3857
+ ]));
3858
+ console.log("");
3859
+ console.log(renderizarSecaoAscii("Regras praticas", [
3860
+ "Foi feita para IA operar melhor; leitura humana e consequencia, nao centro de produto.",
3861
+ "Governa contrato, intencao, erro, efeito, garantia, fluxo, vinculos e execucao.",
3862
+ "Nao escreve contrato final sozinho nem substitui decisao arquitetural.",
3863
+ "Se voce quer testar a Sema de verdade, nao peca so HTML solto.",
3864
+ "Peca `.sema` + arquitetura + React + TypeScript, ou use o modo `Sema primeiro`.",
3865
+ "Se o projeto ja existe, trate `importar` como rascunho e `drift` como juiz.",
3866
+ "IA pequena comeca no menor artefato que resolve a tarefa; nao enfie `ast.json` inteiro nela de bobeira.",
3867
+ "Antes de editar software vivo, leia `briefing.min.json` ou `briefing.json` em vez de sair cavando arquivo na fe.",
3868
+ "Trate `route`, `worker`, `evento`, `fila`, `cron`, `webhook`, `cache`, `storage` e `policy` como superficies de primeira classe.",
3869
+ ]));
3870
+ return 0;
3871
+ }
3872
+
3873
+ async function comandoPromptIa(): Promise<number> {
3874
+ const descoberta = await descobrirDocsIa();
3875
+ console.log("Prompt-base de IA da Sema");
3876
+ console.log("");
3877
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3878
+ console.log("");
3879
+ console.log(PROMPT_BASE_IA);
3880
+ return 0;
3881
+ }
3882
+
3883
+ async function comandoPromptIaUi(): Promise<number> {
3884
+ const descoberta = await descobrirDocsIa();
3885
+ console.log("Prompt de IA da Sema para UI");
3886
+ console.log("");
3887
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3888
+ console.log("");
3889
+ console.log(PROMPT_IA_UI);
3890
+ return 0;
3891
+ }
3892
+
3893
+ async function comandoPromptIaReact(): Promise<number> {
3894
+ const descoberta = await descobrirDocsIa();
3895
+ console.log("Prompt de IA da Sema para React");
3896
+ console.log("");
3897
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3898
+ console.log("");
3899
+ console.log(PROMPT_IA_REACT);
3900
+ return 0;
3901
+ }
3902
+
3903
+ async function comandoPromptIaSemaPrimeiro(): Promise<number> {
3904
+ const descoberta = await descobrirDocsIa();
3905
+ console.log("Prompt de IA da Sema no modo Sema primeiro");
3906
+ console.log("");
3907
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3908
+ console.log("");
3909
+ console.log(PROMPT_IA_SEMA_PRIMEIRO);
3910
+ return 0;
3911
+ }
3912
+
3913
+ async function comandoExemplosPromptIa(): Promise<number> {
3914
+ const descoberta = await descobrirDocsIa();
3915
+ console.log("Exemplos de prompt de IA da Sema");
3916
+ console.log("");
3917
+ console.log(renderizarCabecalhoDocsIa(descoberta));
3918
+ console.log("");
3919
+ console.log(EXEMPLOS_PROMPT_IA);
3920
+ return 0;
3921
+ }
3922
+
3923
+ async function comandoResumo(
3924
+ entrada: string | undefined,
3925
+ args: string[],
3926
+ emJson: boolean,
3927
+ ): Promise<number> {
3928
+ const tamanho = normalizarTamanhoResumo(args);
3929
+ const modo = normalizarModoResumo(obterOpcao(args, "--para"));
3930
+ const pastaSaida = obterOpcao(args, "--saida");
3931
+ const escreverNaRaiz = possuiFlag(args, "--raiz");
3932
+ const alvo = entrada ? path.resolve(process.cwd(), entrada) : process.cwd();
3933
+
3934
+ if (entrada && entrada.toLowerCase().endsWith(".sema")) {
3935
+ const contexto = await carregarContextoModuloIa(alvo);
3936
+ const resumoSemantico = coletarResumoSemanticoModulo(contexto);
3937
+ const guiaPorCapacidade = criarGuiaCapacidadeIa();
3938
+ const texto = tamanho === "medio"
3939
+ ? renderizarResumoModuloMarkdown(resumoSemantico, modo, guiaPorCapacidade)
3940
+ : renderizarResumoModuloTexto(resumoSemantico, tamanho, modo);
3941
+
3942
+ let pastaResumo: string | undefined;
3943
+ let artefatosCompactos: string[] = [];
3944
+ if (pastaSaida) {
3945
+ pastaResumo = path.resolve(pastaSaida);
3946
+ await mkdir(pastaResumo, { recursive: true });
3947
+ const gerado = await gerarArquivosResumoModuloIa(contexto, pastaResumo);
3948
+ artefatosCompactos = gerado.artefatosCompactos;
3949
+ }
3950
+
3951
+ if (emJson) {
3952
+ console.log(JSON.stringify({
3953
+ comando: "resumo",
3954
+ modo,
3955
+ tamanho,
3956
+ geradoEm: contexto.geradoEm,
3957
+ arquivo: contexto.arquivo,
3958
+ modulo: contexto.modulo,
3959
+ pastaSaida: pastaResumo ?? null,
3960
+ artefatosCompactos,
3961
+ guiaPorCapacidade,
3962
+ resumo: resumoSemantico,
3963
+ texto,
3964
+ }, null, 2));
3965
+ return 0;
3966
+ }
3967
+
3968
+ if (pastaResumo) {
3969
+ console.log(`Resumo IA-first gerado em ${pastaResumo}`);
3970
+ console.log("");
3971
+ }
3972
+ console.log(texto);
3973
+ return 0;
3974
+ }
3975
+
3976
+ const resumoProjeto = await gerarResumoProjetoIa(alvo, pastaSaida, escreverNaRaiz);
3977
+ const arquivoResumo = tamanho === "micro"
3978
+ ? "SEMA_BRIEF.micro.txt"
3979
+ : tamanho === "curto"
3980
+ ? "SEMA_BRIEF.curto.txt"
3981
+ : "SEMA_BRIEF.md";
3982
+ const texto = await readFile(path.join(resumoProjeto.pastaSaida, arquivoResumo), "utf8");
3983
+
3984
+ if (emJson) {
3985
+ console.log(JSON.stringify({
3986
+ comando: "resumo",
3987
+ modo,
3988
+ tamanho,
3989
+ geradoEm: resumoProjeto.geradoEm,
3990
+ baseProjeto: resumoProjeto.baseProjeto,
3991
+ pastaSaida: resumoProjeto.pastaSaida,
3992
+ artefatos: resumoProjeto.artefatos,
3993
+ guiaPorCapacidade: resumoProjeto.guiaPorCapacidade,
3994
+ modulos: resumoProjeto.modulos,
3995
+ texto,
3996
+ }, null, 2));
3997
+ return 0;
3998
+ }
3999
+
4000
+ console.log(`Resumo IA-first do projeto gerado em ${resumoProjeto.pastaSaida}`);
4001
+ console.log("");
4002
+ console.log(texto);
4003
+ return 0;
4004
+ }
4005
+
4006
+ async function comandoPromptCurto(
4007
+ entrada: string | undefined,
4008
+ args: string[],
4009
+ emJson: boolean,
4010
+ ): Promise<number> {
4011
+ const tamanho = normalizarTamanhoResumo(args);
4012
+ const modo = normalizarModoResumo(obterOpcao(args, "--para"));
4013
+ const alvo = entrada ? path.resolve(process.cwd(), entrada) : process.cwd();
4014
+
4015
+ if (entrada && entrada.toLowerCase().endsWith(".sema")) {
4016
+ const contexto = await carregarContextoModuloIa(alvo);
4017
+ const resumoSemantico = coletarResumoSemanticoModulo(contexto);
4018
+ const capacidade: CapacidadeIa = tamanho === "micro" ? "pequena" : tamanho === "curto" ? "media" : "grande";
4019
+ const prompt = criarPromptCurtoModulo(resumoSemantico, modo, tamanho, capacidade);
4020
+
4021
+ if (emJson) {
4022
+ console.log(JSON.stringify({
4023
+ comando: "prompt-curto",
4024
+ modo,
4025
+ tamanho,
4026
+ capacidade,
4027
+ geradoEm: contexto.geradoEm,
4028
+ arquivo: contexto.arquivo,
4029
+ modulo: contexto.modulo,
4030
+ prompt,
4031
+ }, null, 2));
4032
+ return 0;
4033
+ }
4034
+
4035
+ console.log(prompt);
4036
+ return 0;
4037
+ }
4038
+
4039
+ const resumoProjeto = await gerarResumoProjetoIa(alvo);
4040
+ const arquivoResumo = tamanho === "micro"
4041
+ ? "SEMA_BRIEF.micro.txt"
4042
+ : tamanho === "curto"
4043
+ ? "SEMA_BRIEF.curto.txt"
4044
+ : "SEMA_BRIEF.md";
4045
+ const contextoProjeto = await readFile(path.join(resumoProjeto.pastaSaida, arquivoResumo), "utf8");
4046
+ const capacidade: CapacidadeIa = tamanho === "micro" ? "pequena" : tamanho === "curto" ? "media" : "grande";
4047
+ const prompt = `Voce esta operando Sema em modo IA-first.
4048
+
4049
+ Isto nao e material feito para humano; e contexto comprimido para IA.
4050
+
4051
+ Capacidade alvo: ${capacidade}
4052
+ Modo da tarefa: ${modo}
4053
+
4054
+ Regras:
4055
+ - comece pelo resumo compacto abaixo
4056
+ - se a tarefa pedir mais contexto, abra \`SEMA_INDEX.json\`
4057
+ - nao tente ler o repo inteiro se o resumo ja disser onde tocar
4058
+ - preserve contrato, risco, lacuna e checks sugeridos
4059
+
4060
+ Contexto do projeto:
4061
+ ${contextoProjeto.trim()}
4062
+ `;
4063
+
4064
+ if (emJson) {
4065
+ console.log(JSON.stringify({
4066
+ comando: "prompt-curto",
4067
+ modo,
4068
+ tamanho,
4069
+ capacidade,
4070
+ geradoEm: resumoProjeto.geradoEm,
4071
+ baseProjeto: resumoProjeto.baseProjeto,
4072
+ pastaSaida: resumoProjeto.pastaSaida,
4073
+ prompt,
4074
+ }, null, 2));
4075
+ return 0;
4076
+ }
4077
+
4078
+ console.log(prompt);
4079
+ return 0;
4080
+ }
4081
+
4082
+ async function comandoContextoIa(arquivo: string, pastaSaida: string | undefined, emJson: boolean): Promise<number> {
4083
+ const resultado = await gerarContextoIa(arquivo, pastaSaida);
4084
+
4085
+ if (emJson) {
4086
+ console.log(JSON.stringify(resultado, null, 2));
4087
+ return 0;
4088
+ }
4089
+
4090
+ const resumoGerado = await readFile(path.join(resultado.pastaSaida, "README.md"), "utf8");
4091
+ console.log(`Pacote de contexto gerado em ${resultado.pastaSaida}`);
4092
+ console.log("");
4093
+ console.log(resumoGerado);
4094
+ return 0;
4095
+ }
4096
+
4097
+ async function comandoTestar(
4098
+ arquivo: string,
4099
+ alvo: AlvoGeracao,
4100
+ saida: string,
4101
+ estrutura: EstruturaSaida,
4102
+ framework: FrameworkGeracao,
4103
+ ): Promise<number> {
4104
+ const incompatibilidade = validarCompatibilidadeFramework(alvo, framework);
4105
+ if (incompatibilidade) {
4106
+ console.error(incompatibilidade);
4107
+ return 1;
4108
+ }
4109
+ const codigo = await lerArquivoTexto(arquivo);
4110
+ const resultado = compilarCodigo(codigo, arquivo);
4111
+ if (temErros(resultado.diagnosticos)) {
4112
+ console.error(formatarDiagnosticos(resultado.diagnosticos));
4113
+ return 1;
4114
+ }
4115
+ const ir = garantirIr(resultado, arquivo);
4116
+ const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
4117
+ await escreverArquivos(saida, arquivos);
4118
+ if (framework !== "base") {
4119
+ console.log(`Scaffold ${framework} gerado em ${saida}. A execucao automatica de testes continua focada no framework base da Sema.`);
4120
+ return 0;
4121
+ }
4122
+ return executarTestesGerados(alvo, saida, arquivos).codigoSaida;
4123
+ }
4124
+
4125
+ function imprimirResumoVerificacao(resumos: ResumoModuloVerificacao[]): void {
4126
+ console.log("\nResumo da verificacao:");
4127
+ let totalArquivos = 0;
4128
+ let totalTestes = 0;
4129
+ let totalAlvos = 0;
4130
+
4131
+ for (const resumo of resumos) {
4132
+ console.log(`- Modulo ${resumo.modulo} (${resumo.arquivoFonte})`);
4133
+ for (const alvo of resumo.alvos) {
4134
+ totalArquivos += alvo.arquivosGerados;
4135
+ totalTestes += alvo.quantidadeTestes;
4136
+ totalAlvos += 1;
4137
+ console.log(
4138
+ ` alvo=${alvo.alvo} framework=${alvo.framework} estrutura=${alvo.estrutura} status=${alvo.sucesso ? "ok" : "falhou"} arquivos=${alvo.arquivosGerados} testes=${alvo.quantidadeTestes}${alvo.testesExecutados ? "" : " (skip)"} saida=${alvo.pastaSaida}`,
4139
+ );
4140
+ }
4141
+ }
4142
+
4143
+ console.log(`Totais: modulos=${resumos.length} alvos=${totalAlvos} arquivos=${totalArquivos} testes=${totalTestes}`);
4144
+ }
4145
+
4146
+ async function comandoVerificar(
4147
+ entrada: string | undefined,
4148
+ baseSaida: string,
4149
+ cwd = process.cwd(),
4150
+ ): Promise<number> {
4151
+ const contextoProjeto = await carregarProjeto(entrada, cwd);
4152
+ const modulos = contextoProjeto.modulosSelecionados;
4153
+ const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
4154
+ if (temErros(diagnosticos)) {
4155
+ console.error(formatarDiagnosticos(diagnosticos));
4156
+ return 1;
4157
+ }
4158
+
4159
+ const alvos = resolverAlvosVerificacao(contextoProjeto.configCarregada);
4160
+ const configuracoesAlvo = alvos.map((alvo) => ({
4161
+ alvo,
4162
+ ...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
4163
+ }));
4164
+ const incompatibilidade = configuracoesAlvo.find((item) => item.incompatibilidade);
4165
+ if (incompatibilidade?.incompatibilidade) {
4166
+ console.error(incompatibilidade.incompatibilidade);
4167
+ return 1;
4168
+ }
4169
+
4170
+ const resumos: ResumoModuloVerificacao[] = [];
4171
+ for (const modulo of modulos) {
4172
+ const ir = garantirIr(modulo.resultado, modulo.caminho);
4173
+ console.log(`Verificando modulo ${modulo.caminho}`);
4174
+ const resumoModulo: ResumoModuloVerificacao = {
4175
+ modulo: ir.nome,
4176
+ arquivoFonte: modulo.caminho,
4177
+ alvos: [],
4178
+ };
4179
+ for (const configuracaoAlvo of configuracoesAlvo) {
4180
+ const { alvo, framework, estrutura } = configuracaoAlvo;
4181
+ const pastaAlvo = path.join(baseSaida, alvo, nomeSubpastaModulo(modulo.caminho));
4182
+ const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
4183
+ await escreverArquivos(pastaAlvo, arquivos);
4184
+ const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework);
4185
+ resumoModulo.alvos.push({
4186
+ alvo,
4187
+ arquivosGerados: arquivos.length,
4188
+ quantidadeTestes: execucao.quantidadeTestes,
4189
+ pastaSaida: pastaAlvo,
4190
+ sucesso: execucao.codigoSaida === 0,
4191
+ framework,
4192
+ estrutura,
4193
+ testesExecutados,
4194
+ });
4195
+ if (execucao.codigoSaida !== 0) {
4196
+ imprimirResumoVerificacao([...resumos, resumoModulo]);
4197
+ console.error(`Falha na verificacao do modulo ${modulo.caminho} para o alvo ${alvo}.`);
4198
+ return execucao.codigoSaida;
4199
+ }
4200
+ }
4201
+ resumos.push(resumoModulo);
4202
+ }
4203
+
4204
+ imprimirResumoVerificacao(resumos);
4205
+ console.log("Verificacao completa concluida com sucesso.");
4206
+ return 0;
4207
+ }
4208
+
4209
+ async function comandoVerificarJson(
4210
+ entrada: string | undefined,
4211
+ baseSaida: string,
4212
+ cwd = process.cwd(),
4213
+ ): Promise<number> {
4214
+ const contextoProjeto = await carregarProjeto(entrada, cwd);
4215
+ const modulos = contextoProjeto.modulosSelecionados;
4216
+ const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
4217
+ if (temErros(diagnosticos)) {
4218
+ console.log(JSON.stringify({
4219
+ comando: "verificar",
4220
+ sucesso: false,
4221
+ diagnosticos,
4222
+ modulos: [],
4223
+ totais: { modulos: 0, alvos: 0, arquivos: 0, testes: 0 },
4224
+ }, null, 2));
4225
+ return 1;
4226
+ }
4227
+
4228
+ const alvos = resolverAlvosVerificacao(contextoProjeto.configCarregada);
4229
+ const configuracoesAlvo = alvos.map((alvo) => ({
4230
+ alvo,
4231
+ ...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
4232
+ }));
4233
+ const incompatibilidade = configuracoesAlvo.find((item) => item.incompatibilidade);
4234
+ if (incompatibilidade?.incompatibilidade) {
4235
+ console.log(JSON.stringify({
4236
+ comando: "verificar",
4237
+ sucesso: false,
4238
+ erro: incompatibilidade.incompatibilidade,
4239
+ modulos: [],
4240
+ totais: { modulos: 0, alvos: 0, arquivos: 0, testes: 0 },
4241
+ }, null, 2));
4242
+ return 1;
4243
+ }
4244
+
4245
+ const resumos: Array<ResumoModuloVerificacao & { saidaTestes?: Array<{ alvo: string; stdout: string; stderr: string }> }> = [];
4246
+ let codigoSaida = 0;
4247
+
4248
+ for (const modulo of modulos) {
4249
+ const ir = garantirIr(modulo.resultado, modulo.caminho);
4250
+ const resumoModulo: ResumoModuloVerificacao & { saidaTestes: Array<{ alvo: string; stdout: string; stderr: string }> } = {
4251
+ modulo: ir.nome,
4252
+ arquivoFonte: modulo.caminho,
4253
+ alvos: [],
4254
+ saidaTestes: [],
4255
+ };
4256
+
4257
+ for (const configuracaoAlvo of configuracoesAlvo) {
4258
+ const { alvo, framework, estrutura } = configuracaoAlvo;
4259
+ const pastaAlvo = path.join(baseSaida, alvo, nomeSubpastaModulo(modulo.caminho));
4260
+ const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
4261
+ await escreverArquivos(pastaAlvo, arquivos);
4262
+ const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework, true);
4263
+ resumoModulo.alvos.push({
4264
+ alvo,
4265
+ arquivosGerados: arquivos.length,
4266
+ quantidadeTestes: execucao.quantidadeTestes,
4267
+ pastaSaida: pastaAlvo,
4268
+ sucesso: execucao.codigoSaida === 0,
4269
+ framework,
4270
+ estrutura,
4271
+ testesExecutados,
4272
+ });
4273
+ resumoModulo.saidaTestes.push({ alvo, stdout: execucao.saidaPadrao, stderr: execucao.saidaErro });
4274
+ if (execucao.codigoSaida !== 0) {
4275
+ codigoSaida = execucao.codigoSaida;
4276
+ }
4277
+ }
4278
+
4279
+ resumos.push(resumoModulo);
4280
+ }
4281
+
4282
+ const totais = {
4283
+ modulos: resumos.length,
4284
+ alvos: resumos.reduce((total, resumo) => total + resumo.alvos.length, 0),
4285
+ arquivos: resumos.reduce((total, resumo) => total + resumo.alvos.reduce((subTotal, alvo) => subTotal + alvo.arquivosGerados, 0), 0),
4286
+ testes: resumos.reduce((total, resumo) => total + resumo.alvos.reduce((subTotal, alvo) => subTotal + alvo.quantidadeTestes, 0), 0),
4287
+ };
4288
+
4289
+ console.log(JSON.stringify({
4290
+ comando: "verificar",
4291
+ sucesso: codigoSaida === 0,
4292
+ modulos: resumos,
4293
+ totais,
4294
+ }, null, 2));
4295
+
4296
+ return codigoSaida;
4297
+ }
4298
+
4299
+ async function principal(): Promise<void> {
4300
+ const { comando, resto } = obterArgumentos();
4301
+ const comandoCru = process.argv[2];
4302
+ if (comandoCru === "--versao" || comandoCru === "--version" || comandoCru === "-v") {
4303
+ console.log(VERSAO_CLI);
4304
+ process.exit(0);
4305
+ }
4306
+ if (!comando || comandoCru === "--help" || comandoCru === "-h") {
4307
+ console.log(ajuda());
4308
+ process.exit(0);
4309
+ }
4310
+
4311
+ const cwd = process.cwd();
4312
+ const posicionais = obterPosicionais(resto);
4313
+ let codigoSaida = 0;
4314
+ switch (comando) {
4315
+ case "iniciar":
4316
+ codigoSaida = await comandoIniciar(cwd, normalizarTemplateIniciar(obterOpcao(resto, "--template")));
4317
+ break;
4318
+ case "validar":
4319
+ codigoSaida = possuiFlag(resto, "--json")
4320
+ ? await comandoValidarJson(posicionais[0])
4321
+ : await comandoValidar(posicionais[0]);
4322
+ break;
4323
+ case "ast":
4324
+ codigoSaida = possuiFlag(resto, "--json")
4325
+ ? await comandoAstJson(resto[0] ?? "")
4326
+ : await comandoAst(resto[0] ?? "");
4327
+ break;
4328
+ case "ir":
4329
+ codigoSaida = possuiFlag(resto, "--json")
4330
+ ? await comandoIrJson(resto[0] ?? "")
4331
+ : await comandoIr(resto[0] ?? "");
4332
+ break;
4333
+ case "compilar":
4334
+ {
4335
+ const config = await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd);
4336
+ const alvo = resolverAlvoPadrao(obterOpcao(resto, "--alvo"), config);
4337
+ const framework = resolverFrameworkPadrao(obterOpcao(resto, "--framework"), config);
4338
+ const estrutura = resolverEstruturaSaidaPadrao(obterOpcao(resto, "--estrutura"), framework, config);
4339
+ const saida = resolverSaidaPadrao(obterOpcao(resto, "--saida"), alvo, config);
4340
+ codigoSaida = await comandoCompilar(posicionais[0], alvo, saida, estrutura, framework, cwd);
4341
+ }
4342
+ break;
4343
+ case "gerar":
4344
+ {
4345
+ const config = await carregarConfiguracaoProjeto(posicionais[1] ? path.resolve(cwd, posicionais[1]) : cwd);
4346
+ const alvo = resolverAlvoPadrao(posicionais[0] ?? obterOpcao(resto, "--alvo"), config);
4347
+ const framework = resolverFrameworkPadrao(obterOpcao(resto, "--framework"), config);
4348
+ const estrutura = resolverEstruturaSaidaPadrao(obterOpcao(resto, "--estrutura"), framework, config);
4349
+ const saida = resolverSaidaPadrao(obterOpcao(resto, "--saida"), alvo, config);
4350
+ codigoSaida = await comandoCompilar(posicionais[1], alvo, saida, estrutura, framework, cwd);
4351
+ }
4352
+ break;
4353
+ case "diagnosticos":
4354
+ codigoSaida = await comandoDiagnosticos(posicionais[0] ?? "", resto.includes("--json"));
4355
+ break;
4356
+ case "testar":
4357
+ {
4358
+ const config = await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd);
4359
+ const alvo = resolverAlvoPadrao(obterOpcao(resto, "--alvo"), config);
4360
+ const framework = resolverFrameworkPadrao(obterOpcao(resto, "--framework"), config);
4361
+ const estrutura = resolverEstruturaSaidaPadrao(obterOpcao(resto, "--estrutura"), framework, config);
4362
+ const saida = resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-testes"), alvo, config);
4363
+ codigoSaida = await comandoTestar(
4364
+ path.resolve(cwd, posicionais[0] ?? ""),
4365
+ alvo,
4366
+ saida,
4367
+ estrutura,
4368
+ framework,
4369
+ );
4370
+ }
4371
+ break;
4372
+ case "verificar":
4373
+ codigoSaida = possuiFlag(resto, "--json")
4374
+ ? await comandoVerificarJson(
4375
+ posicionais[0],
4376
+ resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)),
4377
+ cwd,
4378
+ )
4379
+ : await comandoVerificar(
4380
+ posicionais[0],
4381
+ resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)),
4382
+ cwd,
4383
+ );
4384
+ break;
4385
+ case "inspecionar":
4386
+ codigoSaida = await comandoInspecionar(posicionais[0], possuiFlag(resto, "--json"), cwd);
4387
+ break;
4388
+ case "drift":
4389
+ codigoSaida = await comandoDrift(posicionais[0], possuiFlag(resto, "--json"), cwd);
4390
+ break;
4391
+ case "importar":
4392
+ {
4393
+ const fonte = normalizarFonteImportacao(posicionais[0]);
4394
+ if (!fonte || !posicionais[1]) {
4395
+ 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]");
4396
+ codigoSaida = 1;
4397
+ break;
4398
+ }
4399
+ codigoSaida = await comandoImportar(
4400
+ fonte,
4401
+ path.resolve(cwd, posicionais[1]),
4402
+ path.resolve(cwd, obterOpcao(resto, "--saida", "./sema/importado")!),
4403
+ obterOpcao(resto, "--namespace"),
4404
+ possuiFlag(resto, "--json"),
4405
+ );
4406
+ }
4407
+ break;
4408
+ case "formatar":
4409
+ codigoSaida = await comandoFormatar(
4410
+ posicionais[0],
4411
+ possuiFlag(resto, "--check"),
4412
+ possuiFlag(resto, "--json"),
4413
+ );
4414
+ break;
4415
+ case "doctor":
4416
+ codigoSaida = await comandoDoctor();
4417
+ break;
4418
+ case "ajuda-ia":
4419
+ codigoSaida = await comandoAjudaIa();
4420
+ break;
4421
+ case "starter-ia":
4422
+ codigoSaida = await comandoStarterIa();
4423
+ break;
4424
+ case "sync-ai-entrypoints":
4425
+ codigoSaida = await comandoSyncAiEntrypoints(possuiFlag(resto, "--json"));
4426
+ break;
4427
+ case "resumo":
4428
+ codigoSaida = await comandoResumo(
4429
+ posicionais[0],
4430
+ resto,
4431
+ possuiFlag(resto, "--json"),
4432
+ );
4433
+ break;
4434
+ case "prompt-curto":
4435
+ codigoSaida = await comandoPromptCurto(
4436
+ posicionais[0],
4437
+ resto,
4438
+ possuiFlag(resto, "--json"),
4439
+ );
4440
+ break;
4441
+ case "prompt-ia":
4442
+ codigoSaida = await comandoPromptIa();
4443
+ break;
4444
+ case "prompt-ia-ui":
4445
+ codigoSaida = await comandoPromptIaUi();
4446
+ break;
4447
+ case "prompt-ia-react":
4448
+ codigoSaida = await comandoPromptIaReact();
4449
+ break;
4450
+ case "prompt-ia-sema-primeiro":
4451
+ codigoSaida = await comandoPromptIaSemaPrimeiro();
4452
+ break;
4453
+ case "exemplos-prompt-ia":
4454
+ codigoSaida = await comandoExemplosPromptIa();
4455
+ break;
4456
+ case "contexto-ia":
4457
+ codigoSaida = await comandoContextoIa(
4458
+ posicionais[0] ?? "",
4459
+ obterOpcao(resto, "--saida"),
4460
+ possuiFlag(resto, "--json"),
4461
+ );
4462
+ break;
4463
+ default:
4464
+ console.log(ajuda());
4465
+ codigoSaida = 1;
4466
+ break;
4467
+ }
4468
+
4469
+ process.exit(codigoSaida);
4470
+ }
4471
+
4472
+ principal().catch((erro) => {
4473
+ console.error("Falha ao executar a CLI da Sema.");
4474
+ console.error(erro instanceof Error ? erro.stack ?? erro.message : erro);
4475
+ process.exit(1);
4476
+ });