@semacode/cli 4.1.2 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semacode/cli",
3
- "version": "4.1.2",
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",