@semacode/cli 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import { gerarTypeScript } from "@sema/gerador-typescript";
12
12
  import { carregarConfiguracaoProjeto, carregarProjeto, resolverAlvoPadrao, resolverAlvosVerificacao, resolverEstruturaSaidaPadrao, resolverFrameworkPadrao, resolverSaidaPadrao, } from "./projeto.js";
13
13
  import { importarProjetoLegado, resumoImportacao } from "./importador.js";
14
14
  import { analisarDriftLegado } from "./drift.js";
15
- const STARTER_IA = `Voce esta trabalhando com Sema, um Protocolo de Governanca de Intencao para IA e backend vivo.
15
+ const STARTER_IA = `Voce esta trabalhando com Sema, um Protocolo de Governanca de Intencao para IA sobre software vivo em backend e front consumer.
16
16
 
17
17
  Importante:
18
18
  - a Sema se apresenta publicamente como protocolo e funciona tecnicamente como linguagem de intencao
@@ -56,7 +56,7 @@ Comandos essenciais:
56
56
  - validacao: \`sema validar <arquivo.sema> --json\`
57
57
  - diagnosticos: \`sema diagnosticos <arquivo.sema> --json\`
58
58
  - formatacao: \`sema formatar <arquivo.sema>\`
59
- - importacao assistida de legado: \`sema importar <nestjs|fastapi|flask|nextjs|firebase|dotnet|java|go|rust|cpp|typescript|python|dart> <diretorio> --saida <diretorio>\`
59
+ - 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>\`
60
60
  - geracao de codigo: \`sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart> --saida <diretorio>\`
61
61
  - verificacao final: \`sema verificar <arquivo-ou-pasta> [--json]\`
62
62
 
@@ -312,42 +312,106 @@ Comandos uteis da CLI para esse fluxo:
312
312
  `;
313
313
  const DIRETORIO_CLI_ATUAL = path.dirname(fileURLToPath(import.meta.url));
314
314
  const VERSAO_CLI = pacoteCli.version;
315
+ const ARQUIVOS_CANONICOS_IA_RAIZ = [
316
+ "llms.txt",
317
+ "SEMA_BRIEF.md",
318
+ "SEMA_INDEX.json",
319
+ "AGENTS.md",
320
+ "README.md",
321
+ "llms-full.txt",
322
+ ];
323
+ const DOCUMENTOS_SUPORTE_IA = [
324
+ "docs/AGENT_STARTER.md",
325
+ "docs/integracao-com-ia.md",
326
+ "docs/fluxo-pratico-ia-sema.md",
327
+ "docs/como-ensinar-a-sema-para-ia.md",
328
+ "docs/sintaxe.md",
329
+ "docs/cli.md",
330
+ ];
315
331
  function obterArgumentos() {
316
332
  const [, , comando, ...resto] = process.argv;
317
333
  return { comando: comando, resto };
318
334
  }
335
+ function renderizarCaixaAscii(linhas) {
336
+ const largura = Math.max(...linhas.map((linha) => linha.length), 12);
337
+ const borda = `+${"-".repeat(largura + 2)}+`;
338
+ return [
339
+ borda,
340
+ ...linhas.map((linha) => `| ${linha.padEnd(largura, " ")} |`),
341
+ borda,
342
+ ].join("\n");
343
+ }
344
+ function renderizarSecaoAscii(titulo, linhas) {
345
+ return [
346
+ titulo,
347
+ ...linhas.map((linha) => ` ${linha}`),
348
+ ].join("\n");
349
+ }
319
350
  function ajuda() {
320
- return `Sema CLI
321
-
322
- Comandos:
323
- sema --versao
324
- sema --version
325
- sema -v
326
- sema iniciar
327
- sema validar <arquivo-ou-pasta>
328
- sema ast <arquivo.sema>
329
- sema ir <arquivo.sema>
330
- sema compilar <arquivo-ou-pasta> --alvo <python|typescript|dart> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]
331
- sema gerar <python|typescript|dart> <arquivo-ou-pasta> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]
332
- sema testar <arquivo.sema> --alvo <python|typescript|dart> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]
333
- sema diagnosticos <arquivo.sema> [--json]
334
- sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--json]
335
- sema inspecionar [arquivo-ou-pasta] [--json]
336
- sema drift <arquivo-ou-pasta> [--json]
337
- sema importar <nestjs|fastapi|flask|nextjs|firebase|dotnet|java|go|rust|cpp|typescript|python|dart> <diretorio> [--saida <diretorio>] [--namespace <base>] [--json]
338
- sema doctor
339
- sema formatar <arquivo-ou-pasta> [--check] [--json]
340
- sema ajuda-ia
341
- sema starter-ia
342
- sema resumo <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>] [--saida <diretorio>] [--raiz] [--json]
343
- sema prompt-curto <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>] [--json]
344
- sema prompt-ia
345
- sema prompt-ia-ui
346
- sema prompt-ia-react
347
- sema prompt-ia-sema-primeiro
348
- sema exemplos-prompt-ia
349
- sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--json]
350
- `;
351
+ return [
352
+ renderizarCaixaAscii([
353
+ `Sema CLI v${VERSAO_CLI}`,
354
+ "IA-first para contrato, geracao e adocao incremental",
355
+ "novo projeto, edicao guiada e legado sem contrato inicial",
356
+ ]),
357
+ "",
358
+ renderizarSecaoAscii("Fluxos rapidos", [
359
+ "[1] Projeto novo / producao inicial",
360
+ "sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer>",
361
+ "sema validar contratos/<modulo>.sema --json",
362
+ "sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart> --saida <diretorio>",
363
+ "sema verificar <arquivo-ou-pasta> --json",
364
+ "",
365
+ "[2] Editar projeto que ja usa Sema",
366
+ "sema inspecionar . --json",
367
+ "sema resumo <arquivo-ou-pasta> --micro --para mudanca",
368
+ "sema drift <arquivo-ou-pasta> --json",
369
+ "sema contexto-ia <arquivo.sema> --saida ./.tmp/contexto --json",
370
+ "",
371
+ "[3] Adotar Sema em projeto que ainda nao usa",
372
+ "sema importar <fonte> <diretorio> --saida <diretorio> --json",
373
+ "sema formatar <arquivo-ou-pasta>",
374
+ "sema validar <arquivo-ou-pasta> --json",
375
+ "sema drift <arquivo-ou-pasta> --json",
376
+ ]),
377
+ "",
378
+ renderizarSecaoAscii("IA por capacidade", [
379
+ "pequena: sema resumo --micro + briefing.min.json + prompt-curto.txt",
380
+ "media: sema resumo --curto + drift.json + briefing.min.json",
381
+ "grande: sema contexto-ia + briefing.json + ir.json + ast.json",
382
+ ]),
383
+ "",
384
+ renderizarSecaoAscii("Comandos principais", [
385
+ "descoberta: sema inspecionar [arquivo-ou-pasta] [--json]",
386
+ "auditoria: sema drift <arquivo-ou-pasta> [--json]",
387
+ "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]",
388
+ "validacao: sema validar <arquivo-ou-pasta> [--json]",
389
+ "diagnostico: sema diagnosticos <arquivo.sema> [--json]",
390
+ "geracao: sema compilar <arquivo-ou-pasta> --alvo <python|typescript|dart> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
391
+ "teste local: sema testar <arquivo.sema> --alvo <python|typescript|dart> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
392
+ "verificacao final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--json]",
393
+ "formatacao: sema formatar <arquivo-ou-pasta> [--check] [--json]",
394
+ ]),
395
+ "",
396
+ renderizarSecaoAscii("Ajuda IA-first", [
397
+ "sema ajuda-ia",
398
+ "sema starter-ia",
399
+ "sema resumo <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>] [--saida <diretorio>] [--raiz] [--json]",
400
+ "sema prompt-curto <arquivo-ou-pasta> [--micro|--curto|--medio] [--para <resumo|onboarding|review|mudanca|bug|arquitetura>] [--json]",
401
+ "sema prompt-ia",
402
+ "sema prompt-ia-ui",
403
+ "sema prompt-ia-react",
404
+ "sema prompt-ia-sema-primeiro",
405
+ "sema exemplos-prompt-ia",
406
+ "sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--json]",
407
+ "sema sync-ai-entrypoints [--json]",
408
+ ]),
409
+ "",
410
+ renderizarSecaoAscii("Operacional", [
411
+ "sema doctor",
412
+ "sema --versao | --version | -v",
413
+ ]),
414
+ ].join("\n");
351
415
  }
352
416
  async function carregarModulos(entrada, cwd = process.cwd()) {
353
417
  return (await carregarProjeto(entrada, cwd)).modulosSelecionados;
@@ -430,7 +494,10 @@ async function comandoDoctor() {
430
494
  { nome: "java", ok: comandoDisponivel("java") },
431
495
  { nome: "code", ok: comandoDisponivel("code", ["--version"]) },
432
496
  ];
433
- console.log("Sema doctor");
497
+ console.log(renderizarCaixaAscii([
498
+ "Sema doctor",
499
+ "checa a toolchain minima para validar, gerar e operar a CLI",
500
+ ]));
434
501
  for (const check of checks) {
435
502
  console.log(`- ${check.nome}: ${check.ok ? "ok" : "ausente"}`);
436
503
  }
@@ -471,6 +538,18 @@ function normalizarFonteImportacao(valor) {
471
538
  if (valor === "next") {
472
539
  return "nextjs";
473
540
  }
541
+ if (valor === "next-consumer" || valor === "nextjs-consumer") {
542
+ return "nextjs-consumer";
543
+ }
544
+ if (valor === "react-vite" || valor === "react-vite-consumer" || valor === "react-consumer") {
545
+ return "react-vite-consumer";
546
+ }
547
+ if (valor === "angular" || valor === "angular-consumer") {
548
+ return "angular-consumer";
549
+ }
550
+ if (valor === "flutter" || valor === "flutter-consumer") {
551
+ return "flutter-consumer";
552
+ }
474
553
  if (valor === "fb") {
475
554
  return "firebase";
476
555
  }
@@ -493,6 +572,10 @@ function normalizarFonteImportacao(valor) {
493
572
  || valor === "fastapi"
494
573
  || valor === "flask"
495
574
  || valor === "nextjs"
575
+ || valor === "nextjs-consumer"
576
+ || valor === "react-vite-consumer"
577
+ || valor === "angular-consumer"
578
+ || valor === "flutter-consumer"
496
579
  || valor === "firebase"
497
580
  || valor === "dotnet"
498
581
  || valor === "java"
@@ -510,6 +593,10 @@ function normalizarTemplateIniciar(valor) {
510
593
  if (valor === "nestjs"
511
594
  || valor === "fastapi"
512
595
  || valor === "nextjs-api"
596
+ || valor === "nextjs-consumer"
597
+ || valor === "react-vite-consumer"
598
+ || valor === "angular-consumer"
599
+ || valor === "flutter-consumer"
513
600
  || valor === "node-firebase-worker"
514
601
  || valor === "aspnet-api"
515
602
  || valor === "springboot-api"
@@ -692,6 +779,7 @@ function renderizarCabecalhoDocsIa(descoberta) {
692
779
  const linhas = [
693
780
  "Modo IA-first da instalacao atual",
694
781
  "- Use `sema` como interface publica principal.",
782
+ "- A Sema entra em projeto novo, projeto ja semantizado e adocao incremental em legado sem contrato inicial.",
695
783
  "- Nao assuma monorepo, `node pacotes/cli/dist/index.js`, `npm run project:check` ou uma pasta `exemplos` externa ao projeto atual.",
696
784
  "- Se a IA tiver contexto curto, comece por `sema resumo` e `sema prompt-curto`.",
697
785
  "- Se a IA aguentar mais contexto, suba para `sema drift --json` e `sema contexto-ia`.",
@@ -847,6 +935,11 @@ function coletarResumoSemanticoModulo(contexto) {
847
935
  inferido: limitarLista(unicosOrdenados(briefing.oQueFoiInferido), 6),
848
936
  checksSugeridos: limitarLista(unicosOrdenados(briefing.oQueValidar), 6),
849
937
  testesMinimos: limitarLista(unicosOrdenados(briefing.testesMinimos), 6),
938
+ consumerFramework: briefing.consumerFramework ?? drift.drift.consumerFramework ?? null,
939
+ appRoutes: limitarLista(unicosOrdenados(briefing.appRoutes ?? drift.drift.appRoutes ?? []), 8),
940
+ consumerSurfaces: limitarLista(unicosOrdenados(briefing.consumerSurfaces ?? []), 8),
941
+ consumerBridges: limitarLista(unicosOrdenados(briefing.consumerBridges ?? []), 8),
942
+ arquivosProvaveisEditar: limitarLista(unicosOrdenados(briefing.arquivosProvaveisEditar ?? briefing.oQueTocar), 8),
850
943
  };
851
944
  }
852
945
  function renderizarResumoModuloTexto(resumo, tamanho, modo) {
@@ -856,6 +949,10 @@ function renderizarResumoModuloTexto(resumo, tamanho, modo) {
856
949
  `MODULO: ${resumo.modulo}`,
857
950
  `FAZ: ${resumo.faz}`,
858
951
  `PERFIL: ${resumo.perfilCompatibilidade}`,
952
+ `CONSUMER_FRAMEWORK: ${resumo.consumerFramework ?? "nenhum"}`,
953
+ `APP_ROUTES: ${resumirListaTexto(resumo.appRoutes, limite)}`,
954
+ `CONSUMER_SURFACES: ${resumirListaTexto(resumo.consumerSurfaces, limite)}`,
955
+ `CONSUMER_BRIDGES: ${resumirListaTexto(resumo.consumerBridges, limite)}`,
859
956
  `PUBLICO: ${resumirListaTexto(resumo.superficiesPublicas, limite)}`,
860
957
  `TAREFAS: ${resumirListaTexto(resumo.tarefasPrincipais, limite)}`,
861
958
  `ENTRADAS: ${resumirListaTexto(resumo.entradasChave, limite)}`,
@@ -906,6 +1003,17 @@ function renderizarResumoModuloMarkdown(resumo, modo, guiaPorCapacidade) {
906
1003
  `- Erros: ${resumirListaTexto(resumo.erros, 6)}`,
907
1004
  `- Entidades afetadas: ${resumirListaTexto(resumo.entidadesAfetadas, 6)}`,
908
1005
  "",
1006
+ ...(resumo.consumerFramework
1007
+ ? [
1008
+ "## Consumer IA-first",
1009
+ "",
1010
+ `- Framework consumer: ${resumo.consumerFramework}`,
1011
+ `- Rotas de app: ${resumirListaTexto(resumo.appRoutes, 6)}`,
1012
+ `- Superficies consumer: ${resumirListaTexto(resumo.consumerSurfaces, 6)}`,
1013
+ `- Bridges consumer: ${resumirListaTexto(resumo.consumerBridges, 6)}`,
1014
+ "",
1015
+ ]
1016
+ : []),
909
1017
  "## Intervencao segura",
910
1018
  "",
911
1019
  `- Arquivos provaveis: ${resumirListaTexto(resumo.arquivosProvaveis, 6)}`,
@@ -953,12 +1061,17 @@ function criarBriefingMinimo(resumo, modo, tamanho) {
953
1061
  efeitos: resumo.efeitos,
954
1062
  erros: resumo.erros,
955
1063
  arquivosProvaveis: resumo.arquivosProvaveis,
1064
+ arquivosProvaveisEditar: resumo.arquivosProvaveisEditar,
956
1065
  simbolosRelacionados: resumo.simbolosRelacionados,
957
1066
  riscosPrincipais: resumo.riscosPrincipais,
958
1067
  lacunas: resumo.lacunas,
959
1068
  inferido: resumo.inferido,
960
1069
  checksSugeridos: resumo.checksSugeridos,
961
1070
  testesMinimos: resumo.testesMinimos,
1071
+ consumerFramework: resumo.consumerFramework,
1072
+ appRoutes: resumo.appRoutes,
1073
+ consumerSurfaces: resumo.consumerSurfaces,
1074
+ consumerBridges: resumo.consumerBridges,
962
1075
  };
963
1076
  }
964
1077
  function criarPromptCurtoModulo(resumo, modo, tamanho, capacidade) {
@@ -975,13 +1088,15 @@ Regras:
975
1088
  - preserve a intencao do contrato
976
1089
  - use este resumo como fonte compacta inicial
977
1090
  - se a tarefa pedir mais contexto, suba para \`briefing.min.json\`, \`drift.json\` e depois \`ir.json\`
978
- - nao saia editando backend vivo sem olhar risco, lacuna e checks sugeridos
1091
+ - nao saia editando software vivo sem olhar risco, lacuna e checks sugeridos
1092
+ ${resumo.consumerFramework ? "- se for tarefa visual consumer, priorize `appRoutes`, `consumerSurfaces` e `consumerBridges` antes de abrir arquivos aleatorios" : ""}
979
1093
 
980
1094
  Contexto compacto:
981
1095
  ${resumoTexto}
982
1096
  `;
983
1097
  }
984
1098
  function renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade) {
1099
+ const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
985
1100
  const linhas = [
986
1101
  "# SEMA_BRIEF",
987
1102
  "",
@@ -990,6 +1105,13 @@ function renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade) {
990
1105
  `- Gerado em: \`${geradoEm}\``,
991
1106
  `- Modulos: \`${modulos.length}\``,
992
1107
  "",
1108
+ "## Entrada canonica para IA",
1109
+ "",
1110
+ `- Ordem minima: ${entradaCanonica.ordemLeitura.join(" -> ")}`,
1111
+ `- IA pequena: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
1112
+ `- IA media: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
1113
+ `- IA grande: ${entradaCanonica.porCapacidade.grande.join(" -> ")}`,
1114
+ "",
993
1115
  "## Guia por capacidade",
994
1116
  "",
995
1117
  ];
@@ -1011,6 +1133,19 @@ function renderizarResumoProjetoMarkdown(geradoEm, modulos, guiaPorCapacidade) {
1011
1133
  }
1012
1134
  return `${linhas.join("\n").trim()}\n`;
1013
1135
  }
1136
+ function criarEntradaCanonicaProjeto(guiaPorCapacidade) {
1137
+ return {
1138
+ 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.",
1139
+ ordemLeitura: [...ARQUIVOS_CANONICOS_IA_RAIZ],
1140
+ porCapacidade: {
1141
+ pequena: ["llms.txt", "SEMA_BRIEF.micro.txt", "SEMA_INDEX.json", "AGENTS.md"],
1142
+ media: ["llms.txt", "SEMA_BRIEF.curto.txt", "SEMA_INDEX.json", "AGENTS.md", "README.md"],
1143
+ grande: ["llms-full.txt", "SEMA_BRIEF.md", "SEMA_INDEX.json", "AGENTS.md", "README.md"],
1144
+ },
1145
+ docsSuporte: [...DOCUMENTOS_SUPORTE_IA],
1146
+ guiaPorCapacidade,
1147
+ };
1148
+ }
1014
1149
  function falharContextoIa(mensagem) {
1015
1150
  throw new Error(mensagem);
1016
1151
  }
@@ -1044,6 +1179,41 @@ function resumirDriftPorModulo(modulo, caminho, resultadoDrift) {
1044
1179
  const recursosDivergentes = modulo
1045
1180
  ? resultadoDrift.recursos_divergentes.filter((recurso) => recurso.modulo === modulo)
1046
1181
  : [];
1182
+ const vinculosModulo = modulo
1183
+ ? [
1184
+ ...resultadoDrift.vinculos_validos.filter((vinculo) => vinculo.modulo === modulo),
1185
+ ...resultadoDrift.vinculos_quebrados.filter((vinculo) => vinculo.modulo === modulo),
1186
+ ]
1187
+ : [];
1188
+ const rotasConsumerModulo = new Set(vinculosModulo
1189
+ .filter((vinculo) => vinculo.tipo === "superficie")
1190
+ .map((vinculo) => vinculo.valor));
1191
+ const arquivosRelacionados = [...new Set([
1192
+ ...tasks.flatMap((task) => task.arquivosReferenciados),
1193
+ ...tasks.flatMap((task) => task.arquivosProvaveisEditar),
1194
+ ...implsValidos.map((impl) => impl.arquivo).filter((item) => Boolean(item)),
1195
+ ...implsQuebrados.flatMap((impl) => impl.candidatos?.map((candidato) => candidato.arquivo) ?? []),
1196
+ ...vinculosValidos.map((vinculo) => vinculo.arquivo).filter((item) => Boolean(item)),
1197
+ ...recursosValidos.map((recurso) => recurso.arquivo).filter(Boolean),
1198
+ ...recursosDivergentes.map((recurso) => recurso.arquivo).filter(Boolean),
1199
+ ])].sort((a, b) => a.localeCompare(b, "pt-BR"));
1200
+ const consumerSurfaces = resultadoDrift.consumerSurfaces
1201
+ .filter((surface) => arquivosRelacionados.includes(surface.arquivo)
1202
+ || rotasConsumerModulo.has(surface.rota))
1203
+ .map((surface) => `${surface.tipoArquivo}:${surface.rota} -> ${surface.arquivo}`)
1204
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1205
+ const consumerBridges = resultadoDrift.consumerBridges
1206
+ .filter((bridge) => arquivosRelacionados.includes(bridge.arquivo))
1207
+ .map((bridge) => bridge.caminho)
1208
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1209
+ const appRoutes = [...new Set(resultadoDrift.consumerSurfaces
1210
+ .filter((surface) => arquivosRelacionados.includes(surface.arquivo)
1211
+ || rotasConsumerModulo.has(surface.rota))
1212
+ .map((surface) => surface.rota))]
1213
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1214
+ const consumerFramework = appRoutes.length > 0 || consumerBridges.length > 0
1215
+ ? resultadoDrift.consumerFramework
1216
+ : null;
1047
1217
  return {
1048
1218
  caminho,
1049
1219
  modulo,
@@ -1060,15 +1230,12 @@ function resumirDriftPorModulo(modulo, caminho, resultadoDrift) {
1060
1230
  : tasks.some((task) => task.confiancaVinculo === "media")
1061
1231
  ? "media"
1062
1232
  : "baixa",
1063
- arquivosRelacionados: [...new Set([
1064
- ...tasks.flatMap((task) => task.arquivosReferenciados),
1065
- ...tasks.flatMap((task) => task.arquivosProvaveisEditar),
1066
- ...implsValidos.map((impl) => impl.arquivo).filter((item) => Boolean(item)),
1067
- ...implsQuebrados.flatMap((impl) => impl.candidatos?.map((candidato) => candidato.arquivo) ?? []),
1068
- ...vinculosValidos.map((vinculo) => vinculo.arquivo).filter((item) => Boolean(item)),
1069
- ...recursosValidos.map((recurso) => recurso.arquivo).filter(Boolean),
1070
- ...recursosDivergentes.map((recurso) => recurso.arquivo).filter(Boolean),
1071
- ])].sort((a, b) => a.localeCompare(b, "pt-BR")),
1233
+ arquivosRelacionados,
1234
+ arquivosProvaveisEditar: arquivosRelacionados,
1235
+ consumerFramework,
1236
+ appRoutes,
1237
+ consumerSurfaces,
1238
+ consumerBridges,
1072
1239
  checksSugeridos: [...new Set(tasks.flatMap((task) => task.checksSugeridos))],
1073
1240
  lacunas: [...new Set(tasks.flatMap((task) => task.lacunas))],
1074
1241
  tasks,
@@ -1090,6 +1257,7 @@ function criarBriefingAgente(arquivo, modulo, ir, resumoDrift, resultadoDrift) {
1090
1257
  ...(ir?.resumoAgente.riscos ?? []),
1091
1258
  ])],
1092
1259
  oQueTocar: resumoDrift.arquivosRelacionados,
1260
+ arquivosProvaveisEditar: resumoDrift.arquivosProvaveisEditar,
1093
1261
  oQueValidar: [...new Set([
1094
1262
  ...resumoDrift.checksSugeridos,
1095
1263
  ...resultadoDrift.resumo_operacional.oQueValidar,
@@ -1117,6 +1285,10 @@ function criarBriefingAgente(arquivo, modulo, ir, resumoDrift, resultadoDrift) {
1117
1285
  ...(ir?.routes.map((route) => `${route.metodo ?? "?"} ${route.caminho ?? route.nome}`) ?? []),
1118
1286
  ...(ir?.superficies.map((superficie) => `${superficie.tipo}:${superficie.nome}`) ?? []),
1119
1287
  ],
1288
+ consumerFramework: resumoDrift.consumerFramework,
1289
+ appRoutes: resumoDrift.appRoutes,
1290
+ consumerSurfaces: resumoDrift.consumerSurfaces,
1291
+ consumerBridges: resumoDrift.consumerBridges,
1120
1292
  testesMinimos: [
1121
1293
  "sema validar <arquivo> --json",
1122
1294
  "sema drift <arquivo> --json",
@@ -1216,6 +1388,7 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
1216
1388
  const contextoProjeto = await carregarProjeto(entrada, process.cwd());
1217
1389
  const geradoEm = new Date().toISOString();
1218
1390
  const guiaPorCapacidade = criarGuiaCapacidadeIa();
1391
+ const entradaCanonica = criarEntradaCanonicaProjeto(guiaPorCapacidade);
1219
1392
  const resultadoDrift = await analisarDriftLegado(contextoProjeto);
1220
1393
  const modulos = contextoProjeto.modulosSelecionados.map((item) => {
1221
1394
  const modulo = item.resultado.modulo?.nome ?? path.basename(item.caminho, ".sema");
@@ -1251,12 +1424,14 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
1251
1424
  cliVersao: VERSAO_CLI,
1252
1425
  baseProjeto,
1253
1426
  totalModulos: modulos.length,
1427
+ entradaCanonica,
1254
1428
  guiaPorCapacidade,
1255
1429
  modulos,
1256
1430
  };
1257
1431
  const micro = [
1258
1432
  `PROJETO: ${path.basename(baseProjeto)}`,
1259
1433
  `MODULOS: ${modulos.length}`,
1434
+ `ENTRADA_IA: ${entradaCanonica.porCapacidade.pequena.join(" -> ")}`,
1260
1435
  `TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 3)}`,
1261
1436
  `TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 3)}`,
1262
1437
  `TOP_LACUNAS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.lacunas)), 3)}`,
@@ -1267,6 +1442,7 @@ async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz
1267
1442
  `PROJETO: ${path.basename(baseProjeto)}`,
1268
1443
  `BASE: ${baseProjeto}`,
1269
1444
  `MODULOS: ${modulos.length}`,
1445
+ `ENTRADA_IA: ${entradaCanonica.porCapacidade.media.join(" -> ")}`,
1270
1446
  `TOP_MODULOS: ${resumirListaTexto(modulos.map((modulo) => modulo.modulo), 6)}`,
1271
1447
  `TOP_RISCOS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.riscosPrincipais)), 6)}`,
1272
1448
  `TOP_LACUNAS: ${resumirListaTexto(unicosOrdenados(modulos.flatMap((modulo) => modulo.lacunas)), 6)}`,
@@ -1548,6 +1724,596 @@ async function comandoIniciar(cwd, template) {
1548
1724
  - Contratos em \`contratos/\`
1549
1725
  - Handlers App Router em \`src/app/api/\`
1550
1726
  - Rota de exemplo validada por \`drift\`
1727
+ `,
1728
+ },
1729
+ ];
1730
+ }
1731
+ else if (template === "nextjs-consumer") {
1732
+ arquivos = [
1733
+ {
1734
+ caminhoRelativo: "sema.config.json",
1735
+ conteudo: `{
1736
+ "origens": ["./contratos"],
1737
+ "saida": "./generated",
1738
+ "alvos": ["typescript"],
1739
+ "alvoPadrao": "typescript",
1740
+ "estruturaSaida": "modulos",
1741
+ "framework": "base",
1742
+ "modoEstrito": true,
1743
+ "diretoriosCodigo": ["./src"],
1744
+ "fontesLegado": ["nextjs-consumer", "typescript"],
1745
+ "diretoriosSaidaPorAlvo": {
1746
+ "typescript": "./generated/typescript"
1747
+ },
1748
+ "convencoesGeracaoPorProjeto": "base"
1749
+ }
1750
+ `,
1751
+ },
1752
+ {
1753
+ caminhoRelativo: "contratos/showroom_consumer.sema",
1754
+ conteudo: `module showroom.consumer {
1755
+ task fetch_showroom_ranking {
1756
+ input {
1757
+ }
1758
+ output {
1759
+ ranking: Json
1760
+ }
1761
+ impl {
1762
+ ts: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
1763
+ }
1764
+ vinculos {
1765
+ arquivo: "src/lib/sema_consumer_bridge.ts"
1766
+ simbolo: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
1767
+ superficie: "/ranking"
1768
+ arquivo: "src/app/ranking/page.tsx"
1769
+ arquivo: "src/app/ranking/loading.tsx"
1770
+ arquivo: "src/app/ranking/error.tsx"
1771
+ }
1772
+ guarantees {
1773
+ ranking existe
1774
+ }
1775
+ }
1776
+ }
1777
+ `,
1778
+ },
1779
+ {
1780
+ caminhoRelativo: "src/lib/sema_consumer_bridge.ts",
1781
+ conteudo: `export async function semaFetchShowroomRanking() {
1782
+ return {
1783
+ ranking: [
1784
+ { clube: "Tigres do Norte", pontos: 33 },
1785
+ { clube: "Porto Azul", pontos: 31 },
1786
+ { clube: "Galo de Ouro", pontos: 28 },
1787
+ ],
1788
+ };
1789
+ }
1790
+ `,
1791
+ },
1792
+ {
1793
+ caminhoRelativo: "src/app/ranking/page.tsx",
1794
+ conteudo: `import { semaFetchShowroomRanking } from "../../lib/sema_consumer_bridge";
1795
+
1796
+ export default async function RankingPage() {
1797
+ const { ranking } = await semaFetchShowroomRanking();
1798
+
1799
+ return (
1800
+ <main>
1801
+ <h1>Ranking showroom</h1>
1802
+ <ul>
1803
+ {ranking.map((item) => (
1804
+ <li key={item.clube}>
1805
+ {item.clube} - {item.pontos} pts
1806
+ </li>
1807
+ ))}
1808
+ </ul>
1809
+ </main>
1810
+ );
1811
+ }
1812
+ `,
1813
+ },
1814
+ {
1815
+ caminhoRelativo: "src/app/ranking/loading.tsx",
1816
+ conteudo: `export default function Loading() {
1817
+ return <p>Carregando ranking...</p>;
1818
+ }
1819
+ `,
1820
+ },
1821
+ {
1822
+ caminhoRelativo: "src/app/ranking/error.tsx",
1823
+ conteudo: `"use client";
1824
+
1825
+ export default function Error({
1826
+ error,
1827
+ reset,
1828
+ }: {
1829
+ error: Error;
1830
+ reset: () => void;
1831
+ }) {
1832
+ return (
1833
+ <main>
1834
+ <h1>Falha ao carregar ranking</h1>
1835
+ <p>{error.message}</p>
1836
+ <button type="button" onClick={reset}>Tentar novamente</button>
1837
+ </main>
1838
+ );
1839
+ }
1840
+ `,
1841
+ },
1842
+ {
1843
+ caminhoRelativo: "README.md",
1844
+ conteudo: `# Starter Next.js Consumer + Sema
1845
+
1846
+ - Contratos em \`contratos/\`
1847
+ - Bridge consumer canonico em \`src/lib/sema_consumer_bridge.ts\`
1848
+ - Superficies App Router em \`src/app/\`
1849
+ - O slice oficial desta fase e \`consumer bridge + App Router surfaces\`
1850
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual drift
1851
+ `,
1852
+ },
1853
+ ];
1854
+ }
1855
+ else if (template === "react-vite-consumer") {
1856
+ arquivos = [
1857
+ {
1858
+ caminhoRelativo: "sema.config.json",
1859
+ conteudo: `{
1860
+ "origens": ["./contratos"],
1861
+ "saida": "./generated",
1862
+ "alvos": ["typescript"],
1863
+ "alvoPadrao": "typescript",
1864
+ "estruturaSaida": "modulos",
1865
+ "framework": "base",
1866
+ "modoEstrito": true,
1867
+ "diretoriosCodigo": ["./src"],
1868
+ "fontesLegado": ["react-vite-consumer", "typescript"],
1869
+ "diretoriosSaidaPorAlvo": {
1870
+ "typescript": "./generated/typescript"
1871
+ },
1872
+ "convencoesGeracaoPorProjeto": "base"
1873
+ }
1874
+ `,
1875
+ },
1876
+ {
1877
+ caminhoRelativo: "contratos/showroom_consumer.sema",
1878
+ conteudo: `module showroom.consumer {
1879
+ task fetch_showroom_ranking {
1880
+ input {
1881
+ }
1882
+ output {
1883
+ ranking: Json
1884
+ }
1885
+ impl {
1886
+ ts: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
1887
+ }
1888
+ vinculos {
1889
+ arquivo: "src/lib/sema_consumer_bridge.ts"
1890
+ simbolo: src.lib.sema_consumer_bridge.semaFetchShowroomRanking
1891
+ superficie: "/ranking"
1892
+ arquivo: "src/router.tsx"
1893
+ arquivo: "src/pages/ranking.tsx"
1894
+ }
1895
+ guarantees {
1896
+ ranking existe
1897
+ }
1898
+ }
1899
+ }
1900
+ `,
1901
+ },
1902
+ {
1903
+ caminhoRelativo: "src/lib/sema_consumer_bridge.ts",
1904
+ conteudo: `export async function semaFetchShowroomRanking() {
1905
+ return {
1906
+ ranking: [
1907
+ { clube: "Tigres do Norte", pontos: 33 },
1908
+ { clube: "Porto Azul", pontos: 31 },
1909
+ { clube: "Galo de Ouro", pontos: 28 },
1910
+ ],
1911
+ };
1912
+ }
1913
+ `,
1914
+ },
1915
+ {
1916
+ caminhoRelativo: "src/pages/ranking.tsx",
1917
+ conteudo: `import { useEffect, useState } from "react";
1918
+ import { semaFetchShowroomRanking } from "../lib/sema_consumer_bridge";
1919
+
1920
+ export function RankingPage() {
1921
+ const [ranking, setRanking] = useState<Array<{ clube: string; pontos: number }>>([]);
1922
+
1923
+ useEffect(() => {
1924
+ void semaFetchShowroomRanking().then((payload) => setRanking(payload.ranking ?? []));
1925
+ }, []);
1926
+
1927
+ return (
1928
+ <main>
1929
+ <h1>Ranking showroom</h1>
1930
+ <ul>
1931
+ {ranking.map((item) => (
1932
+ <li key={item.clube}>
1933
+ {item.clube} - {item.pontos} pts
1934
+ </li>
1935
+ ))}
1936
+ </ul>
1937
+ </main>
1938
+ );
1939
+ }
1940
+ `,
1941
+ },
1942
+ {
1943
+ caminhoRelativo: "src/router.tsx",
1944
+ conteudo: `import { createBrowserRouter } from "react-router-dom";
1945
+ import { RankingPage } from "./pages/ranking";
1946
+
1947
+ export const appRouter = createBrowserRouter([
1948
+ {
1949
+ path: "/ranking",
1950
+ Component: RankingPage,
1951
+ },
1952
+ ]);
1953
+ `,
1954
+ },
1955
+ {
1956
+ caminhoRelativo: "src/App.tsx",
1957
+ conteudo: `import { RouterProvider } from "react-router-dom";
1958
+ import { appRouter } from "./router";
1959
+
1960
+ export default function App() {
1961
+ return <RouterProvider router={appRouter} />;
1962
+ }
1963
+ `,
1964
+ },
1965
+ {
1966
+ caminhoRelativo: "src/main.tsx",
1967
+ conteudo: `import React from "react";
1968
+ import ReactDOM from "react-dom/client";
1969
+ import App from "./App";
1970
+
1971
+ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
1972
+ <React.StrictMode>
1973
+ <App />
1974
+ </React.StrictMode>,
1975
+ );
1976
+ `,
1977
+ },
1978
+ {
1979
+ caminhoRelativo: "README.md",
1980
+ conteudo: `# Starter React Vite Consumer + Sema
1981
+
1982
+ - Contratos em \`contratos/\`
1983
+ - Bridge consumer canonico em \`src/lib/sema_consumer_bridge.ts\`
1984
+ - Rotas explicitas em \`src/router.tsx\`
1985
+ - Superficies consumer em \`src/pages/\`
1986
+ - O slice oficial desta fase e \`consumer bridge + react-router surfaces\`
1987
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual drift
1988
+ `,
1989
+ },
1990
+ ];
1991
+ }
1992
+ else if (template === "angular-consumer") {
1993
+ arquivos = [
1994
+ {
1995
+ caminhoRelativo: "sema.config.json",
1996
+ conteudo: `{
1997
+ "origens": ["./contratos"],
1998
+ "saida": "./generated",
1999
+ "alvos": ["typescript"],
2000
+ "alvoPadrao": "typescript",
2001
+ "estruturaSaida": "modulos",
2002
+ "framework": "base",
2003
+ "modoEstrito": true,
2004
+ "diretoriosCodigo": ["./src"],
2005
+ "fontesLegado": ["angular-consumer", "typescript"],
2006
+ "diretoriosSaidaPorAlvo": {
2007
+ "typescript": "./generated/typescript"
2008
+ },
2009
+ "convencoesGeracaoPorProjeto": "base"
2010
+ }
2011
+ `,
2012
+ },
2013
+ {
2014
+ caminhoRelativo: "contratos/showroom_consumer.sema",
2015
+ conteudo: `module showroom.consumer {
2016
+ task fetch_showroom_ranking {
2017
+ input {
2018
+ }
2019
+ output {
2020
+ ranking: Json
2021
+ }
2022
+ impl {
2023
+ ts: src.app.sema_consumer_bridge.semaFetchShowroomRanking
2024
+ }
2025
+ vinculos {
2026
+ arquivo: "src/app/sema_consumer_bridge.ts"
2027
+ simbolo: src.app.sema_consumer_bridge.semaFetchShowroomRanking
2028
+ superficie: "/ranking"
2029
+ arquivo: "src/app/app.routes.ts"
2030
+ arquivo: "src/app/features/ranking/ranking.routes.ts"
2031
+ arquivo: "src/app/features/ranking/ranking-page.component.ts"
2032
+ }
2033
+ guarantees {
2034
+ ranking existe
2035
+ }
2036
+ }
2037
+ }
2038
+ `,
2039
+ },
2040
+ {
2041
+ caminhoRelativo: "src/app/sema_consumer_bridge.ts",
2042
+ conteudo: `export async function semaFetchShowroomRanking() {
2043
+ return {
2044
+ ranking: [
2045
+ { clube: "Tigres do Norte", pontos: 33 },
2046
+ { clube: "Porto Azul", pontos: 31 },
2047
+ { clube: "Galo de Ouro", pontos: 28 },
2048
+ ],
2049
+ };
2050
+ }
2051
+ `,
2052
+ },
2053
+ {
2054
+ caminhoRelativo: "src/app/app.routes.ts",
2055
+ conteudo: `import { Routes } from "@angular/router";
2056
+
2057
+ export const routes: Routes = [
2058
+ {
2059
+ path: "ranking",
2060
+ loadChildren: () => import("./features/ranking/ranking.routes").then((m) => m.RANKING_ROUTES),
2061
+ },
2062
+ ];
2063
+ `,
2064
+ },
2065
+ {
2066
+ caminhoRelativo: "src/app/features/ranking/ranking.routes.ts",
2067
+ conteudo: `import { Routes } from "@angular/router";
2068
+
2069
+ export const RANKING_ROUTES: Routes = [
2070
+ {
2071
+ path: "",
2072
+ loadComponent: () => import("./ranking-page.component").then((m) => m.RankingPageComponent),
2073
+ },
2074
+ ];
2075
+ `,
2076
+ },
2077
+ {
2078
+ caminhoRelativo: "src/app/features/ranking/ranking-page.component.ts",
2079
+ conteudo: `import { Component, OnInit } from "@angular/core";
2080
+ import { CommonModule } from "@angular/common";
2081
+ import { semaFetchShowroomRanking } from "../../sema_consumer_bridge";
2082
+
2083
+ @Component({
2084
+ selector: "app-ranking-page",
2085
+ standalone: true,
2086
+ imports: [CommonModule],
2087
+ template: \`
2088
+ <main>
2089
+ <h1>Ranking showroom</h1>
2090
+ <ul>
2091
+ <li *ngFor="let item of ranking">
2092
+ {{ item.clube }} - {{ item.pontos }} pts
2093
+ </li>
2094
+ </ul>
2095
+ </main>
2096
+ \`,
2097
+ })
2098
+ export class RankingPageComponent implements OnInit {
2099
+ ranking: Array<{ clube: string; pontos: number }> = [];
2100
+
2101
+ async ngOnInit() {
2102
+ const payload = await semaFetchShowroomRanking();
2103
+ this.ranking = payload.ranking ?? [];
2104
+ }
2105
+ }
2106
+ `,
2107
+ },
2108
+ {
2109
+ caminhoRelativo: "src/app/app.component.ts",
2110
+ conteudo: `import { Component } from "@angular/core";
2111
+ import { RouterOutlet } from "@angular/router";
2112
+
2113
+ @Component({
2114
+ selector: "app-root",
2115
+ standalone: true,
2116
+ imports: [RouterOutlet],
2117
+ template: "<router-outlet />",
2118
+ })
2119
+ export class AppComponent {}
2120
+ `,
2121
+ },
2122
+ {
2123
+ caminhoRelativo: "src/main.ts",
2124
+ conteudo: `import { bootstrapApplication } from "@angular/platform-browser";
2125
+ import { provideRouter } from "@angular/router";
2126
+ import { AppComponent } from "./app/app.component";
2127
+ import { routes } from "./app/app.routes";
2128
+
2129
+ void bootstrapApplication(AppComponent, {
2130
+ providers: [provideRouter(routes)],
2131
+ });
2132
+ `,
2133
+ },
2134
+ {
2135
+ caminhoRelativo: "README.md",
2136
+ conteudo: `# Starter Angular Consumer + Sema
2137
+
2138
+ - Contratos em \`contratos/\`
2139
+ - Bridge consumer canonico em \`src/app/sema_consumer_bridge.ts\`
2140
+ - Rotas lazy em \`src/app/app.routes.ts\`
2141
+ - Feature folders em \`src/app/features/\`
2142
+ - O slice oficial desta fase e \`consumer bridge + route config surfaces\`
2143
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual drift
2144
+ `,
2145
+ },
2146
+ ];
2147
+ }
2148
+ else if (template === "flutter-consumer") {
2149
+ arquivos = [
2150
+ {
2151
+ caminhoRelativo: "sema.config.json",
2152
+ conteudo: `{
2153
+ "origens": ["./contratos"],
2154
+ "saida": "./generated",
2155
+ "alvos": ["dart"],
2156
+ "alvoPadrao": "dart",
2157
+ "estruturaSaida": "modulos",
2158
+ "framework": "base",
2159
+ "modoEstrito": true,
2160
+ "diretoriosCodigo": ["./lib"],
2161
+ "fontesLegado": ["flutter-consumer", "dart"],
2162
+ "diretoriosSaidaPorAlvo": {
2163
+ "dart": "./generated/dart"
2164
+ },
2165
+ "convencoesGeracaoPorProjeto": "base"
2166
+ }
2167
+ `,
2168
+ },
2169
+ {
2170
+ caminhoRelativo: "pubspec.yaml",
2171
+ conteudo: `name: sema_flutter_consumer
2172
+ description: Starter Flutter consumer IA-first com Sema
2173
+ publish_to: "none"
2174
+
2175
+ environment:
2176
+ sdk: ">=3.3.0 <4.0.0"
2177
+
2178
+ dependencies:
2179
+ flutter:
2180
+ sdk: flutter
2181
+ go_router: ^14.0.0
2182
+ `,
2183
+ },
2184
+ {
2185
+ caminhoRelativo: "contratos/showroom_consumer.sema",
2186
+ conteudo: `module showroom.consumer {
2187
+ task fetch_showroom_ranking {
2188
+ input {
2189
+ }
2190
+ output {
2191
+ resultado: Json
2192
+ }
2193
+ impl {
2194
+ dart: lib.sema_consumer_bridge.semaFetchShowroomRanking
2195
+ }
2196
+ vinculos {
2197
+ arquivo: "lib/sema_consumer_bridge.dart"
2198
+ simbolo: lib.sema_consumer_bridge.semaFetchShowroomRanking
2199
+ superficie: "/ranking"
2200
+ arquivo: "lib/router.dart"
2201
+ arquivo: "lib/screens/ranking_screen.dart"
2202
+ }
2203
+ guarantees {
2204
+ resultado existe
2205
+ }
2206
+ }
2207
+ }
2208
+ `,
2209
+ },
2210
+ {
2211
+ caminhoRelativo: "lib/sema_consumer_bridge.dart",
2212
+ conteudo: `Future<Map<String, dynamic>> semaFetchShowroomRanking() async {
2213
+ return {
2214
+ "ranking": [
2215
+ {"clube": "Tigres do Norte", "pontos": 33},
2216
+ {"clube": "Porto Azul", "pontos": 31},
2217
+ {"clube": "Galo de Ouro", "pontos": 28},
2218
+ ],
2219
+ };
2220
+ }
2221
+ `,
2222
+ },
2223
+ {
2224
+ caminhoRelativo: "lib/router.dart",
2225
+ conteudo: `import "package:go_router/go_router.dart";
2226
+ import "package:flutter/widgets.dart";
2227
+ import "screens/ranking_screen.dart";
2228
+
2229
+ final appRouter = GoRouter(
2230
+ routes: [
2231
+ GoRoute(
2232
+ path: "/ranking",
2233
+ builder: (BuildContext context, GoRouterState state) => const RankingScreen(),
2234
+ ),
2235
+ ],
2236
+ );
2237
+ `,
2238
+ },
2239
+ {
2240
+ caminhoRelativo: "lib/screens/ranking_screen.dart",
2241
+ conteudo: `import "package:flutter/widgets.dart";
2242
+ import "../sema_consumer_bridge.dart";
2243
+
2244
+ class RankingScreen extends StatefulWidget {
2245
+ const RankingScreen({super.key});
2246
+
2247
+ @override
2248
+ State<RankingScreen> createState() => _RankingScreenState();
2249
+ }
2250
+
2251
+ class _RankingScreenState extends State<RankingScreen> {
2252
+ List<Map<String, dynamic>> ranking = const [];
2253
+
2254
+ @override
2255
+ void initState() {
2256
+ super.initState();
2257
+ semaFetchShowroomRanking().then((payload) {
2258
+ final itens = (payload["ranking"] as List<dynamic>? ?? const [])
2259
+ .whereType<Map<String, dynamic>>()
2260
+ .toList();
2261
+ if (!mounted) return;
2262
+ setState(() {
2263
+ ranking = itens;
2264
+ });
2265
+ });
2266
+ }
2267
+
2268
+ @override
2269
+ Widget build(BuildContext context) {
2270
+ return ListView(
2271
+ children: [
2272
+ const Padding(
2273
+ padding: EdgeInsets.all(16),
2274
+ child: Text("Ranking showroom"),
2275
+ ),
2276
+ ...ranking.map((item) => Padding(
2277
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
2278
+ child: Text("\${item["clube"]} - \${item["pontos"]} pts"),
2279
+ )),
2280
+ ],
2281
+ );
2282
+ }
2283
+ }
2284
+ `,
2285
+ },
2286
+ {
2287
+ caminhoRelativo: "lib/main.dart",
2288
+ conteudo: `import "package:flutter/material.dart";
2289
+ import "router.dart";
2290
+
2291
+ void main() {
2292
+ runApp(const ShowroomApp());
2293
+ }
2294
+
2295
+ class ShowroomApp extends StatelessWidget {
2296
+ const ShowroomApp({super.key});
2297
+
2298
+ @override
2299
+ Widget build(BuildContext context) {
2300
+ return MaterialApp.router(
2301
+ routerConfig: appRouter,
2302
+ );
2303
+ }
2304
+ }
2305
+ `,
2306
+ },
2307
+ {
2308
+ caminhoRelativo: "README.md",
2309
+ conteudo: `# Starter Flutter Consumer + Sema
2310
+
2311
+ - Contratos em \`contratos/\`
2312
+ - Bridge consumer canonico em \`lib/sema_consumer_bridge.dart\`
2313
+ - Rotas consumer em \`lib/router.dart\`
2314
+ - Superficies consumer em \`lib/screens/\`
2315
+ - O slice oficial desta fase e \`consumer bridge + router/screen surfaces\`
2316
+ - \`drift\` valida \`impl\`, \`vinculos\`, bridge e superficies, sem prometer visual diff
1551
2317
  `,
1552
2318
  },
1553
2319
  ];
@@ -2106,6 +2872,10 @@ async function comandoInspecionar(entrada, emJson, cwd = process.cwd()) {
2106
2872
  modoAdocao: contextoProjeto.modoAdocao,
2107
2873
  scoreDrift: resultadoDrift.resumo_operacional.scoreMedio,
2108
2874
  confiancaGeral: resultadoDrift.resumo_operacional.confiancaGeral,
2875
+ consumerFramework: resultadoDrift.consumerFramework,
2876
+ appRoutes: resultadoDrift.appRoutes,
2877
+ consumerSurfaces: resultadoDrift.consumerSurfaces,
2878
+ consumerBridges: resultadoDrift.consumerBridges,
2109
2879
  },
2110
2880
  projeto: {
2111
2881
  arquivos: contextoProjeto.arquivosProjeto,
@@ -2440,49 +3210,80 @@ async function comandoStarterIa() {
2440
3210
  console.log(STARTER_IA);
2441
3211
  return 0;
2442
3212
  }
3213
+ async function comandoSyncAiEntrypoints(emJson) {
3214
+ const resumoProjeto = await gerarResumoProjetoIa(process.cwd(), undefined, true);
3215
+ const indexJson = JSON.parse(await readFile(path.join(resumoProjeto.pastaSaida, "SEMA_INDEX.json"), "utf8"));
3216
+ const artefatos = [...new Set([
3217
+ ...ARQUIVOS_CANONICOS_IA_RAIZ,
3218
+ ...resumoProjeto.artefatos,
3219
+ ])];
3220
+ if (emJson) {
3221
+ console.log(JSON.stringify({
3222
+ comando: "sync-ai-entrypoints",
3223
+ sucesso: true,
3224
+ baseProjeto: resumoProjeto.baseProjeto,
3225
+ pastaSaida: resumoProjeto.pastaSaida,
3226
+ artefatos,
3227
+ entradaCanonica: indexJson.entradaCanonica,
3228
+ }, null, 2));
3229
+ return 0;
3230
+ }
3231
+ console.log("Entrypoints IA-first sincronizados");
3232
+ console.log("");
3233
+ console.log(`Base do projeto: ${resumoProjeto.baseProjeto}`);
3234
+ console.log(`Ordem canonica: ${indexJson.entradaCanonica.ordemLeitura.join(" -> ")}`);
3235
+ console.log(`IA pequena: ${indexJson.entradaCanonica.porCapacidade.pequena.join(" -> ")}`);
3236
+ console.log(`IA media: ${indexJson.entradaCanonica.porCapacidade.media.join(" -> ")}`);
3237
+ console.log(`IA grande: ${indexJson.entradaCanonica.porCapacidade.grande.join(" -> ")}`);
3238
+ return 0;
3239
+ }
2443
3240
  async function comandoAjudaIa() {
2444
3241
  const descoberta = await descobrirDocsIa();
2445
3242
  console.log("Ajuda de IA da Sema");
2446
3243
  console.log("");
3244
+ console.log(renderizarCaixaAscii([
3245
+ "IA-first para greenfield, edicao guiada e legado sem contrato inicial",
3246
+ "use o menor artefato semantico que resolva a tarefa",
3247
+ ]));
3248
+ console.log("");
2447
3249
  console.log(renderizarCabecalhoDocsIa(descoberta));
2448
3250
  console.log("");
2449
- console.log("O que a Sema faz de verdade");
2450
- console.log("- Foi feita para IA operar melhor; leitura humana e consequencia, nao centro de produto.");
2451
- console.log("- Governa contrato, intencao, erro, efeito, garantia, fluxo, vinculos e execucao.");
2452
- console.log("- Usa `importar` para bootstrap revisavel de legado.");
2453
- console.log("- Usa `impl` para ligar contrato a simbolos reais.");
2454
- console.log("- Usa `vinculos` para ligar contrato a arquivo, simbolo, recurso e superficie real.");
2455
- console.log("- Usa `execucao` para explicitar timeout, retry, compensacao e criticidade.");
2456
- console.log("- Usa `drift` para medir divergencia entre contrato e codigo vivo com score, confianca e lacunas.");
2457
- console.log("- Usa `resumo` e `prompt-curto` para IA pequena ou gratuita.");
2458
- console.log("- Usa `contexto-ia` para preparar AST, IR, diagnosticos, drift, `briefing.json` e artefatos compactos antes da edicao.");
3251
+ console.log(renderizarSecaoAscii("Tres jeitos de usar a Sema", [
3252
+ "[1] Producao inicial: modele, valide, compile e verifique antes de subir codigo derivado.",
3253
+ "[2] Edicao em projeto com Sema: inspecione, leia resumo, rode drift e gere contexto antes de editar codigo vivo.",
3254
+ "[3] Projeto sem Sema ainda: importe, revise o rascunho, formate, valide e use drift como juiz da adocao incremental.",
3255
+ ]));
2459
3256
  console.log("");
2460
- console.log("O que a Sema nao promete");
2461
- console.log("- Nao escreve contrato final sozinho.");
2462
- console.log("- Nao substitui decisao arquitetural.");
2463
- console.log("- Nao adivinha regra de negocio que o codigo nao explicita.");
3257
+ console.log(renderizarSecaoAscii("Capacidade de IA", [
3258
+ "pequena: `sema resumo --micro`, `briefing.min.json`, `prompt-curto.txt`",
3259
+ "media: `sema resumo --curto`, `drift.json`, `briefing.min.json`",
3260
+ "grande: `sema contexto-ia`, `briefing.json`, `ir.json`, `ast.json`",
3261
+ ]));
2464
3262
  console.log("");
2465
- console.log("Fluxo recomendado");
2466
- console.log("- Use `sema starter-ia` para um texto curto de onboarding.");
2467
- console.log("- Use `sema resumo <arquivo> --micro --para onboarding` para IA pequena.");
2468
- console.log("- Use `sema prompt-curto <arquivo> --curto --para mudanca` para colar contexto em modelo gratuito.");
2469
- console.log("- Use `sema prompt-ia` para o prompt-base geral.");
2470
- console.log("- Use `sema prompt-ia-ui` para tarefas visuais com Sema + UI.");
2471
- console.log("- Use `sema prompt-ia-react` para projeto com Sema + React + TypeScript.");
2472
- console.log("- Use `sema prompt-ia-sema-primeiro` para forcar modelagem semantica antes da implementacao.");
2473
- console.log("- Use `sema exemplos-prompt-ia` para pegar modelos prontos de prompt.");
2474
- console.log("- Use `sema inspecionar` para descobrir base, codigo vivo e fontes legado.");
2475
- console.log("- Use `sema drift` para medir impls, vinculos, rotas, score e lacunas.");
2476
- console.log("- Use `sema contexto-ia <arquivo.sema>` para gerar AST, IR, drift, `briefing.json` e `briefing.min.json` do modulo alvo.");
2477
- console.log("- Use `sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart> --saida <diretorio>` quando a tarefa pedir codigo derivado.");
3263
+ console.log(renderizarSecaoAscii("Fluxo recomendado", [
3264
+ "Use `sema starter-ia` para um texto curto de onboarding.",
3265
+ "Use `sema sync-ai-entrypoints` para regenerar `SEMA_BRIEF.*` e `SEMA_INDEX.json` na raiz.",
3266
+ "Use `sema resumo <arquivo> --micro --para onboarding` para IA pequena.",
3267
+ "Use `sema prompt-curto <arquivo> --curto --para mudanca` para colar contexto em modelo gratuito.",
3268
+ "Use `sema prompt-ia`, `sema prompt-ia-ui`, `sema prompt-ia-react` e `sema prompt-ia-sema-primeiro` conforme a tarefa.",
3269
+ "Use `sema exemplos-prompt-ia` para pegar modelos prontos de prompt.",
3270
+ "Use `sema inspecionar` para descobrir base, codigo vivo e fontes legado.",
3271
+ "Use `sema drift` para medir impls, vinculos, rotas, score e lacunas.",
3272
+ "Use `sema contexto-ia <arquivo.sema>` para gerar AST, IR, drift, `briefing.json` e `briefing.min.json`.",
3273
+ "Use `sema compilar <arquivo-ou-pasta> --alvo <typescript|python|dart> --saida <diretorio>` quando a tarefa pedir codigo derivado.",
3274
+ ]));
2478
3275
  console.log("");
2479
- console.log("Regra pratica");
2480
- console.log("- Se voce quer testar a Sema de verdade, nao peca so HTML solto.");
2481
- console.log("- Peca `.sema` + arquitetura + React + TypeScript, ou use o modo `Sema primeiro`.");
2482
- console.log("- Se o projeto ja existe, trate `importar` como rascunho e `drift` como juiz.");
2483
- console.log("- IA pequena comeca no menor artefato que resolve a tarefa; nao enfie `ast.json` inteiro nela de bobeira.");
2484
- console.log("- Antes de editar backend vivo, leia `briefing.min.json` ou `briefing.json` em vez de sair cavando arquivo na fe.");
2485
- console.log("- Trate `route`, `worker`, `evento`, `fila`, `cron`, `webhook`, `cache`, `storage` e `policy` como superficies de primeira classe.");
3276
+ console.log(renderizarSecaoAscii("Regras praticas", [
3277
+ "Foi feita para IA operar melhor; leitura humana e consequencia, nao centro de produto.",
3278
+ "Governa contrato, intencao, erro, efeito, garantia, fluxo, vinculos e execucao.",
3279
+ "Nao escreve contrato final sozinho nem substitui decisao arquitetural.",
3280
+ "Se voce quer testar a Sema de verdade, nao peca so HTML solto.",
3281
+ "Peca `.sema` + arquitetura + React + TypeScript, ou use o modo `Sema primeiro`.",
3282
+ "Se o projeto ja existe, trate `importar` como rascunho e `drift` como juiz.",
3283
+ "IA pequena comeca no menor artefato que resolve a tarefa; nao enfie `ast.json` inteiro nela de bobeira.",
3284
+ "Antes de editar software vivo, leia `briefing.min.json` ou `briefing.json` em vez de sair cavando arquivo na fe.",
3285
+ "Trate `route`, `worker`, `evento`, `fila`, `cron`, `webhook`, `cache`, `storage` e `policy` como superficies de primeira classe.",
3286
+ ]));
2486
3287
  return 0;
2487
3288
  }
2488
3289
  async function comandoPromptIa() {
@@ -2897,7 +3698,7 @@ async function principal() {
2897
3698
  {
2898
3699
  const fonte = normalizarFonteImportacao(posicionais[0]);
2899
3700
  if (!fonte || !posicionais[1]) {
2900
- console.error("Uso: sema importar <nestjs|fastapi|flask|nextjs|firebase|dotnet|java|go|rust|cpp|typescript|python|dart> <diretorio> [--saida <diretorio>] [--namespace <base>] [--json]");
3701
+ 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]");
2901
3702
  codigoSaida = 1;
2902
3703
  break;
2903
3704
  }
@@ -2916,6 +3717,9 @@ async function principal() {
2916
3717
  case "starter-ia":
2917
3718
  codigoSaida = await comandoStarterIa();
2918
3719
  break;
3720
+ case "sync-ai-entrypoints":
3721
+ codigoSaida = await comandoSyncAiEntrypoints(possuiFlag(resto, "--json"));
3722
+ break;
2919
3723
  case "resumo":
2920
3724
  codigoSaida = await comandoResumo(posicionais[0], resto, possuiFlag(resto, "--json"));
2921
3725
  break;