@semacode/cli 4.1.1 → 4.1.3

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.
@@ -461,7 +461,7 @@ function validarComandoBasico(comando, args) {
461
461
  const estrutura = valorOpcao(parsed, "--estrutura"), framework = valorOpcao(parsed, "--framework");
462
462
  return Boolean(alvo && ALVOS_GERACAO.has(alvo)) && (!estrutura || ESTRUTURAS.has(estrutura)) && (!framework || FRAMEWORKS.has(framework));
463
463
  }
464
- case "verificar": return parsearArgumentos(args, { opcoes: ["--saida", "--alvo"], flags: ["--sem-cache"], maxPosicionais: 1 }) !== null;
464
+ case "verificar": return parsearArgumentos(args, { opcoes: ["--saida", "--alvo"], flags: ["--sem-cache", "--preservar-implementacao"], maxPosicionais: 1 }) !== null;
465
465
  case "sessao": return parsearArgumentos(args, { flags: ["--json"], maxPosicionais: 2 }) !== null;
466
466
  case "formatar": return parsearArgumentos(args, { flags: ["--check"], maxPosicionais: 1 }) !== null;
467
467
  case "inspecionar": {
@@ -29,7 +29,8 @@ export function criarAjudaRaiz(versao) {
29
29
  "Arquivos existentes são preservados; use --force somente para sobrescrita explícita. O starter de exemplos vem por padrão; --com-exemplos instala o pacote completo. Em subpasta de workspace já governado, a subpasta é registrada nos diretoriosCodigo do sema.config.json ancestral em vez de criar config local.",
30
30
  "sema validar contratos/<modulo>.sema --json",
31
31
  "sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio>",
32
- "sema verificar <arquivo-ou-pasta> [--saida <dir>] [--alvo <alvo>] [--sem-cache] --json",
32
+ "sema verificar <arquivo-ou-pasta> [--saida <dir>] [--alvo <alvo>] [--sem-cache] [--preservar-implementacao] --json",
33
+ " --preservar-implementacao: somente Python base; preserva implementação local, regenera testes e ignora cache. Não comprova produção.",
33
34
  "",
34
35
  "[2] Editar projeto que já usa Sema",
35
36
  "sema inspecionar . [--drift <none|cache|fresh>] --json",
@@ -121,7 +122,7 @@ export function criarAjudaRaiz(versao) {
121
122
  "geração: sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
122
123
  "teste local: sema testar <arquivo.sema> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
123
124
  "aliases nativos: cs|csharp -> dotnet; c++|cxx|cc -> cpp (CLI e sema.config.json)",
124
- "verificação final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--alvo <alvo>] [--sem-cache] [--json]",
125
+ "verificação final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--alvo <alvo>] [--sem-cache] [--preservar-implementacao] [--json]",
125
126
  "formatação: sema formatar <arquivo-ou-pasta> [--check] [--json]",
126
127
  "author: sema author <iniciar|validar|briefing|revisar-cliches|validar-narrativa|validar-proibicoes> [arquivo] [--tema-sensivel] [--preset conto|romance|roteiro|lore|campanha] [--saida <arquivo>] [--texto <texto>] [--json]",
127
128
  "profile: sema profile validar <software|workflow|ops|game|simulation|legal|research|redacao|propostas|conversas> <arquivo-ou-pasta> [--maturidade draft|prototype|production|critical] [--preset <preset>] [--artefato <texto>|--artefato-arquivo <arquivo>] [--json]",
@@ -18,7 +18,7 @@ export interface ResumoAlvoVerificacao {
18
18
  estrutura: EstruturaSaida;
19
19
  testesExecutados: boolean;
20
20
  origem?: "executado" | "cache";
21
- tipoEvidencia?: "andaime_demonstrativo";
21
+ tipoEvidencia?: "andaime_demonstrativo" | "implementacao_local_preservada";
22
22
  implementacaoExternaExecutada?: boolean;
23
23
  }
24
24
  export interface ResumoModuloVerificacao {
@@ -3,6 +3,7 @@ import type { EstruturaSaida } from "./tipos.js";
3
3
  export interface OpcoesVerificar {
4
4
  alvo?: string;
5
5
  semCache?: boolean;
6
+ preservarImplementacao?: boolean;
6
7
  }
7
8
  export declare function alvosSuportadosPelaStack(fontesLegado: string[] | undefined): AlvoGeracao[] | undefined;
8
9
  export declare function avisoEscopoStackVerificacao(fontesLegado: string[] | undefined, alvosEfetivos: AlvoGeracao[]): string | undefined;
@@ -6,6 +6,7 @@ import pacoteCli from "../package.json" with { type: "json" };
6
6
  import { compilarCodigo, formatarDiagnosticos, lerArquivoTexto, temErros, } from "@sema/nucleo";
7
7
  import { carregarProjeto, resolverAlvosVerificacao, } from "./projeto.js";
8
8
  import { SCHEMA_CACHE_VERIFICACAO, calcularChaveVerificacao, carregarCacheVerificacao, gravarCacheVerificacao, } from "./verificacaoCache.js";
9
+ import { erroModoPreservacao, prepararImplementacaoPythonLocal, evidenciaPython } from "./verificacaoPythonLocal.js";
9
10
  const FONTES_POR_ALVO_SUPORTADO = {
10
11
  typescript: ["typescript", "javascript"],
11
12
  javascript: ["typescript", "javascript"],
@@ -210,7 +211,7 @@ export function imprimirResumoVerificacao(resumos) {
210
211
  totalArquivos += alvo.arquivosGerados;
211
212
  totalTestes += alvo.quantidadeTestes;
212
213
  totalAlvos += 1;
213
- console.log(` 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}`);
214
+ console.log(` alvo=${alvo.alvo} framework=${alvo.framework} estrutura=${alvo.estrutura} status=${alvo.sucesso ? "ok" : "falhou"} arquivos=${alvo.arquivosGerados} testes=${alvo.quantidadeTestes}${alvo.testesExecutados ? "" : " (skip)"}${alvo.tipoEvidencia ? ` evidencia=${alvo.tipoEvidencia} (não comprova produção)` : ""} saida=${alvo.pastaSaida}`);
214
215
  }
215
216
  }
216
217
  console.log(`Totais: modulos=${resumos.length} alvos=${totalAlvos} arquivos=${totalArquivos} testes=${totalTestes}`);
@@ -240,6 +241,11 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
240
241
  ...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
241
242
  }));
242
243
  const incompatibilidade = configuracoesAlvo.find((item) => item.incompatibilidade);
244
+ const erroPreservacao = erroModoPreservacao(opcoes.preservarImplementacao, configuracoesAlvo);
245
+ if (erroPreservacao) {
246
+ console.error(erroPreservacao);
247
+ return 1;
248
+ }
243
249
  if (incompatibilidade?.incompatibilidade) {
244
250
  console.error(incompatibilidade.incompatibilidade);
245
251
  return 1;
@@ -272,7 +278,7 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
272
278
  estrutura,
273
279
  conteudoContrato,
274
280
  });
275
- const emCache = opcoes.semCache ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
281
+ const emCache = opcoes.semCache || opcoes.preservarImplementacao ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
276
282
  if (emCache) {
277
283
  resumoModulo.alvos.push({
278
284
  alvo,
@@ -294,21 +300,27 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
294
300
  continue;
295
301
  }
296
302
  const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
297
- const escrita = await escreverArquivos(pastaAlvo, arquivos, { artefatoGerado: true });
303
+ const preparacao = opcoes.preservarImplementacao ? await prepararImplementacaoPythonLocal(pastaAlvo, arquivos) : { arquivosParaEscrita: arquivos };
304
+ if (preparacao.erro) {
305
+ console.error(preparacao.erro);
306
+ return 1;
307
+ }
308
+ const escrita = await escreverArquivos(pastaAlvo, preparacao.arquivosParaEscrita, { artefatoGerado: true });
298
309
  artefatosGeradosAcimaDoLimite.push(...escrita.artefatosGeradosAcimaDoLimite);
299
310
  const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework);
300
- await gravarCacheVerificacao({
301
- schemaVersion: SCHEMA_CACHE_VERIFICACAO,
302
- chave,
303
- versaoCli: pacoteCli.version,
304
- alvo,
305
- modulo: ir.nome,
306
- sucesso: execucao.codigoSaida === 0,
307
- quantidadeTestes: execucao.quantidadeTestes,
308
- testesExecutados,
309
- arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
310
- geradoEm: new Date().toISOString(),
311
- });
311
+ if (!opcoes.preservarImplementacao)
312
+ await gravarCacheVerificacao({
313
+ schemaVersion: SCHEMA_CACHE_VERIFICACAO,
314
+ chave,
315
+ versaoCli: pacoteCli.version,
316
+ alvo,
317
+ modulo: ir.nome,
318
+ sucesso: execucao.codigoSaida === 0,
319
+ quantidadeTestes: execucao.quantidadeTestes,
320
+ testesExecutados,
321
+ arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
322
+ geradoEm: new Date().toISOString(),
323
+ });
312
324
  resumoModulo.alvos.push({
313
325
  alvo,
314
326
  arquivosGerados: arquivos.length,
@@ -319,7 +331,7 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
319
331
  estrutura,
320
332
  testesExecutados,
321
333
  origem: "executado",
322
- ...(alvo === "python" ? { tipoEvidencia: "andaime_demonstrativo", implementacaoExternaExecutada: false } : {}),
334
+ ...(alvo === "python" ? evidenciaPython(opcoes.preservarImplementacao) : {}),
323
335
  });
324
336
  if (execucao.codigoSaida !== 0) {
325
337
  imprimirResumoVerificacao([...resumos, resumoModulo]);
@@ -372,6 +384,11 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
372
384
  ...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
373
385
  }));
374
386
  const incompatibilidade = configuracoesAlvo.find((item) => item.incompatibilidade);
387
+ const erroPreservacao = erroModoPreservacao(opcoes.preservarImplementacao, configuracoesAlvo);
388
+ if (erroPreservacao) {
389
+ console.log(JSON.stringify({ comando: "verificar", sucesso: false, erro: erroPreservacao, modulos: [], totais: { modulos: 0, alvos: 0, arquivos: 0, testes: 0 } }));
390
+ return 1;
391
+ }
375
392
  if (incompatibilidade?.incompatibilidade) {
376
393
  console.log(JSON.stringify({
377
394
  comando: "verificar",
@@ -418,7 +435,7 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
418
435
  estrutura,
419
436
  conteudoContrato,
420
437
  });
421
- const emCache = opcoes.semCache ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
438
+ const emCache = opcoes.semCache || opcoes.preservarImplementacao ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
422
439
  if (emCache) {
423
440
  resumoModulo.alvos.push({
424
441
  alvo,
@@ -439,21 +456,27 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
439
456
  continue;
440
457
  }
441
458
  const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
442
- const escrita = await escreverArquivos(pastaAlvo, arquivos, { artefatoGerado: true });
459
+ const preparacao = opcoes.preservarImplementacao ? await prepararImplementacaoPythonLocal(pastaAlvo, arquivos) : { arquivosParaEscrita: arquivos };
460
+ if (preparacao.erro) {
461
+ console.log(JSON.stringify({ comando: "verificar", sucesso: false, erro: preparacao.erro, modulos: resumos, totais: { modulos: resumos.length, alvos: 0, arquivos: 0, testes: 0 } }));
462
+ return 1;
463
+ }
464
+ const escrita = await escreverArquivos(pastaAlvo, preparacao.arquivosParaEscrita, { artefatoGerado: true });
443
465
  artefatosGeradosAcimaDoLimite.push(...escrita.artefatosGeradosAcimaDoLimite);
444
466
  const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework, true);
445
- await gravarCacheVerificacao({
446
- schemaVersion: SCHEMA_CACHE_VERIFICACAO,
447
- chave,
448
- versaoCli: pacoteCli.version,
449
- alvo,
450
- modulo: ir.nome,
451
- sucesso: execucao.codigoSaida === 0,
452
- quantidadeTestes: execucao.quantidadeTestes,
453
- testesExecutados,
454
- arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
455
- geradoEm: new Date().toISOString(),
456
- });
467
+ if (!opcoes.preservarImplementacao)
468
+ await gravarCacheVerificacao({
469
+ schemaVersion: SCHEMA_CACHE_VERIFICACAO,
470
+ chave,
471
+ versaoCli: pacoteCli.version,
472
+ alvo,
473
+ modulo: ir.nome,
474
+ sucesso: execucao.codigoSaida === 0,
475
+ quantidadeTestes: execucao.quantidadeTestes,
476
+ testesExecutados,
477
+ arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
478
+ geradoEm: new Date().toISOString(),
479
+ });
457
480
  resumoModulo.alvos.push({
458
481
  alvo,
459
482
  arquivosGerados: arquivos.length,
@@ -464,7 +487,7 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
464
487
  estrutura,
465
488
  testesExecutados,
466
489
  origem: "executado",
467
- ...(alvo === "python" ? { tipoEvidencia: "andaime_demonstrativo", implementacaoExternaExecutada: false } : {}),
490
+ ...(alvo === "python" ? evidenciaPython(opcoes.preservarImplementacao) : {}),
468
491
  });
469
492
  resumoModulo.saidaTestes.push({ alvo, stdout: execucao.saidaPadrao, stderr: execucao.saidaErro });
470
493
  if (execucao.codigoSaida !== 0) {
@@ -105,8 +105,8 @@ export async function principal() {
105
105
  break;
106
106
  case "verificar":
107
107
  codigoSaida = possuiFlag(resto, "--json")
108
- ? 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") })
109
- : 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") });
108
+ ? 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"), preservarImplementacao: possuiFlag(resto, "--preservar-implementacao") })
109
+ : 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"), preservarImplementacao: possuiFlag(resto, "--preservar-implementacao") });
110
110
  break;
111
111
  case "inspecionar":
112
112
  codigoSaida = await comandoInspecionar(posicionais[0], possuiFlag(resto, "--json"), cwd, resto);
@@ -0,0 +1,17 @@
1
+ type Arquivo = {
2
+ caminhoRelativo: string;
3
+ conteudo: string;
4
+ };
5
+ export declare function erroModoPreservacao(preservar: boolean | undefined, configuracoes: Array<{
6
+ alvo: string;
7
+ framework: string;
8
+ }>): string | undefined;
9
+ export declare function prepararImplementacaoPythonLocal(base: string, arquivos: Arquivo[]): Promise<{
10
+ arquivosParaEscrita: Arquivo[];
11
+ erro?: string;
12
+ }>;
13
+ export declare function evidenciaPython(preservar: boolean | undefined): {
14
+ tipoEvidencia: "andaime_demonstrativo" | "implementacao_local_preservada";
15
+ implementacaoExternaExecutada: boolean;
16
+ };
17
+ export {};
@@ -0,0 +1,46 @@
1
+ // SEMA-GOVERNED: sema.produto.cli_verificacao
2
+ // Contrato: contratos/sema/cli_verificacao.sema
3
+ // Descrição: preserva Python local explicitamente; testes continuam derivados do contrato, sem cache.
4
+ import { lstat } from "node:fs/promises";
5
+ import path from "node:path";
6
+ export function erroModoPreservacao(preservar, configuracoes) {
7
+ if (preservar && (configuracoes.length !== 1 || configuracoes[0].alvo !== "python" || configuracoes[0].framework !== "base")) {
8
+ return "--preservar-implementacao exige --alvo python e framework base.";
9
+ }
10
+ return undefined;
11
+ }
12
+ export async function prepararImplementacaoPythonLocal(base, arquivos) {
13
+ const implementacoes = arquivos.filter(arquivo => arquivo.caminhoRelativo.endsWith(".py") && !path.basename(arquivo.caminhoRelativo).startsWith("test_"));
14
+ if (implementacoes.length === 0)
15
+ return { arquivosParaEscrita: [], erro: "Nenhuma implementação Python local prevista pelo contrato." };
16
+ try {
17
+ for (const arquivo of implementacoes) {
18
+ const destino = path.resolve(base, arquivo.caminhoRelativo);
19
+ const relativo = path.relative(path.resolve(base), destino);
20
+ if (path.isAbsolute(relativo) || relativo.split(path.sep).includes(".."))
21
+ throw new Error("escape");
22
+ let atual = destino;
23
+ while (true) {
24
+ const info = await lstat(atual);
25
+ if (info.isSymbolicLink() || (atual === destino ? !info.isFile() || info.nlink > 1 : !info.isDirectory()))
26
+ throw new Error("link ou tipo inválido");
27
+ const pai = path.dirname(atual);
28
+ if (pai === atual)
29
+ break;
30
+ atual = pai;
31
+ }
32
+ }
33
+ const preservados = new Set(implementacoes.map(arquivo => arquivo.caminhoRelativo));
34
+ return { arquivosParaEscrita: arquivos.filter(arquivo => !preservados.has(arquivo.caminhoRelativo)) };
35
+ }
36
+ catch {
37
+ return { arquivosParaEscrita: [], erro: "Implementação Python local ausente ou caminho não seguro. Gere e preencha a implementação antes de usar --preservar-implementacao." };
38
+ }
39
+ }
40
+ export function evidenciaPython(preservar) {
41
+ return {
42
+ tipoEvidencia: preservar ? "implementacao_local_preservada" : "andaime_demonstrativo",
43
+ implementacaoExternaExecutada: false,
44
+ };
45
+ }
46
+ //# sourceMappingURL=verificacaoPythonLocal.js.map
package/docs/testing.md CHANGED
@@ -169,3 +169,21 @@ this scaffold: its tests and guarantees cannot establish production behavior.
169
169
  Use the application's real tests and drift evidence for that claim. Generated
170
170
  failure cases currently retain the explicit synthetic error context and are
171
171
  not evidence that the real implementation produces those errors.
172
+
173
+ To execute an explicitly filled local Python implementation without overwriting
174
+ it, use the same verification output base that contains the generated module:
175
+
176
+ ```bash
177
+ sema verificar contratos/app.sema --alvo python --saida .tmp/check --preservar-implementacao --json
178
+ ```
179
+
180
+ This opt-in mode supports only Python with the base framework. All expected
181
+ implementation files must already exist as regular files, without symlinks,
182
+ junctions or hardlinks. It preserves those files byte for byte, regenerates the
183
+ contract tests and support artifacts, and always runs the tests without reading
184
+ or writing the verification cache. Missing implementations fail before writes;
185
+ new expectations and incorrect implementation results still fail. The output
186
+ reports `tipoEvidencia: implementacao_local_preservada` and
187
+ `implementacaoExternaExecutada: false`: it proves execution of those local files,
188
+ not automatic execution of a linked `impl`, a remote deployment or production.
189
+ Normal verification retains the demonstration scaffold behavior.
@@ -8,7 +8,7 @@ export declare function gerarComentarioInvariantesTypeScript(invariantes: Expres
8
8
  export declare function gerarLiteralCamposTypeScript(campos: IrCampo[]): string;
9
9
  export declare function coletarTiposExternos(modulo: IrModulo): string[];
10
10
  export declare function gerarLiteralErrosTypeScript(erros: Record<string, string>): string;
11
- export declare function formatarValorTypeScript(valor: string, camposConhecidos: Set<string>, variavel: string): string;
11
+ export declare function formatarValorTypeScript(valor: string, camposConhecidos: Set<string>, variavel: string, literal?: boolean): string;
12
12
  export declare function resolverReferenciaTypeScript(referencia: string, variavel: string): string;
13
13
  export declare function gerarExpressaoTypeScript(expressao: ExpressaoSemantica, camposConhecidos: Set<string>, variavel: string): string;
14
14
  export declare function valorPadraoTypeScript(tipo: string, nomeCampo: string): string;
@@ -17,7 +17,7 @@ export declare function dividirLiteralNoNivelRaiz(valor: string, separador: ","
17
17
  export declare function resolverTipoItemTeste(tipoDeclarado?: string): string | undefined;
18
18
  export declare function formatarLiteralTesteTypeScript(valor: string, tipoDeclarado?: string): unknown;
19
19
  export declare function converterBlocoTesteParaValorTypeScript(bloco: IrBlocoDeclarativo, tiposDeclarados?: Map<string, string>): Record<string, unknown>;
20
- export declare function gerarPreparacaoSaida(task: IrTask): string;
20
+ export declare function gerarPreparacaoSaida(task: IrTask, tiposDeclarados?: Map<string, Map<string, string>>): string;
21
21
  export declare function gerarValidacoes(task: IrTask): string;
22
22
  export declare function gerarGarantias(task: IrTask): string;
23
23
  export declare function gerarFuncaoGarantias(task: IrTask): string;
@@ -48,7 +48,7 @@ export function gerarLiteralErrosTypeScript(erros) {
48
48
  }
49
49
  return `{\n${entradas.map(([nome, mensagem]) => ` ${JSON.stringify(nome)}: ${JSON.stringify(mensagem)},`).join("\n")}\n}`;
50
50
  }
51
- export function formatarValorTypeScript(valor, camposConhecidos, variavel) {
51
+ export function formatarValorTypeScript(valor, camposConhecidos, variavel, literal = false) {
52
52
  const texto = valor.trim();
53
53
  if (/^-?\d+(?:\.\d+)?$/.test(texto)) {
54
54
  return texto;
@@ -62,7 +62,7 @@ export function formatarValorTypeScript(valor, camposConhecidos, variavel) {
62
62
  if (texto === "nulo") {
63
63
  return "null";
64
64
  }
65
- if (camposConhecidos.has(texto.split(".")[0] ?? texto)) {
65
+ if (!literal && camposConhecidos.has(texto.split(".")[0] ?? texto)) {
66
66
  return `${variavel}.${texto}`;
67
67
  }
68
68
  return JSON.stringify(texto);
@@ -260,29 +260,131 @@ export function converterBlocoTesteParaValorTypeScript(bloco, tiposDeclarados) {
260
260
  }
261
261
  return literal;
262
262
  }
263
- export function gerarPreparacaoSaida(task) {
263
+ function achatarGarantiasConjuntivasTypeScript(expressao) {
264
+ return expressao.tipo === "composta" && expressao.operadorLogico === "e"
265
+ ? expressao.termos.flatMap(achatarGarantiasConjuntivasTypeScript)
266
+ : [expressao];
267
+ }
268
+ function sugerirValoresTypeScript(tipo, tiposDeclarados) {
269
+ if (["Numero", "Inteiro", "Decimal"].includes(tipo))
270
+ return "[1, 0, -1]";
271
+ if (tipo === "Booleano")
272
+ return "[false, true]";
273
+ if (["Texto", "Id", "Email", "Url", "Data", "DataHora", "Timestamp"].includes(tipo)) {
274
+ return '["valor_garantido", "outro_valor"]';
275
+ }
276
+ if (/^Lista</u.test(tipo) || /\[\]$/u.test(tipo))
277
+ return "[[]]";
278
+ if (/^Mapa</u.test(tipo) || ["Json", "Objeto"].includes(tipo) || tiposDeclarados.has(tipo))
279
+ return "[{}]";
280
+ return "[1, 0, -1, true, false, \"valor_garantido\", \"outro_valor\", {}]";
281
+ }
282
+ export function gerarPreparacaoSaida(task, tiposDeclarados = new Map()) {
264
283
  const camposSaida = new Set(task.output.map((campo) => campo.nome));
265
284
  const linhas = [];
266
285
  for (const campo of task.output) {
267
286
  linhas.push(` ${campo.nome}: ${valorPadraoTypeScript(campo.tipo, campo.nome)},`);
268
287
  }
269
- const ajustes = [];
270
- for (const garantia of task.garantiasEstruturadas) {
271
- if (garantia.tipo === "pertencimento" && garantia.valores && camposSaida.has(garantia.alvo)) {
272
- ajustes.push(` saida.${garantia.alvo} = ${formatarValorTypeScript(garantia.valores[0] ?? "", camposSaida, "saida")} as any;`);
273
- }
274
- if (garantia.tipo === "comparacao" && garantia.valor && camposSaida.has(garantia.alvo.split(".")[0] ?? garantia.alvo)) {
275
- ajustes.push(` ${resolverReferenciaTypeScript(garantia.alvo, "saida")} = ${formatarValorTypeScript(garantia.valor, camposSaida, "saida")} as any;`);
288
+ const grupos = new Map();
289
+ for (const garantia of task.garantiasEstruturadas.flatMap(achatarGarantiasConjuntivasTypeScript)) {
290
+ if (!("alvo" in garantia) || !camposSaida.has(garantia.alvo.split(".")[0] ?? garantia.alvo))
291
+ continue;
292
+ if (garantia.tipo === "comparacao" || garantia.tipo === "pertencimento" || garantia.tipo === "existe") {
293
+ grupos.set(garantia.alvo, [...(grupos.get(garantia.alvo) ?? []), garantia]);
276
294
  }
277
- if (garantia.tipo === "existe" && garantia.alvo.includes(".")) {
278
- const [raiz, filho] = garantia.alvo.split(".", 2);
279
- if (raiz && filho && camposSaida.has(raiz)) {
280
- ajustes.push(` saida.${raiz} = (saida.${raiz} ?? {}) as any;`);
281
- ajustes.push(` (saida.${raiz} as any).${filho} = (saida.${raiz} as any).${filho} ?? "valor_garantido";`);
295
+ }
296
+ const referencias = new Set();
297
+ for (const regras of grupos.values()) {
298
+ for (const regra of regras) {
299
+ if (regra.tipo === "comparacao" && !regra.valorLiteral && camposSaida.has(regra.valor.split(".")[0] ?? regra.valor)) {
300
+ referencias.add(regra.valor);
282
301
  }
283
302
  }
284
303
  }
285
- return ` const saida = {\n${linhas.join("\n")}\n } as ${task.nome}Saida;\n${ajustes.join("\n")}`;
304
+ for (const referencia of referencias) {
305
+ if (!grupos.has(referencia)) {
306
+ grupos.set(referencia, [{ tipo: "existe", alvo: referencia, textoOriginal: `${referencia} existe` }]);
307
+ }
308
+ }
309
+ const caminhos = [...grupos.keys()].sort((a, b) => Number(referencias.has(b)) - Number(referencias.has(a)) || a.split(".").length - b.split(".").length);
310
+ const ajustes = [];
311
+ for (const caminho of caminhos) {
312
+ const regras = grupos.get(caminho);
313
+ let tipo = task.output.find((campo) => campo.nome === caminho.split(".")[0])?.tipo ?? "Json";
314
+ for (const segmento of caminho.split(".").slice(1)) {
315
+ tipo = tiposDeclarados.get(tipo)?.get(segmento) ?? "Json";
316
+ }
317
+ const restricoes = regras.map((regra) => {
318
+ if (regra.tipo === "comparacao") {
319
+ return `[${JSON.stringify(regra.operador)}, ${formatarValorTypeScript(regra.valor, camposSaida, "saida", regra.valorLiteral)}]`;
320
+ }
321
+ if (regra.tipo === "pertencimento") {
322
+ const valores = (regra.valores ?? []).map((valor) => formatarValorTypeScript(valor, camposSaida, "saida", true));
323
+ return `["in", [${valores.join(", ")}]]`;
324
+ }
325
+ return '["existe", null]';
326
+ });
327
+ const caminhoLiteral = JSON.stringify(caminho.split("."));
328
+ const somenteExistencia = regras.every((regra) => regra.tipo === "existe");
329
+ const temDescendente = caminhos.some((outro) => outro.startsWith(`${caminho}.`));
330
+ if (somenteExistencia && temDescendente) {
331
+ ajustes.push(` if (semaLerSaida(saida, ${caminhoLiteral}) == null) semaDefinirSaida(saida, ${caminhoLiteral}, {});`);
332
+ continue;
333
+ }
334
+ const inteiro = tipo === "Inteiro" ? "true" : "false";
335
+ ajustes.push(` semaDefinirSaida(saida, ${caminhoLiteral}, semaSintetizarSaida(semaLerSaida(saida, ${caminhoLiteral}), [${restricoes.join(", ")}], ${inteiro}, ${sugerirValoresTypeScript(tipo, tiposDeclarados)}));`);
336
+ }
337
+ const helpers = grupos.size === 0 ? "" : `
338
+ const semaLerSaida = (objeto: any, caminho: string[]): any => caminho.reduce((atual, campo) => atual == null ? undefined : atual[campo], objeto);
339
+ const semaDefinirSaida = (objeto: any, caminho: string[], valor: any): void => {
340
+ let atual = objeto;
341
+ for (const campo of caminho.slice(0, -1)) {
342
+ if (atual[campo] === null || typeof atual[campo] !== "object") atual[campo] = {};
343
+ atual = atual[campo];
344
+ }
345
+ const ultimo = caminho[caminho.length - 1];
346
+ if (ultimo !== undefined) atual[ultimo] = valor;
347
+ };
348
+ const semaSintetizarSaida = (atual: any, restricoes: Array<[string, any]>, inteiro: boolean, sementes: any[]): any => {
349
+ const aceita = (valor: any): boolean => restricoes.every(([operador, limite]) => {
350
+ if (operador === "existe") return valor !== undefined && valor !== null;
351
+ if (operador === "in") return Array.isArray(limite) && limite.some((item: any) => Object.is(item, valor));
352
+ if (operador === "==") return valor === limite;
353
+ if (operador === "!=") return valor !== limite;
354
+ const esquerda: any = valor;
355
+ const direita: any = limite;
356
+ if (operador === ">") return esquerda > direita;
357
+ if (operador === ">=") return esquerda >= direita;
358
+ if (operador === "<") return esquerda < direita;
359
+ if (operador === "<=") return esquerda <= direita;
360
+ return false;
361
+ });
362
+ const candidatos: any[] = [atual, ...sementes];
363
+ const numeros: number[] = [];
364
+ for (const [operador, limite] of restricoes) {
365
+ if (operador === "in" && Array.isArray(limite)) candidatos.push(...limite);
366
+ else if (operador !== "existe") {
367
+ candidatos.push(limite);
368
+ if (typeof limite === "number" && Number.isFinite(limite)) {
369
+ numeros.push(limite);
370
+ candidatos.push(limite - 1, limite + 1, Math.floor(limite), Math.ceil(limite));
371
+ } else if (typeof limite === "string") {
372
+ candidatos.push(limite + "\\u0000", limite.slice(0, -1), "");
373
+ }
374
+ }
375
+ }
376
+ numeros.sort((a, b) => a - b);
377
+ for (let indice = 0; indice < numeros.length - 1; indice += 1) {
378
+ candidatos.push((numeros[indice]! + numeros[indice + 1]!) / 2);
379
+ }
380
+ for (const candidato of candidatos) {
381
+ if (inteiro && (typeof candidato !== "number" || !Number.isInteger(candidato))) continue;
382
+ if (aceita(candidato)) return candidato;
383
+ }
384
+ throw new Error("Nao foi possivel sintetizar uma saida demonstrativa compativel com as garantias.");
385
+ };
386
+ `;
387
+ return ` const saida = {\n${linhas.join("\n")}\n } as ${task.nome}Saida;${helpers}\n${ajustes.join("\n")}`;
286
388
  }
287
389
  export function gerarValidacoes(task) {
288
390
  const linhas = [];
@@ -1,7 +1,7 @@
1
1
  import type { IrModulo, IrTask } from "@sema/nucleo";
2
2
  import { type ArquivoGerado } from "@sema/padroes";
3
3
  export declare function gerarRotas(modulo: IrModulo): string;
4
- export declare function gerarTask(task: IrTask): string;
4
+ export declare function gerarTask(task: IrTask, tiposDeclarados?: Map<string, Map<string, string>>): string;
5
5
  export declare function gerarTestes(modulo: IrModulo): string;
6
6
  export type TipoCabecalhoSemaTypeScript = "contrato" | "teste" | "dto" | "service" | "controller" | "spec";
7
7
  export declare function gerarCabecalhoSemaTypeScript(modulo: IrModulo, tipo?: TipoCabecalhoSemaTypeScript): string;
@@ -68,7 +68,7 @@ ${verificacoesErro || " throw erro;"}
68
68
  `;
69
69
  }).join("\n");
70
70
  }
71
- export function gerarTask(task) {
71
+ export function gerarTask(task, tiposDeclarados = new Map()) {
72
72
  const nomeSimbolo = normalizarNomeParaSimbolo(task.nome);
73
73
  const entradaNome = `${nomeSimbolo}_entrada`;
74
74
  const saidaNome = `${nomeSimbolo}_saida`;
@@ -130,7 +130,7 @@ ${task.stateContract ? ` // Vinculo de estado: ${task.stateContract.nomeEstado
130
130
  ${task.implementacoesExternas.length > 0 ? ` // Implementacoes externas vinculadas:\n${task.implementacoesExternas.map((impl) => ` // - ${impl.origem}: ${impl.caminho} [${impl.statusImpl ?? "nao_verificado"}]`).join("\n")}` : ""}
131
131
  // Efeitos declarados:
132
132
  ${task.efeitosEstruturados.map((efeito) => ` // - categoria=${efeito.categoria} alvo=${efeito.alvo}${efeito.detalhe ? ` detalhe=${efeito.detalhe}` : ""}${efeito.criticidade ? ` criticidade=${efeito.criticidade}` : ""}`).join("\n") || task.effects.map((efeito) => ` // - ${efeito}`).join("\n") || " // - Nenhum efeito declarado."}
133
- ${gerarPreparacaoSaida(task)}
133
+ ${gerarPreparacaoSaida(task, tiposDeclarados)}
134
134
  ${gerarGarantias(task)}
135
135
  }
136
136
 
@@ -211,6 +211,13 @@ export function gerarCabecalhoSemaTypeScript(modulo, tipo = "contrato") {
211
211
  }
212
212
  export function gerarTypeScriptBase(modulo) {
213
213
  const nomeBase = normalizarNomeModulo(modulo.nome).replace(/\./g, "_");
214
+ const mapaTiposDeclarados = new Map();
215
+ for (const entidade of modulo.entities) {
216
+ mapaTiposDeclarados.set(entidade.nome, new Map(entidade.campos.map((campo) => [campo.nome, campo.tipo])));
217
+ }
218
+ for (const tipo of modulo.types) {
219
+ mapaTiposDeclarados.set(tipo.nome, new Map(tipo.definicao.campos.map((campo) => [campo.nome, campo.tipo])));
220
+ }
214
221
  const interoperabilidades = modulo.interoperabilidades
215
222
  .map((interop) => `// Interop externo ${interop.origem}: ${interop.caminho}`)
216
223
  .join("\n");
@@ -235,7 +242,7 @@ export function gerarTypeScriptBase(modulo) {
235
242
  const routes = modulo.routes
236
243
  .map((route) => `// Route ${route.nome}: metodo=${route.metodo ?? "nao_definido"} caminho=${route.caminho ?? "nao_definido"} task=${route.task ?? "nao_definida"} input_publico=${route.inputPublico.map((campo) => campo.nome).join(", ") || "padrao_task"} output_publico=${route.outputPublico.map((campo) => campo.nome).join(", ") || "padrao_task"} erros_publicos=${route.errosPublicos.map((erro) => erro.nome).join(", ") || "padrao_task"} effects_publicos=${route.efeitosPublicos.map((efeito) => `${efeito.categoria}:${efeito.alvo}`).join(", ") || "nenhum"} garantias_publicas=${route.garantiasPublicasMinimas.length}`)
237
244
  .join("\n");
238
- const tasks = modulo.tasks.map(gerarTask).join("\n");
245
+ const tasks = modulo.tasks.map((task) => gerarTask(task, mapaTiposDeclarados)).join("\n");
239
246
  const contratosPublicos = gerarRotas(modulo);
240
247
  const codigo = `${gerarCabecalhoSemaTypeScript(modulo)}${interoperabilidades ? `${interoperabilidades}\n` : ""}\n${tiposExternos}\n${tipos}\n${entidades}\n${enums}\n${states}\n${flows}\n${routes}\n${tasks}\n${contratosPublicos}\n`;
241
248
  const testes = `${gerarCabecalhoSemaTypeScript(modulo, "teste")}${gerarTestes(modulo)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semacode/cli",
3
- "version": "4.1.1",
3
+ "version": "4.1.3",
4
4
  "description": "Local-first semantic governance CLI for AI agents, proven with Claude, Codex, zCode (GLM) and Kimi: contracts, drift, impact, docs gates, and code generation.",
5
5
  "type": "module",
6
6
  "icon": "logo.png",