@semacode/cli 3.0.0 → 3.5.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.
Files changed (46) hide show
  1. package/dist/agentEntryPoints.js +78 -64
  2. package/dist/cliGrammar.js +4 -4
  3. package/dist/cliHelpTexto.js +6 -6
  4. package/dist/docs.part01.d.ts +4 -1
  5. package/dist/docs.part01.js +8 -0
  6. package/dist/docs.part02.js +9 -1
  7. package/dist/drift.part01.d.ts +7 -1
  8. package/dist/drift.part04.d.ts +14 -2
  9. package/dist/drift.part04.js +35 -2
  10. package/dist/drift.part09.d.ts +2 -2
  11. package/dist/drift.part09.js +10 -3
  12. package/dist/drift.part11.js +24 -3
  13. package/dist/drift.part14.js +2 -0
  14. package/dist/exemplosOficiais.d.ts +2 -1
  15. package/dist/exemplosOficiais.js +9 -3
  16. package/dist/geracaoCore.d.ts +1 -0
  17. package/dist/geracaoCore.js +2 -0
  18. package/dist/importador.part01.d.ts +1 -1
  19. package/dist/importador.part06.d.ts +2 -0
  20. package/dist/importador.part06.js +87 -1
  21. package/dist/importador.part09.js +7 -1
  22. package/dist/index.part04.js +13 -6
  23. package/dist/index.part07.d.ts +8 -2
  24. package/dist/index.part07.js +146 -4
  25. package/dist/index.part08.js +3 -3
  26. package/dist/initCommand.d.ts +1 -0
  27. package/dist/initCommand.js +15 -3
  28. package/dist/initTemplatesBase.js +78 -78
  29. package/dist/projetoConfig.js +2 -0
  30. package/dist/projetoLegado.js +18 -0
  31. package/dist/tipos.d.ts +1 -1
  32. package/dist/typescript-http-modelos.d.ts +1 -1
  33. package/dist/typescript-http-rotas.d.ts +1 -0
  34. package/dist/typescript-http-rotas.js +93 -0
  35. package/dist/typescript-http.d.ts +1 -1
  36. package/dist/typescript-http.js +1 -1
  37. package/dist/verificacaoCache.d.ts +25 -0
  38. package/dist/verificacaoCache.js +80 -0
  39. package/docs/syntax.md +14 -0
  40. package/exemplos/express_crud.sema +139 -0
  41. package/exemplos/fastify_crud.sema +146 -0
  42. package/node_modules/@sema/nucleo/dist/ir/conversor.part01.js +37 -38
  43. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +1 -0
  44. package/node_modules/@sema/nucleo/dist/semantico/analisador.part01.d.ts +7 -0
  45. package/node_modules/@sema/nucleo/dist/semantico/analisador.part01.js +33 -10
  46. package/package.json +1 -1
@@ -80,13 +80,14 @@ export function normalizarCaminhoRota(caminho) {
80
80
  if (!caminho) {
81
81
  return "/";
82
82
  }
83
- const limpo = normalizarCaminhoFlask(caminho.trim().replace(/\s*\/\s*/g, "/"));
83
+ const limpo = normalizarCaminhoFlask(caminho.trim().replace(/\s*\/\s*/g, "/"))
84
+ .replace(/:([A-Za-z_]\w*)/g, "{$1}");
84
85
  const comBarra = limpo.startsWith("/") ? limpo : `/${limpo}`;
85
86
  const normalizado = comBarra.replace(/\/+/g, "/");
86
87
  return normalizado.endsWith("/") && normalizado !== "/" ? normalizado.slice(0, -1) : normalizado;
87
88
  }
88
89
  export function extrairFontesHttpTypeScript(fontesLegado) {
89
- return fontesLegado.filter((fonte) => fonte === "nestjs" || fonte === "nextjs" || fonte === "firebase");
90
+ return fontesLegado.filter((fonte) => fonte === "nestjs" || fonte === "nextjs" || fonte === "firebase" || fonte === "express" || fonte === "fastify");
90
91
  }
91
92
  export function extrairFontesHttpBackend(fontesLegado) {
92
93
  return fontesLegado.filter((fonte) => fonte === "dotnet" || fonte === "java" || fonte === "go" || fonte === "rust" || fonte === "php");
@@ -227,13 +228,19 @@ export function escolherRotasEsperadas(task, fontesLegado) {
227
228
  if (fontesTs.includes("firebase") && /(apps\.worker|worker|sema_contract_bridge|health)/i.test(implTsOuJs.caminho)) {
228
229
  esperadas.add("firebase");
229
230
  }
231
+ if (/\bexpress\b|\broutes?\b|\brouter\b/i.test(implTsOuJs.caminho) && fontesTs.includes("express")) {
232
+ esperadas.add("express");
233
+ }
234
+ if (/\bfastify\b/i.test(implTsOuJs.caminho) && fontesTs.includes("fastify")) {
235
+ esperadas.add("fastify");
236
+ }
230
237
  if (esperadas.size > 0) {
231
238
  return [...esperadas];
232
239
  }
233
240
  if (fontesTs.length > 0) {
234
241
  return fontesTs;
235
242
  }
236
- return ["nestjs", "nextjs", "firebase"];
243
+ return ["nestjs", "nextjs", "firebase", "express", "fastify"];
237
244
  }
238
245
  if (task.implementacoesExternas.some((impl) => impl.origem === "py")) {
239
246
  const fontesPython = fontesLegado.filter((fonte) => fonte === "fastapi" || fonte === "flask");
@@ -1,11 +1,12 @@
1
1
  // SEMA-GOVERNED: sema.governanca_ia_contexto, sema.produto.governanca_ia.drift.cache.store
2
2
  // Descrição: integra índices calculados ou reutilizados e sempre recalcula o resultado final do drift.
3
3
  import path from "node:path";
4
+ import { resolverAlvoPadrao, resolverEstruturaSaidaPadrao, resolverFrameworkPadrao } from "./projeto.js";
4
5
  import { emitirDiagnosticosArquivosOrcamento, workspaceExigeCabecalhoCodigoGovernado, } from "./driftOrcamento.js";
5
6
  import { definirDiretoriosIgnoradosAtivos, obterDiretoriosIgnoradosAtivos, extrairTermosEscopoDrift, resolverDiretoriosIgnoradosAtivos, resolverOpcoesDrift } from "./drift.part01.js";
6
7
  import { construirContextoRelevanciaConsumer, filtrarConsumerSurfacesPorEscopo } from "./drift.part02.js";
7
8
  import { inferirConsumerFrameworkPrincipal } from "./drift.part06.js";
8
- import { calcularConfiancaTask, calcularRiscoOperacional, calcularScoreTask, chaveCaminhoCanonicoDrift, encontrarCandidatosFisicosImplementacaoDrift, resumirLacunasTask, resumirOperacional } from "./drift.part04.js";
9
+ import { calcularConfiancaTask, calcularRiscoOperacional, calcularScoreTask, carregarEvidenciaVerificacaoDrift, chaveCaminhoCanonicoDrift, encontrarCandidatosFisicosImplementacaoDrift, resumirLacunasTask, resumirOperacional } from "./drift.part04.js";
9
10
  import { analisarPersistenciaReal, ordenarCandidatos } from "./drift.part09.js";
10
11
  import { simboloEhBridgeConsumer } from "./drift.part05.js";
11
12
  import { prepararIndicesDrift } from "./drift.part13.js";
@@ -275,6 +276,24 @@ export async function analisarDriftLegado(contexto, opcoes) {
275
276
  bloqueiosAnalise.add("escopo_estreito_ambiguo");
276
277
  }
277
278
  configuracaoEscopo.bloqueios = [...bloqueiosAnalise];
279
+ const evidenciaPorModulo = new Map();
280
+ for (const item of contexto.modulosSelecionados) {
281
+ const nomeModulo = item.resultado.ir?.nome;
282
+ if (!nomeModulo) {
283
+ continue;
284
+ }
285
+ const frameworkVerificacao = resolverFrameworkPadrao(undefined, contexto.configCarregada);
286
+ const evidencia = await carregarEvidenciaVerificacaoDrift({
287
+ caminhoContrato: item.caminho,
288
+ nomeModulo,
289
+ alvo: resolverAlvoPadrao(undefined, contexto.configCarregada),
290
+ framework: frameworkVerificacao,
291
+ estrutura: resolverEstruturaSaidaPadrao(undefined, frameworkVerificacao, contexto.configCarregada),
292
+ });
293
+ if (evidencia) {
294
+ evidenciaPorModulo.set(nomeModulo, evidencia);
295
+ }
296
+ }
278
297
  for (const resumo of tasksResumo) {
279
298
  const chaveTask = `${resumo.modulo}:${resumo.task}`;
280
299
  const task = taskPorChave.get(chaveTask);
@@ -304,8 +323,10 @@ export async function analisarDriftLegado(contexto, opcoes) {
304
323
  }
305
324
  resumo.confiancaVinculo = calcularConfiancaTask(task, resumo.implsValidos, resumo.implsQuebrados, resumoVinculos.validos, resumoVinculos.quebrados);
306
325
  resumo.riscoOperacional = calcularRiscoOperacional(task);
307
- resumo.lacunas = resumirLacunasTask(task, resumo.semImplementacao, resumo.implsQuebrados, resumoVinculos.quebrados, guardrails);
308
- resumo.scoreSemantico = calcularScoreTask(task, resumo.implsValidos, resumo.implsQuebrados, resumoVinculos.validos, resumoVinculos.quebrados, resumo.semImplementacao);
326
+ const evidenciaVerificada = evidenciaPorModulo.get(resumo.modulo);
327
+ resumo.lacunas = resumirLacunasTask(task, resumo.semImplementacao, resumo.implsQuebrados, resumoVinculos.quebrados, guardrails, evidenciaVerificada);
328
+ resumo.scoreSemantico = calcularScoreTask(task, resumo.implsValidos, resumo.implsQuebrados, resumoVinculos.validos, resumoVinculos.quebrados, resumo.semImplementacao, evidenciaVerificada);
329
+ resumo.evidenciaVerificacao = evidenciaVerificada;
309
330
  resumo.ancoragemVinculo = task.vinculos.length > 0
310
331
  ? "propria"
311
332
  : arquivosAncoraHerdados.length > 0
@@ -286,6 +286,7 @@ export function analisarModulosSelecionadosDrift(estado) {
286
286
  route: route.nome,
287
287
  metodo: route.metodo,
288
288
  caminho: route.caminho,
289
+ frameworksEsperados: [...esperadas],
289
290
  motivo: `Nenhuma rota publica ${route.metodo} ${route.caminho} foi encontrada no codigo legado para o framework esperado.`,
290
291
  };
291
292
  rotasDivergentes.push(registro);
@@ -293,6 +294,7 @@ export function analisarModulosSelecionadosDrift(estado) {
293
294
  tipo: "rota_divergente",
294
295
  modulo: ir.nome,
295
296
  route: route.nome,
297
+ frameworksEsperados: [...esperadas],
296
298
  mensagem: registro.motivo,
297
299
  });
298
300
  }
@@ -17,4 +17,5 @@ export interface PlanoExemplosOficiais {
17
17
  }
18
18
  export declare function localizarDiretorioExemplosOficiais(): Promise<string | undefined>;
19
19
  export declare function planejarExemplosOficiais(): Promise<PlanoExemplosOficiais>;
20
- export declare function materializarExemplosOficiais(baseProjeto?: string, preservarExistentes?: boolean): Promise<ResultadoMaterializacaoExemplos>;
20
+ export declare function normalizarCaminhoExemplo(caminho: string): string;
21
+ export declare function materializarExemplosOficiais(baseProjeto?: string, preservarExistentes?: boolean, somenteCaminhos?: ReadonlySet<string>): Promise<ResultadoMaterializacaoExemplos>;
@@ -56,7 +56,10 @@ export async function planejarExemplosOficiais() {
56
56
  arquivos,
57
57
  };
58
58
  }
59
- export async function materializarExemplosOficiais(baseProjeto = process.cwd(), preservarExistentes = true) {
59
+ export function normalizarCaminhoExemplo(caminho) {
60
+ return caminho.replace(/\\/g, "/");
61
+ }
62
+ export async function materializarExemplosOficiais(baseProjeto = process.cwd(), preservarExistentes = true, somenteCaminhos) {
60
63
  const plano = await planejarExemplosOficiais();
61
64
  const origem = plano.origem;
62
65
  const destino = path.resolve(baseProjeto, "exemplos");
@@ -71,10 +74,13 @@ export async function materializarExemplosOficiais(baseProjeto = process.cwd(),
71
74
  erro: "Diretorio de exemplos oficiais nao foi encontrado no pacote da CLI.",
72
75
  };
73
76
  }
77
+ const arquivosSelecionados = somenteCaminhos
78
+ ? plano.arquivos.filter((arquivo) => somenteCaminhos.has(normalizarCaminhoExemplo(arquivo.caminhoRelativo)))
79
+ : plano.arquivos;
74
80
  const criados = [];
75
81
  const preservados = [];
76
- await validarDestinosEscritaWorkspace(baseProjeto, plano.arquivos.map((arquivo) => arquivo.caminhoRelativo));
77
- for (const arquivo of plano.arquivos) {
82
+ await validarDestinosEscritaWorkspace(baseProjeto, arquivosSelecionados.map((arquivo) => arquivo.caminhoRelativo));
83
+ for (const arquivo of arquivosSelecionados) {
78
84
  const resultado = await escreverArquivoWorkspaceSeguro(baseProjeto, arquivo.caminhoRelativo, await readFile(arquivo.origemArquivo, "utf8"), { sobrescrever: !preservarExistentes });
79
85
  if (resultado.status === "preservado") {
80
86
  preservados.push(arquivo.nome);
@@ -17,6 +17,7 @@ export interface ResumoAlvoVerificacao {
17
17
  framework: FrameworkGeracao;
18
18
  estrutura: EstruturaSaida;
19
19
  testesExecutados: boolean;
20
+ origem?: "executado" | "cache";
20
21
  }
21
22
  export interface ResumoModuloVerificacao {
22
23
  modulo: string;
@@ -85,6 +85,8 @@ export function normalizarFonteImportacao(valor) {
85
85
  return "php";
86
86
  }
87
87
  if (valor === "nestjs"
88
+ || valor === "express"
89
+ || valor === "fastify"
88
90
  || valor === "fastapi"
89
91
  || valor === "flask"
90
92
  || valor === "nextjs"
@@ -1,6 +1,6 @@
1
1
  import ts from "typescript";
2
2
  import { type Diagnostico } from "@sema/nucleo";
3
- export type FonteImportacao = "nestjs" | "fastapi" | "flask" | "nextjs" | "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer" | "firebase" | "typescript" | "python" | "dart" | "dotnet" | "java" | "go" | "rust" | "cpp" | "php";
3
+ export type FonteImportacao = "nestjs" | "express" | "fastify" | "fastapi" | "flask" | "nextjs" | "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer" | "firebase" | "typescript" | "python" | "dart" | "dotnet" | "java" | "go" | "rust" | "cpp" | "php";
4
4
  export type OrigemInteropImportada = "ts" | "py" | "dart" | "cs" | "java" | "go" | "rust" | "cpp" | "php";
5
5
  export interface CampoImportado {
6
6
  nome: string;
@@ -1,6 +1,8 @@
1
1
  import { extrairRotasTypeScriptHttp } from "./typescript-http.js";
2
2
  import { CampoImportado, ModuloImportado, TipoDescoberto } from "./importador.part01.js";
3
3
  export declare function importarNestJsDeArquivo(diretorioBase: string, arquivo: string, namespaceBase: string, tiposGlobais: Map<string, TipoDescoberto>): ModuloImportado[];
4
+ export declare function importarExpressFastifyDeArquivo(diretorioBase: string, arquivo: string, namespaceBase: string, origemDesejada: "express" | "fastify"): ModuloImportado[];
5
+ export declare function importarExpressFastifyBase(diretorio: string, namespaceBase: string, origem: "express" | "fastify"): Promise<ModuloImportado[]>;
4
6
  export declare function importarTypeScriptBase(diretorio: string, namespaceBase: string, modoNestjs?: boolean): Promise<ModuloImportado[]>;
5
7
  export declare function nomeTaskParaRotaTypeScript(caminho: string, metodo: string): string;
6
8
  export declare function camposDeParametrosRotaTypeScript(parametros: ReturnType<typeof extrairRotasTypeScriptHttp>[number]["parametros"]): CampoImportado[];
@@ -3,7 +3,7 @@
3
3
  import { readFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import ts from "typescript";
6
- import { extrairRotasTypeScriptHttp, inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http.js";
6
+ import { extrairRotasExpressFastify, extrairRotasTypeScriptHttp, inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http.js";
7
7
  import { extrairTextoLiteral, inferirContextoPorArquivo, juntarCaminhoHttp, lerDecorator, listarArquivosRecursivos, mapearTipoPrimitivo, paraSnakeCase } from "./importador.part01.js";
8
8
  import { deduplicarCampos, extrairChamadaServiceTs } from "./importador.part04.js";
9
9
  import { caminhoImplTs, camposDeSemanticaTypeScriptHttp, criarEntidadesReferenciadas, descreverEfeitosPorHeuristica, expandirCamposTs, extrairErrosTs } from "./importador.part02.js";
@@ -98,6 +98,92 @@ export function importarNestJsDeArquivo(diretorioBase, arquivo, namespaceBase, t
98
98
  routes: deduplicarRotas(routes),
99
99
  }];
100
100
  }
101
+ function nomearTarefaRotaChamada(metodo, caminho) {
102
+ const ultimoSegmento = caminho.replace(/\{[^}]+\}/g, "").split("/").filter(Boolean).pop() ?? "recurso";
103
+ const singular = ultimoSegmento.length > 3 && /es$/.test(ultimoSegmento)
104
+ ? ultimoSegmento.replace(/es$/, "")
105
+ : ultimoSegmento.replace(/s$/, "");
106
+ switch (metodo) {
107
+ case "POST":
108
+ return `criar_${singular}`;
109
+ case "PUT":
110
+ case "PATCH":
111
+ return `atualizar_${singular}`;
112
+ case "DELETE":
113
+ return `remover_${singular}`;
114
+ case "GET":
115
+ return caminho.includes("{") ? `obter_${singular}` : `listar_${ultimoSegmento}`;
116
+ default:
117
+ return `${metodo.toLowerCase()}_${ultimoSegmento}`;
118
+ }
119
+ }
120
+ export function importarExpressFastifyDeArquivo(diretorioBase, arquivo, namespaceBase, origemDesejada) {
121
+ const relacao = path.relative(diretorioBase, arquivo);
122
+ const codigo = ts.sys.readFile(arquivo, "utf8") ?? "";
123
+ const sourceFile = ts.createSourceFile(arquivo, codigo, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
124
+ const contextoSegmentos = inferirContextoPorArquivo(relacao);
125
+ const nomeModulo = [namespaceBase, ...contextoSegmentos].join(".");
126
+ const rotasArquivo = extrairRotasExpressFastify(sourceFile).filter((rota) => rota.origem === origemDesejada);
127
+ if (rotasArquivo.length === 0) {
128
+ return [];
129
+ }
130
+ const tasks = [];
131
+ const routes = [];
132
+ for (const rota of rotasArquivo) {
133
+ const taskNome = nomearTarefaRotaChamada(rota.metodo, rota.caminho);
134
+ const implSimbolo = /^[A-Za-z_]\w*$/.test(rota.simbolo) ? rota.simbolo : undefined;
135
+ tasks.push({
136
+ nome: taskNome,
137
+ resumo: `Task importada automaticamente da rota ${rota.metodo} ${rota.caminho} em ${relacao}.`,
138
+ input: rota.parametros.map((parametro) => ({
139
+ nome: parametro.nome,
140
+ tipo: parametro.tipoSema,
141
+ obrigatorio: true,
142
+ })),
143
+ output: [],
144
+ errors: [],
145
+ effects: [],
146
+ impl: implSimbolo ? { ts: caminhoImplTs(diretorioBase, arquivo, implSimbolo) } : undefined,
147
+ origemArquivo: relacao,
148
+ origemSimbolo: rota.simbolo,
149
+ });
150
+ routes.push({
151
+ nome: `${taskNome}_publico`,
152
+ resumo: `Rota ${origemDesejada} importada automaticamente de ${relacao}.`,
153
+ metodo: rota.metodo,
154
+ caminho: rota.caminho,
155
+ task: taskNome,
156
+ input: rota.parametros.map((parametro) => ({
157
+ nome: parametro.nome,
158
+ tipo: parametro.tipoSema,
159
+ obrigatorio: true,
160
+ })),
161
+ output: [],
162
+ errors: [],
163
+ });
164
+ }
165
+ const { entities, enums } = criarEntidadesReferenciadas(new Map(), new Set(), new Set());
166
+ sincronizarRotasComTasks(routes, tasks);
167
+ return [{
168
+ nome: nomeModulo,
169
+ resumo: `Rascunho Sema importado de um contexto ${origemDesejada === "express" ? "Express" : "Fastify"} legado em ${contextoSegmentos.join("/")}.`,
170
+ entities,
171
+ enums,
172
+ tasks: deduplicarTarefas(tasks),
173
+ routes: deduplicarRotas(routes),
174
+ }];
175
+ }
176
+ export async function importarExpressFastifyBase(diretorio, namespaceBase, origem) {
177
+ const arquivos = await listarArquivosRecursivos(diretorio, [".ts"]);
178
+ const uteis = arquivos.filter((arquivo) => !arquivo.endsWith(".spec.ts")
179
+ && !arquivo.endsWith(".test.ts")
180
+ && !arquivo.endsWith(".d.ts"));
181
+ const modulos = [];
182
+ for (const arquivo of uteis) {
183
+ modulos.push(...importarExpressFastifyDeArquivo(diretorio, arquivo, namespaceBase, origem));
184
+ }
185
+ return modulos;
186
+ }
101
187
  export async function importarTypeScriptBase(diretorio, namespaceBase, modoNestjs = false) {
102
188
  const arquivos = await listarArquivosRecursivos(diretorio, [".ts"]);
103
189
  const uteis = arquivos.filter((arquivo) => !arquivo.endsWith(".spec.ts")
@@ -12,7 +12,7 @@ import { inferirContextoPorArquivo, inferirNamespaceBase, listarArquivosRecursiv
12
12
  import { camposDeParametrosRotaBackend, criarCampoResultadoBackend, descreverEfeitosPorHeuristica, inferirDatabasesPorHeuristica, mapearTipoBackendParaSema } from "./importador.part02.js";
13
13
  import { caminhoImplGenerico, importarFirebaseBase } from "./importador.part07.js";
14
14
  import { acumularModuloImportado, criarModuloImportadoSimples, importarDartBase, importarDotnetBase, importarPythonBase, resolverArquivoRustParaSimbolo, selecionarSimbolosPreferidos } from "./importador.part08.js";
15
- import { importarNextJsBase, importarTypeScriptBase } from "./importador.part06.js";
15
+ import { importarExpressFastifyBase, importarNextJsBase, importarTypeScriptBase } from "./importador.part06.js";
16
16
  import { importarAngularConsumerBase, importarFlutterConsumerBase, importarNextJsConsumerBase, importarReactViteConsumerBase } from "./importador.part04.js";
17
17
  import { formatarModuloImportado, moduloParaCodigo, montarArquivoImportado } from "./importador.part05.js";
18
18
  export async function importarJavaBase(diretorio, namespaceBase) {
@@ -300,6 +300,12 @@ export async function importarProjetoLegado(fonte, diretorio, namespaceBase) {
300
300
  if (fonte === "nestjs") {
301
301
  modulos = await importarTypeScriptBase(base, namespace, true);
302
302
  }
303
+ else if (fonte === "express") {
304
+ modulos = await importarExpressFastifyBase(base, namespace, "express");
305
+ }
306
+ else if (fonte === "fastify") {
307
+ modulos = await importarExpressFastifyBase(base, namespace, "fastify");
308
+ }
303
309
  else if (fonte === "nextjs") {
304
310
  modulos = await importarNextJsBase(base, namespace);
305
311
  }
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import { formatarDiagnosticos, gerarRespostaValidacao, temErros, } from "@sema/nucleo";
6
6
  import { carregarProjeto, } from "./projeto.js";
7
7
  import { analisarDriftLegado, } from "./drift.js";
8
- import { resolverDocumentacaoObrigatoria, verificarDocumentacaoMudanca, } from "./docs.js";
8
+ import { compactarDocumentoObrigatorio, resolverDocumentacaoObrigatoria, verificarDocumentacaoMudanca, } from "./docs.js";
9
9
  import { LIMITE_AVISO_LINHAS_CODIGO_GOVERNADO, LIMITE_AVISO_LINHAS_CONTRATO_SEMA, LIMITE_BLOQUEIO_LINHAS_CODIGO_GOVERNADO, LIMITE_BLOQUEIO_LINHAS_CONTRATO_SEMA, } from "./driftOrcamento.js";
10
10
  import { ARQUIVO_AGENT_CONTEXT_PACK, ARQUIVO_DOC_AGENTES_CAPACIDADE, ARQUIVO_SEMA_BOOT, ARQUIVO_SEMA_SMALL_MODEL, } from './agentContextTipos.js';
11
11
  import { criarGuiaCapacidadeIa, LIMITE_CARACTERES_PAYLOAD_INLINE } from './agentContextPack.js';
@@ -319,7 +319,7 @@ Antes de escolher arquivo de código, leia \`${ARQUIVO_SEMA_BOOT}\` e \`agent-co
319
319
 
320
320
  - Timeout local do agente não é falha do Sema.
321
321
  - Se uma chamada Sema estourar, aumente o timeout e tente de novo.
322
- - Se o projeto inteiro for lento, escopar para \`sema resumo "${contexto.arquivo}" --micro --para mudanca --drift none\`.
322
+ - Se o projeto inteiro for lento, escopar para \`sema resumo "${contexto.arquivo}" --micro --para mudanca --drift none\`.
323
323
  - Se o ambiente impedir continuar, pare bloqueado e não avance com código sem Sema.
324
324
 
325
325
  ## Fechamento
@@ -327,13 +327,13 @@ Antes de escolher arquivo de código, leia \`${ARQUIVO_SEMA_BOOT}\` e \`agent-co
327
327
  1. Editar o arquivo \`.sema\`.
328
328
  2. Rodar \`sema formatar "${contexto.arquivo}"\`.
329
329
  3. Rodar \`sema validar "${contexto.arquivo}" --json\`.
330
- 4. Rodar \`sema drift "${contexto.arquivo}" --cache fresh --json\`.
330
+ 4. Rodar \`sema drift "${contexto.arquivo}" --cache fresh --json\`.
331
331
  5. Fechar com \`sema verificar <arquivo-ou-pasta> --json --saida ./.tmp/verificacao-ia\`.
332
332
 
333
333
  ## Textos base para onboarding do agente
334
334
 
335
335
  - \`sema starter-ia\`
336
- - \`sema resumo "${contexto.arquivo}" --micro --para onboarding --drift none\`
336
+ - \`sema resumo "${contexto.arquivo}" --micro --para onboarding --drift none\`
337
337
  - \`sema prompt-curto "${contexto.arquivo}" --para mudanca\`
338
338
  - \`sema prompt-ia\`
339
339
  `;
@@ -372,7 +372,7 @@ export function resolverEntradaDocs(posicionais, args) {
372
372
  export async function comandoDocsImpacto(posicionais, args, emJson) {
373
373
  const { intencao, arquivosAlvo } = resolverEntradaDocs(posicionais, args);
374
374
  if (!intencao.trim()) {
375
- console.error("Uso: sema docs-impacto --intencao <acao> [--arquivo <caminho>] [--criar-ausentes] [--json]");
375
+ console.error("Uso: sema docs-impacto --intencao <acao> [--arquivo <caminho>] [--criar-ausentes] [--completo] [--json]");
376
376
  return 1;
377
377
  }
378
378
  const resultado = await resolverDocumentacaoObrigatoria({
@@ -381,9 +381,16 @@ export async function comandoDocsImpacto(posicionais, args, emJson) {
381
381
  arquivosAlvo,
382
382
  criarAusentes: possuiFlag(args, "--criar-ausentes"),
383
383
  });
384
+ const completo = possuiFlag(args, "--completo");
384
385
  const payload = {
385
386
  comando: "docs-impacto",
386
- ...resultado,
387
+ ...(completo ? resultado : {
388
+ ...resultado,
389
+ leituraObrigatoria: resultado.leituraObrigatoria.map(compactarDocumentoObrigatorio),
390
+ docsAusentes: resultado.docsAusentes.map(compactarDocumentoObrigatorio),
391
+ docsCriadas: resultado.docsCriadas.map(compactarDocumentoObrigatorio),
392
+ modoDocumentos: "compacto",
393
+ }),
387
394
  };
388
395
  if (emJson) {
389
396
  console.log(JSON.stringify(payload, null, 2));
@@ -1,9 +1,15 @@
1
1
  import { type AlvoGeracao, type FrameworkGeracao } from "@sema/padroes";
2
2
  import type { EstruturaSaida } from "./tipos.js";
3
+ export interface OpcoesVerificar {
4
+ alvo?: string;
5
+ semCache?: boolean;
6
+ }
7
+ export declare function alvosSuportadosPelaStack(fontesLegado: string[] | undefined): AlvoGeracao[] | undefined;
8
+ export declare function avisoEscopoStackVerificacao(fontesLegado: string[] | undefined, alvosEfetivos: AlvoGeracao[]): string | undefined;
3
9
  import { type ResumoModuloVerificacao } from './geracaoCore.js';
4
10
  export declare function comandoPromptCurto(entrada: string | undefined, args: string[], emJson: boolean): Promise<number>;
5
11
  export declare function comandoContextoIa(arquivo: string, pastaSaida: string | undefined, emJson: boolean): Promise<number>;
6
12
  export declare function comandoTestar(arquivo: string, alvo: AlvoGeracao, saida: string, estrutura: EstruturaSaida, framework: FrameworkGeracao): Promise<number>;
7
13
  export declare function imprimirResumoVerificacao(resumos: ResumoModuloVerificacao[]): void;
8
- export declare function comandoVerificar(entrada: string | undefined, baseSaida: string, cwd?: string): Promise<number>;
9
- export declare function comandoVerificarJson(entrada: string | undefined, baseSaida: string, cwd?: string): Promise<number>;
14
+ export declare function comandoVerificar(entrada: string | undefined, baseSaida: string, cwd?: string, opcoes?: OpcoesVerificar): Promise<number>;
15
+ export declare function comandoVerificarJson(entrada: string | undefined, baseSaida: string, cwd?: string, opcoes?: OpcoesVerificar): Promise<number>;
@@ -2,8 +2,55 @@
2
2
  // Descrição: preserva modos explícitos nas superfícies auxiliares de contexto.
3
3
  import { readFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
+ import pacoteCli from "../package.json" with { type: "json" };
5
6
  import { compilarCodigo, formatarDiagnosticos, lerArquivoTexto, temErros, } from "@sema/nucleo";
6
7
  import { carregarProjeto, resolverAlvosVerificacao, } from "./projeto.js";
8
+ import { SCHEMA_CACHE_VERIFICACAO, calcularChaveVerificacao, carregarCacheVerificacao, gravarCacheVerificacao, } from "./verificacaoCache.js";
9
+ const FONTES_POR_ALVO_SUPORTADO = {
10
+ typescript: ["typescript", "javascript"],
11
+ javascript: ["typescript", "javascript"],
12
+ nestjs: ["typescript", "javascript"],
13
+ express: ["typescript", "javascript"],
14
+ fastify: ["typescript", "javascript"],
15
+ nextjs: ["typescript", "javascript"],
16
+ "nextjs-consumer": ["typescript", "javascript"],
17
+ "react-vite-consumer": ["typescript", "javascript"],
18
+ "angular-consumer": ["typescript", "javascript"],
19
+ firebase: ["typescript", "javascript"],
20
+ python: ["python"],
21
+ fastapi: ["python"],
22
+ flask: ["python"],
23
+ dart: ["dart"],
24
+ "flutter-consumer": ["dart"],
25
+ dotnet: ["dotnet"],
26
+ java: ["javascript"],
27
+ lua: ["lua"],
28
+ php: ["php"],
29
+ cpp: ["cpp"],
30
+ };
31
+ export function alvosSuportadosPelaStack(fontesLegado) {
32
+ if (!fontesLegado?.length) {
33
+ return undefined;
34
+ }
35
+ const suportados = new Set();
36
+ for (const fonte of fontesLegado) {
37
+ for (const alvo of FONTES_POR_ALVO_SUPORTADO[fonte] ?? []) {
38
+ suportados.add(alvo);
39
+ }
40
+ }
41
+ return suportados.size > 0 ? [...suportados] : undefined;
42
+ }
43
+ export function avisoEscopoStackVerificacao(fontesLegado, alvosEfetivos) {
44
+ const suportados = alvosSuportadosPelaStack(fontesLegado);
45
+ if (!suportados) {
46
+ return undefined;
47
+ }
48
+ const foraDaStack = alvosEfetivos.filter((alvo) => alvo !== "html" && alvo !== "css" && !suportados.includes(alvo));
49
+ if (foraDaStack.length === 0) {
50
+ return undefined;
51
+ }
52
+ return `Alvos fora da stack detectada (${(fontesLegado ?? []).join(", ")}): ${foraDaStack.join(", ")}. Considere reduzir "alvos" no sema.config.json, remover "fontesLegado" auto-declaradas ou usar --alvo unico.`;
53
+ }
7
54
  import { escreverArquivos, formatarAvisoArtefatosGeradosAcimaDoLimite, } from './fsGovernado.js';
8
55
  import { obterOpcao } from './cliArgs.js';
9
56
  import { avaliarDependenciasVerificacao, imprimirFalhaDependenciasVerificacao } from './doctorCommand.js';
@@ -154,7 +201,7 @@ export function imprimirResumoVerificacao(resumos) {
154
201
  }
155
202
  console.log(`Totais: modulos=${resumos.length} alvos=${totalAlvos} arquivos=${totalArquivos} testes=${totalTestes}`);
156
203
  }
157
- export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd()) {
204
+ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(), opcoes = {}) {
158
205
  const contextoProjeto = await carregarProjeto(entrada, cwd);
159
206
  const modulos = contextoProjeto.modulosSelecionados;
160
207
  const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
@@ -162,7 +209,13 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd())
162
209
  console.error(formatarDiagnosticos(diagnosticos));
163
210
  return 1;
164
211
  }
165
- const alvos = resolverAlvosVerificacao(contextoProjeto.configCarregada);
212
+ const alvos = opcoes.alvo
213
+ ? [opcoes.alvo]
214
+ : resolverAlvosVerificacao(contextoProjeto.configCarregada);
215
+ const avisoEscopo = avisoEscopoStackVerificacao(contextoProjeto.configCarregada?.config.fontesLegado, alvos);
216
+ if (avisoEscopo) {
217
+ console.warn(avisoEscopo);
218
+ }
166
219
  const configuracoesAlvo = alvos.map((alvo) => ({
167
220
  alvo,
168
221
  ...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
@@ -187,13 +240,55 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd())
187
240
  arquivoFonte: modulo.caminho,
188
241
  alvos: [],
189
242
  };
243
+ const conteudoContrato = await readFile(modulo.caminho, "utf8").catch(() => "");
190
244
  for (const configuracaoAlvo of configuracoesAlvo) {
191
245
  const { alvo, framework, estrutura } = configuracaoAlvo;
192
246
  const pastaAlvo = path.join(baseSaida, alvo, nomeSubpastaModulo(modulo.caminho));
247
+ const chave = calcularChaveVerificacao({
248
+ versaoCli: pacoteCli.version,
249
+ versaoNode: process.versions.node ?? "",
250
+ modulo: ir.nome,
251
+ alvo,
252
+ framework: framework ?? "",
253
+ estrutura,
254
+ conteudoContrato,
255
+ });
256
+ const emCache = opcoes.semCache ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
257
+ if (emCache) {
258
+ resumoModulo.alvos.push({
259
+ alvo,
260
+ arquivosGerados: emCache.arquivosGerados.length,
261
+ quantidadeTestes: emCache.quantidadeTestes,
262
+ pastaSaida: pastaAlvo,
263
+ sucesso: emCache.sucesso,
264
+ framework,
265
+ estrutura,
266
+ testesExecutados: false,
267
+ origem: "cache",
268
+ });
269
+ if (!emCache.sucesso) {
270
+ imprimirResumoVerificacao([...resumos, resumoModulo]);
271
+ console.error(`Falha registrada em cache na verificacao do modulo ${modulo.caminho} para o alvo ${alvo}.`);
272
+ return 1;
273
+ }
274
+ continue;
275
+ }
193
276
  const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
194
277
  const escrita = await escreverArquivos(pastaAlvo, arquivos, { artefatoGerado: true });
195
278
  artefatosGeradosAcimaDoLimite.push(...escrita.artefatosGeradosAcimaDoLimite);
196
279
  const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework);
280
+ await gravarCacheVerificacao({
281
+ schemaVersion: SCHEMA_CACHE_VERIFICACAO,
282
+ chave,
283
+ versaoCli: pacoteCli.version,
284
+ alvo,
285
+ modulo: ir.nome,
286
+ sucesso: execucao.codigoSaida === 0,
287
+ quantidadeTestes: execucao.quantidadeTestes,
288
+ testesExecutados,
289
+ arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
290
+ geradoEm: new Date().toISOString(),
291
+ });
197
292
  resumoModulo.alvos.push({
198
293
  alvo,
199
294
  arquivosGerados: arquivos.length,
@@ -203,6 +298,7 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd())
203
298
  framework,
204
299
  estrutura,
205
300
  testesExecutados,
301
+ origem: "executado",
206
302
  });
207
303
  if (execucao.codigoSaida !== 0) {
208
304
  imprimirResumoVerificacao([...resumos, resumoModulo]);
@@ -220,7 +316,7 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd())
220
316
  console.log("Verificacao completa concluida com sucesso.");
221
317
  return 0;
222
318
  }
223
- export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd()) {
319
+ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd(), opcoes = {}) {
224
320
  const contextoProjeto = await carregarProjeto(entrada, cwd);
225
321
  const modulos = contextoProjeto.modulosSelecionados;
226
322
  const diagnosticos = modulos.flatMap((item) => item.resultado.diagnosticos);
@@ -234,7 +330,10 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
234
330
  }, null, 2));
235
331
  return 1;
236
332
  }
237
- const alvos = resolverAlvosVerificacao(contextoProjeto.configCarregada);
333
+ const alvos = opcoes.alvo
334
+ ? [opcoes.alvo]
335
+ : resolverAlvosVerificacao(contextoProjeto.configCarregada);
336
+ const avisoEscopo = avisoEscopoStackVerificacao(contextoProjeto.configCarregada?.config.fontesLegado, alvos);
238
337
  const configuracoesAlvo = alvos.map((alvo) => ({
239
338
  alvo,
240
339
  ...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
@@ -273,13 +372,54 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
273
372
  alvos: [],
274
373
  saidaTestes: [],
275
374
  };
375
+ const conteudoContrato = await readFile(modulo.caminho, "utf8").catch(() => "");
276
376
  for (const configuracaoAlvo of configuracoesAlvo) {
277
377
  const { alvo, framework, estrutura } = configuracaoAlvo;
278
378
  const pastaAlvo = path.join(baseSaida, alvo, nomeSubpastaModulo(modulo.caminho));
379
+ const chave = calcularChaveVerificacao({
380
+ versaoCli: pacoteCli.version,
381
+ versaoNode: process.versions.node ?? "",
382
+ modulo: ir.nome,
383
+ alvo,
384
+ framework: framework ?? "",
385
+ estrutura,
386
+ conteudoContrato,
387
+ });
388
+ const emCache = opcoes.semCache ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
389
+ if (emCache) {
390
+ resumoModulo.alvos.push({
391
+ alvo,
392
+ arquivosGerados: emCache.arquivosGerados.length,
393
+ quantidadeTestes: emCache.quantidadeTestes,
394
+ pastaSaida: pastaAlvo,
395
+ sucesso: emCache.sucesso,
396
+ framework,
397
+ estrutura,
398
+ testesExecutados: false,
399
+ origem: "cache",
400
+ });
401
+ resumoModulo.saidaTestes.push({ alvo, stdout: "", stderr: `resultado reutilizado do cache de verificacao (chave ${chave.slice(0, 12)})` });
402
+ if (!emCache.sucesso) {
403
+ codigoSaida = 1;
404
+ }
405
+ continue;
406
+ }
279
407
  const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
280
408
  const escrita = await escreverArquivos(pastaAlvo, arquivos, { artefatoGerado: true });
281
409
  artefatosGeradosAcimaDoLimite.push(...escrita.artefatosGeradosAcimaDoLimite);
282
410
  const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework, true);
411
+ await gravarCacheVerificacao({
412
+ schemaVersion: SCHEMA_CACHE_VERIFICACAO,
413
+ chave,
414
+ versaoCli: pacoteCli.version,
415
+ alvo,
416
+ modulo: ir.nome,
417
+ sucesso: execucao.codigoSaida === 0,
418
+ quantidadeTestes: execucao.quantidadeTestes,
419
+ testesExecutados,
420
+ arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
421
+ geradoEm: new Date().toISOString(),
422
+ });
283
423
  resumoModulo.alvos.push({
284
424
  alvo,
285
425
  arquivosGerados: arquivos.length,
@@ -289,6 +429,7 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
289
429
  framework,
290
430
  estrutura,
291
431
  testesExecutados,
432
+ origem: "executado",
292
433
  });
293
434
  resumoModulo.saidaTestes.push({ alvo, stdout: execucao.saidaPadrao, stderr: execucao.saidaErro });
294
435
  if (execucao.codigoSaida !== 0) {
@@ -306,6 +447,7 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
306
447
  console.log(JSON.stringify({
307
448
  comando: "verificar",
308
449
  sucesso: codigoSaida === 0,
450
+ avisoEscopo,
309
451
  modulos: resumos,
310
452
  totais,
311
453
  artefatosGeradosAcimaDoLimite,
@@ -34,7 +34,7 @@ export async function principal() {
34
34
  let codigoSaida = 0;
35
35
  switch (comando) {
36
36
  case "iniciar":
37
- codigoSaida = await comandoIniciar(cwd, normalizarTemplateIniciar(obterOpcao(resto, "--template")), { force: possuiFlag(resto, "--force") });
37
+ codigoSaida = await comandoIniciar(cwd, normalizarTemplateIniciar(obterOpcao(resto, "--template")), { force: possuiFlag(resto, "--force"), exemplosCompletos: possuiFlag(resto, "--com-exemplos") });
38
38
  break;
39
39
  case "author":
40
40
  codigoSaida = await comandoAuthor(posicionais, resto, possuiFlag(resto, "--json"));
@@ -100,8 +100,8 @@ export async function principal() {
100
100
  break;
101
101
  case "verificar":
102
102
  codigoSaida = possuiFlag(resto, "--json")
103
- ? await comandoVerificarJson(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd)
104
- : await comandoVerificar(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd);
103
+ ? await comandoVerificarJson(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd, { alvo: obterOpcao(resto, "--alvo"), semCache: possuiFlag(resto, "--sem-cache") })
104
+ : await comandoVerificar(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd, { alvo: obterOpcao(resto, "--alvo"), semCache: possuiFlag(resto, "--sem-cache") });
105
105
  break;
106
106
  case "inspecionar":
107
107
  codigoSaida = await comandoInspecionar(posicionais[0], possuiFlag(resto, "--json"), cwd, resto);